1 /* TTY part of PTY. */ 2 #include <assert.h> 3 #include <minix/drivers.h> 4 #include <minix/driver.h> 5 #include <minix/optset.h> 6 #include <termios.h> 7 #include <sys/ttycom.h> 8 #include <sys/ttydefaults.h> 9 #include <sys/fcntl.h> 10 #include <signal.h> 11 #include "tty.h" 12 13 #include <sys/time.h> 14 #include <sys/select.h> 15 16 /* Address of a tty structure. */ 17 #define tty_addr(line) (&tty_table[line]) 18 19 /* Macros for magic tty structure pointers. */ 20 #define FIRST_TTY tty_addr(0) 21 #define END_TTY tty_addr(sizeof(tty_table) / sizeof(tty_table[0])) 22 23 /* A device exists if at least its 'devread' function is defined. */ 24 #define tty_active(tp) ((tp)->tty_devread != NULL) 25 26 static void tty_timed_out(int arg); 27 static void settimer(tty_t *tty_ptr, int enable); 28 static void in_transfer(tty_t *tp); 29 static int tty_echo(tty_t *tp, int ch); 30 static void rawecho(tty_t *tp, int ch); 31 static int back_over(tty_t *tp); 32 static void reprint(tty_t *tp); 33 static void dev_ioctl(tty_t *tp); 34 static void setattr(tty_t *tp); 35 static void tty_icancel(tty_t *tp); 36 37 static int do_open(devminor_t minor, int access, endpoint_t user_endpt); 38 static int do_close(devminor_t minor); 39 static ssize_t do_read(devminor_t minor, u64_t position, endpoint_t endpt, 40 cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); 41 static ssize_t do_write(devminor_t minor, u64_t position, endpoint_t endpt, 42 cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); 43 static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id); 44 static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt); 45 46 static struct chardriver tty_tab = { 47 .cdr_open = do_open, 48 .cdr_close = do_close, 49 .cdr_read = do_read, 50 .cdr_write = do_write, 51 .cdr_ioctl = tty_ioctl, 52 .cdr_cancel = do_cancel, 53 .cdr_select = do_select 54 }; 55 56 /* Default attributes. */ 57 static struct termios termios_defaults = { 58 .c_iflag = TTYDEF_IFLAG, 59 .c_oflag = TTYDEF_OFLAG, 60 .c_cflag = TTYDEF_CFLAG, 61 .c_lflag = TTYDEF_LFLAG, 62 .c_ispeed = TTYDEF_SPEED, 63 .c_ospeed = TTYDEF_SPEED, 64 .c_cc = { 65 [VEOF] = CEOF, 66 [VEOL] = CEOL, 67 [VERASE] = CERASE, 68 [VINTR] = CINTR, 69 [VKILL] = CKILL, 70 [VMIN] = CMIN, 71 [VQUIT] = CQUIT, 72 [VTIME] = CTIME, 73 [VSUSP] = CSUSP, 74 [VSTART] = CSTART, 75 [VSTOP] = CSTOP, 76 [VREPRINT] = CREPRINT, 77 [VLNEXT] = CLNEXT, 78 [VDISCARD] = CDISCARD, 79 [VSTATUS] = CSTATUS 80 } 81 }; 82 static struct winsize winsize_defaults; /* = all zeroes */ 83 84 static const char lined[TTLINEDNAMELEN] = "termios"; /* line discipline */ 85 86 /* Global variables for the TTY task (declared extern in tty.h). */ 87 tty_t tty_table[NR_PTYS]; 88 u32_t system_hz; 89 int tty_gid; 90 91 static struct optset optset_table[] = { 92 { "gid", OPT_INT, &tty_gid, 10 }, 93 { NULL, 0, NULL, 0 } 94 }; 95 96 static void tty_startup(void); 97 static int tty_init(int, sef_init_info_t *); 98 99 /*===========================================================================* 100 * is_pty * 101 *===========================================================================*/ 102 static int is_pty(devminor_t line) 103 { 104 /* Return TRUE if the given minor device number refers to a pty (the master 105 * side of a pty/tty pair), and FALSE otherwise. 106 */ 107 108 if (line == PTMX_MINOR) 109 return TRUE; 110 if (line >= PTYPX_MINOR && line < PTYPX_MINOR + NR_PTYS) 111 return TRUE; 112 if (line >= UNIX98_MINOR && line < UNIX98_MINOR + NR_PTYS * 2 && !(line & 1)) 113 return TRUE; 114 115 return FALSE; 116 } 117 118 /*===========================================================================* 119 * tty_task * 120 *===========================================================================*/ 121 int main(int argc, char **argv) 122 { 123 /* Main routine of the terminal task. */ 124 125 message tty_mess; /* buffer for all incoming messages */ 126 int ipc_status; 127 int line; 128 int r; 129 register tty_t *tp; 130 131 env_setargs(argc, argv); 132 133 tty_startup(); 134 135 while (TRUE) { 136 /* Check for and handle any events on any of the ttys. */ 137 for (tp = FIRST_TTY; tp < END_TTY; tp++) { 138 if (tp->tty_events) handle_events(tp); 139 } 140 141 /* Get a request message. */ 142 r= driver_receive(ANY, &tty_mess, &ipc_status); 143 if (r != 0) 144 panic("driver_receive failed with: %d", r); 145 146 if (is_ipc_notify(ipc_status)) { 147 switch (_ENDPOINT_P(tty_mess.m_source)) { 148 case CLOCK: 149 /* run watchdogs of expired timers */ 150 expire_timers(tty_mess.m_notify.timestamp); 151 break; 152 default: 153 /* do nothing */ 154 break; 155 } 156 157 /* done, get new message */ 158 continue; 159 } 160 161 if (!IS_CDEV_RQ(tty_mess.m_type)) { 162 chardriver_process(&tty_tab, &tty_mess, ipc_status); 163 continue; 164 } 165 166 /* Only device requests should get to this point. 167 * All requests have a minor device number. 168 */ 169 if (OK != chardriver_get_minor(&tty_mess, &line)) 170 continue; 171 172 if (is_pty(line)) { 173 /* Terminals and pseudo terminals belong together. We can only 174 * make a distinction between the two based on minor number and 175 * not on position in the tty_table. Hence this special case. 176 */ 177 do_pty(&tty_mess, ipc_status); 178 continue; 179 } 180 181 /* Execute the requested device driver function. */ 182 chardriver_process(&tty_tab, &tty_mess, ipc_status); 183 } 184 185 return 0; 186 } 187 188 tty_t * 189 line2tty(devminor_t line) 190 { 191 /* Convert a terminal line to tty_table pointer */ 192 193 tty_t* tp; 194 195 if ((line - TTYPX_MINOR) < NR_PTYS) { 196 tp = tty_addr(line - TTYPX_MINOR); 197 } else if ((line - PTYPX_MINOR) < NR_PTYS) { 198 tp = tty_addr(line - PTYPX_MINOR); 199 } else if ((line - UNIX98_MINOR) < NR_PTYS * 2) { 200 tp = tty_addr((line - UNIX98_MINOR) >> 1); 201 } else { 202 tp = NULL; 203 } 204 205 return(tp); 206 } 207 208 /*===========================================================================* 209 * tty_startup * 210 *===========================================================================*/ 211 static void tty_startup(void) 212 { 213 /* Register init callbacks. */ 214 sef_setcb_init_fresh(tty_init); 215 sef_setcb_init_restart(tty_init); 216 217 /* No signal support for now. */ 218 219 /* Let SEF perform startup. */ 220 sef_startup(); 221 } 222 223 /*===========================================================================* 224 * do_read * 225 *===========================================================================*/ 226 static ssize_t do_read(devminor_t minor, u64_t UNUSED(position), 227 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, 228 cdev_id_t id) 229 { 230 /* A process wants to read from a terminal. */ 231 tty_t *tp; 232 int r; 233 234 if ((tp = line2tty(minor)) == NULL) 235 return ENXIO; 236 237 /* Check if there is already a process hanging in a read, check if the 238 * parameters are correct, do I/O. 239 */ 240 if (tp->tty_incaller != NONE || tp->tty_inleft > 0) 241 return EIO; 242 if (size <= 0) 243 return EINVAL; 244 245 /* Copy information from the message to the tty struct. */ 246 tp->tty_incaller = endpt; 247 tp->tty_inid = id; 248 tp->tty_ingrant = grant; 249 assert(tp->tty_incum == 0); 250 tp->tty_inleft = size; 251 252 if (!(tp->tty_termios.c_lflag & ICANON) && tp->tty_termios.c_cc[VTIME] > 0) { 253 if (tp->tty_termios.c_cc[VMIN] == 0) { 254 /* MIN & TIME specify a read timer that finishes the 255 * read in TIME/10 seconds if no bytes are available. 256 */ 257 settimer(tp, TRUE); 258 tp->tty_min = 1; 259 } else { 260 /* MIN & TIME specify an inter-byte timer that may 261 * have to be cancelled if there are no bytes yet. 262 */ 263 if (tp->tty_eotct == 0) { 264 settimer(tp, FALSE); 265 tp->tty_min = tp->tty_termios.c_cc[VMIN]; 266 } 267 } 268 } 269 270 /* Anything waiting in the input buffer? Clear it out... */ 271 in_transfer(tp); 272 /* ...then go back for more. */ 273 handle_events(tp); 274 if (tp->tty_inleft == 0) 275 return EDONTREPLY; /* already done */ 276 277 /* There were no bytes in the input queue available. */ 278 if (flags & CDEV_NONBLOCK) { 279 tty_icancel(tp); 280 r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN; 281 tp->tty_inleft = tp->tty_incum = 0; 282 tp->tty_incaller = NONE; 283 return r; 284 } 285 286 if (tp->tty_select_ops) 287 select_retry(tp); 288 289 return EDONTREPLY; /* suspend the caller */ 290 } 291 292 /*===========================================================================* 293 * do_write * 294 *===========================================================================*/ 295 static ssize_t do_write(devminor_t minor, u64_t UNUSED(position), 296 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, 297 cdev_id_t id) 298 { 299 /* A process wants to write on a terminal. */ 300 tty_t *tp; 301 int r; 302 303 if ((tp = line2tty(minor)) == NULL) 304 return ENXIO; 305 306 /* Check if there is already a process hanging in a write, check if the 307 * parameters are correct, do I/O. 308 */ 309 if (tp->tty_outcaller != NONE || tp->tty_outleft > 0) 310 return EIO; 311 if (size <= 0) 312 return EINVAL; 313 314 /* Copy message parameters to the tty structure. */ 315 tp->tty_outcaller = endpt; 316 tp->tty_outid = id; 317 tp->tty_outgrant = grant; 318 assert(tp->tty_outcum == 0); 319 tp->tty_outleft = size; 320 321 /* Try to write. */ 322 handle_events(tp); 323 if (tp->tty_outleft == 0) 324 return EDONTREPLY; /* already done */ 325 326 /* None or not all the bytes could be written. */ 327 if (flags & CDEV_NONBLOCK) { 328 r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN; 329 tp->tty_outleft = tp->tty_outcum = 0; 330 tp->tty_outcaller = NONE; 331 return r; 332 } 333 334 if (tp->tty_select_ops) 335 select_retry(tp); 336 337 return EDONTREPLY; /* suspend the caller */ 338 } 339 340 /*===========================================================================* 341 * tty_ioctl * 342 *===========================================================================*/ 343 int tty_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, 344 cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id) 345 { 346 /* Perform an IOCTL on this terminal. POSIX termios calls are handled 347 * by the IOCTL system call. 348 */ 349 tty_t *tp; 350 int i, r; 351 352 if ((tp = line2tty(minor)) == NULL) 353 return ENXIO; 354 355 r = OK; 356 switch (request) { 357 case TIOCGETA: 358 /* Get the termios attributes. */ 359 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_termios, 360 sizeof(struct termios)); 361 break; 362 363 case TIOCSETAW: 364 case TIOCSETAF: 365 case TIOCDRAIN: 366 if (tp->tty_outleft > 0) { 367 if (flags & CDEV_NONBLOCK) 368 return EAGAIN; 369 /* Wait for all ongoing output processing to finish. */ 370 tp->tty_iocaller = endpt; 371 tp->tty_ioid = id; 372 tp->tty_ioreq = request; 373 tp->tty_iogrant = grant; 374 return EDONTREPLY; /* suspend the caller */ 375 } 376 if (request == TIOCDRAIN) break; 377 if (request == TIOCSETAF) tty_icancel(tp); 378 /*FALL THROUGH*/ 379 case TIOCSETA: 380 /* Set the termios attributes. */ 381 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_termios, 382 sizeof(struct termios)); 383 if (r != OK) break; 384 setattr(tp); 385 break; 386 387 case TIOCFLUSH: 388 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &i, sizeof(i)); 389 if (r != OK) break; 390 if(i & FREAD) { tty_icancel(tp); } 391 if(i & FWRITE) { (*tp->tty_ocancel)(tp, 0); } 392 break; 393 case TIOCSTART: 394 tp->tty_inhibited = 0; 395 tp->tty_events = 1; 396 break; 397 case TIOCSTOP: 398 tp->tty_inhibited = 1; 399 tp->tty_events = 1; 400 break; 401 case TIOCSBRK: /* tcsendbreak - turn break on */ 402 if (tp->tty_break_on != NULL) (*tp->tty_break_on)(tp,0); 403 break; 404 case TIOCCBRK: /* tcsendbreak - turn break off */ 405 if (tp->tty_break_off != NULL) (*tp->tty_break_off)(tp,0); 406 break; 407 408 case TIOCGWINSZ: 409 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_winsize, 410 sizeof(struct winsize)); 411 break; 412 413 case TIOCSWINSZ: 414 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_winsize, 415 sizeof(struct winsize)); 416 sigchar(tp, SIGWINCH, 0); 417 break; 418 case TIOCGETD: /* get line discipline */ 419 i = TTYDISC; 420 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &i, sizeof(i)); 421 break; 422 case TIOCSETD: /* set line discipline */ 423 printf("TTY: TIOCSETD: can't set any other line discipline.\n"); 424 r = ENOTTY; 425 break; 426 case TIOCGLINED: /* get line discipline as string */ 427 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) lined, sizeof(lined)); 428 break; 429 case TIOCGQSIZE: /* get input/output queue sizes */ 430 /* Not sure what to report here. We are using input and output queues 431 * of different sizes, and the IOCTL allows for one single value for 432 * both. So far this seems to be just for stty(1) so let's just report 433 * the larger of the two. TODO: revisit queue sizes altogether. 434 */ 435 i = MAX(TTY_IN_BYTES, TTY_OUT_BYTES); 436 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &i, sizeof(i)); 437 break; 438 case TIOCSCTTY: 439 /* Process sets this tty as its controlling tty */ 440 tp->tty_pgrp = user_endpt; 441 break; 442 443 /* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is 444 * not defined. 445 */ 446 case TIOCGPGRP: 447 case TIOCSPGRP: 448 default: 449 r = ENOTTY; 450 } 451 452 return r; 453 } 454 455 /*===========================================================================* 456 * do_open * 457 *===========================================================================*/ 458 static int do_open(devminor_t minor, int access, endpoint_t user_endpt) 459 { 460 /* A tty line has been opened. Make it the callers controlling tty if 461 * CDEV_NOCTTY is *not* set and it is not the log device. CDEV_CTTY is returned 462 * if the tty is made the controlling tty, otherwise OK or an error code. 463 */ 464 tty_t *tp; 465 int r = OK; 466 467 if ((tp = line2tty(minor)) == NULL) 468 return ENXIO; 469 470 /* Make sure the user is not mixing Unix98 and old-style ends. */ 471 if ((*tp->tty_mayopen)(tp, minor) == FALSE) 472 return EIO; 473 474 if (!(access & CDEV_NOCTTY)) { 475 tp->tty_pgrp = user_endpt; 476 r = CDEV_CTTY; 477 } 478 tp->tty_openct++; 479 if (tp->tty_openct == 1) { 480 /* Tell the device that the tty is opened */ 481 (*tp->tty_open)(tp, 0); 482 } 483 484 return r; 485 } 486 487 /*===========================================================================* 488 * do_close * 489 *===========================================================================*/ 490 static int do_close(devminor_t minor) 491 { 492 /* A tty line has been closed. Clean up the line if it is the last close. */ 493 tty_t *tp; 494 495 if ((tp = line2tty(minor)) == NULL) 496 return ENXIO; 497 498 if (--tp->tty_openct == 0) { 499 tp->tty_pgrp = 0; 500 tty_icancel(tp); 501 (*tp->tty_ocancel)(tp, 0); 502 (*tp->tty_close)(tp, 0); 503 tp->tty_termios = termios_defaults; 504 tp->tty_winsize = winsize_defaults; 505 setattr(tp); 506 } 507 508 return OK; 509 } 510 511 /*===========================================================================* 512 * do_cancel * 513 *===========================================================================*/ 514 static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id) 515 { 516 /* A signal has been sent to a process that is hanging trying to read or write. 517 * The pending read or write must be finished off immediately. 518 */ 519 tty_t *tp; 520 int r; 521 522 if ((tp = line2tty(minor)) == NULL) 523 return ENXIO; 524 525 /* Check the parameters carefully, to avoid cancelling twice. */ 526 r = EDONTREPLY; 527 if (tp->tty_inleft != 0 && endpt == tp->tty_incaller && id == tp->tty_inid) { 528 /* Process was reading when killed. Clean up input. */ 529 tty_icancel(tp); 530 r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN; 531 tp->tty_inleft = tp->tty_incum = 0; 532 tp->tty_incaller = NONE; 533 } else if (tp->tty_outleft != 0 && endpt == tp->tty_outcaller && 534 id == tp->tty_outid) { 535 /* Process was writing when killed. Clean up output. */ 536 r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN; 537 tp->tty_outleft = tp->tty_outcum = 0; 538 tp->tty_outcaller = NONE; 539 } else if (tp->tty_ioreq != 0 && endpt == tp->tty_iocaller && 540 id == tp->tty_ioid) { 541 /* Process was waiting for output to drain. */ 542 r = EINTR; 543 tp->tty_ioreq = 0; 544 tp->tty_iocaller = NONE; 545 } 546 if (r != EDONTREPLY) 547 tp->tty_events = 1; 548 /* Only reply if we found a matching request. */ 549 return r; 550 } 551 552 int select_try(struct tty *tp, int ops) 553 { 554 int ready_ops = 0; 555 556 /* Special case. If line is hung up, no operations will block. 557 * (and it can be seen as an exceptional condition.) 558 */ 559 if (tp->tty_termios.c_ospeed == B0) { 560 ready_ops |= ops; 561 } 562 563 if (ops & CDEV_OP_RD) { 564 /* will i/o not block on read? */ 565 if (tp->tty_inleft > 0) { 566 ready_ops |= CDEV_OP_RD; /* EIO - no blocking */ 567 } else if (tp->tty_incount > 0) { 568 /* Is a regular read possible? tty_incount 569 * says there is data. But a read will only succeed 570 * in canonical mode if a newline has been seen. 571 */ 572 if (!(tp->tty_termios.c_lflag & ICANON) || 573 tp->tty_eotct > 0) { 574 ready_ops |= CDEV_OP_RD; 575 } 576 } 577 } 578 579 if (ops & CDEV_OP_WR) { 580 if (tp->tty_outleft > 0) ready_ops |= CDEV_OP_WR; 581 else if ((*tp->tty_devwrite)(tp, 1)) ready_ops |= CDEV_OP_WR; 582 } 583 return ready_ops; 584 } 585 586 int select_retry(struct tty *tp) 587 { 588 int ops; 589 590 if (tp->tty_select_ops && (ops = select_try(tp, tp->tty_select_ops))) { 591 chardriver_reply_select(tp->tty_select_proc, 592 tp->tty_select_minor, ops); 593 tp->tty_select_ops &= ~ops; 594 } 595 return OK; 596 } 597 598 /*===========================================================================* 599 * do_select * 600 *===========================================================================*/ 601 static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt) 602 { 603 tty_t *tp; 604 int ready_ops, watch; 605 606 if ((tp = line2tty(minor)) == NULL) 607 return ENXIO; 608 609 watch = (ops & CDEV_NOTIFY); 610 ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR); 611 612 ready_ops = select_try(tp, ops); 613 614 ops &= ~ready_ops; 615 if (ops && watch) { 616 /* Translated minor numbers are a problem with late select replies. We 617 * have to save the minor number used to do the select, since otherwise 618 * VFS won't be able to make sense of those late replies. We do not 619 * support selecting on two different minors for the same object. 620 */ 621 if (tp->tty_select_ops != 0 && tp->tty_select_minor != minor) { 622 printf("TTY: select on one object with two minors (%d, %d)\n", 623 tp->tty_select_minor, minor); 624 return EBADF; 625 } 626 tp->tty_select_ops |= ops; 627 tp->tty_select_proc = endpt; 628 tp->tty_select_minor = minor; 629 } 630 631 return ready_ops; 632 } 633 634 /*===========================================================================* 635 * handle_events * 636 *===========================================================================*/ 637 void handle_events(tp) 638 tty_t *tp; /* TTY to check for events. */ 639 { 640 /* Handle any events pending on a TTY. */ 641 642 do { 643 tp->tty_events = 0; 644 645 /* Read input and perform input processing. */ 646 (*tp->tty_devread)(tp, 0); 647 648 /* Perform output processing and write output. */ 649 (*tp->tty_devwrite)(tp, 0); 650 651 /* Ioctl waiting for some event? */ 652 if (tp->tty_ioreq != 0) dev_ioctl(tp); 653 } while (tp->tty_events); 654 655 /* Transfer characters from the input queue to a waiting process. */ 656 in_transfer(tp); 657 658 /* Reply if enough bytes are available. */ 659 if (tp->tty_incum >= tp->tty_min && tp->tty_inleft > 0) { 660 chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum); 661 tp->tty_inleft = tp->tty_incum = 0; 662 tp->tty_incaller = NONE; 663 } 664 if (tp->tty_select_ops) 665 { 666 select_retry(tp); 667 } 668 select_retry_pty(tp); 669 } 670 671 /*===========================================================================* 672 * in_transfer * 673 *===========================================================================*/ 674 static void in_transfer(tp) 675 register tty_t *tp; /* pointer to terminal to read from */ 676 { 677 /* Transfer bytes from the input queue to a process reading from a terminal. */ 678 679 int ch; 680 int count; 681 char buf[64], *bp; 682 683 /* Force read to succeed if the line is hung up, looks like EOF to reader. */ 684 if (tp->tty_termios.c_ospeed == B0) tp->tty_min = 0; 685 686 /* Anything to do? */ 687 if (tp->tty_inleft == 0 || tp->tty_eotct < tp->tty_min) return; 688 689 bp = buf; 690 while (tp->tty_inleft > 0 && tp->tty_eotct > 0) { 691 ch = *tp->tty_intail; 692 693 if (!(ch & IN_EOF)) { 694 /* One character to be delivered to the user. */ 695 *bp = ch & IN_CHAR; 696 tp->tty_inleft--; 697 if (++bp == bufend(buf)) { 698 /* Temp buffer full, copy to user space. */ 699 sys_safecopyto(tp->tty_incaller, 700 tp->tty_ingrant, tp->tty_incum, 701 (vir_bytes) buf, (vir_bytes) buflen(buf)); 702 tp->tty_incum += buflen(buf); 703 bp = buf; 704 } 705 } 706 707 /* Remove the character from the input queue. */ 708 if (++tp->tty_intail == bufend(tp->tty_inbuf)) 709 tp->tty_intail = tp->tty_inbuf; 710 tp->tty_incount--; 711 if (ch & IN_EOT) { 712 tp->tty_eotct--; 713 /* Don't read past a line break in canonical mode. */ 714 if (tp->tty_termios.c_lflag & ICANON) tp->tty_inleft = 0; 715 } 716 } 717 718 if (bp > buf) { 719 /* Leftover characters in the buffer. */ 720 count = bp - buf; 721 sys_safecopyto(tp->tty_incaller, tp->tty_ingrant, tp->tty_incum, 722 (vir_bytes) buf, (vir_bytes) count); 723 tp->tty_incum += count; 724 } 725 726 /* Usually reply to the reader, possibly even if incum == 0 (EOF). */ 727 if (tp->tty_inleft == 0) { 728 chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum); 729 tp->tty_inleft = tp->tty_incum = 0; 730 tp->tty_incaller = NONE; 731 } 732 } 733 734 /*===========================================================================* 735 * in_process * 736 *===========================================================================*/ 737 int in_process(tp, buf, count) 738 register tty_t *tp; /* terminal on which character has arrived */ 739 char *buf; /* buffer with input characters */ 740 int count; /* number of input characters */ 741 { 742 /* Characters have just been typed in. Process, save, and echo them. Return 743 * the number of characters processed. 744 */ 745 746 int ch, ct; 747 int timeset = FALSE; 748 749 for (ct = 0; ct < count; ct++) { 750 /* Take one character. */ 751 ch = *buf++ & BYTE; 752 753 /* Strip to seven bits? */ 754 if (tp->tty_termios.c_iflag & ISTRIP) ch &= 0x7F; 755 756 /* Input extensions? */ 757 if (tp->tty_termios.c_lflag & IEXTEN) { 758 759 /* Previous character was a character escape? */ 760 if (tp->tty_escaped) { 761 tp->tty_escaped = NOT_ESCAPED; 762 ch |= IN_ESC; /* protect character */ 763 } 764 765 /* LNEXT (^V) to escape the next character? */ 766 if (ch == tp->tty_termios.c_cc[VLNEXT]) { 767 tp->tty_escaped = ESCAPED; 768 rawecho(tp, '^'); 769 rawecho(tp, '\b'); 770 continue; /* do not store the escape */ 771 } 772 773 /* REPRINT (^R) to reprint echoed characters? */ 774 if (ch == tp->tty_termios.c_cc[VREPRINT]) { 775 reprint(tp); 776 continue; 777 } 778 } 779 780 /* _POSIX_VDISABLE is a normal character value, so better escape it. */ 781 if (ch == _POSIX_VDISABLE) ch |= IN_ESC; 782 783 /* Map CR to LF, ignore CR, or map LF to CR. */ 784 if (ch == '\r') { 785 if (tp->tty_termios.c_iflag & IGNCR) continue; 786 if (tp->tty_termios.c_iflag & ICRNL) ch = '\n'; 787 } else 788 if (ch == '\n') { 789 if (tp->tty_termios.c_iflag & INLCR) ch = '\r'; 790 } 791 792 /* Canonical mode? */ 793 if (tp->tty_termios.c_lflag & ICANON) { 794 795 /* Erase processing (rub out of last character). */ 796 if (ch == tp->tty_termios.c_cc[VERASE]) { 797 (void) back_over(tp); 798 if (!(tp->tty_termios.c_lflag & ECHOE)) { 799 (void) tty_echo(tp, ch); 800 } 801 continue; 802 } 803 804 /* Kill processing (remove current line). */ 805 if (ch == tp->tty_termios.c_cc[VKILL]) { 806 while (back_over(tp)) {} 807 if (!(tp->tty_termios.c_lflag & ECHOE)) { 808 (void) tty_echo(tp, ch); 809 if (tp->tty_termios.c_lflag & ECHOK) 810 rawecho(tp, '\n'); 811 } 812 continue; 813 } 814 815 /* EOF (^D) means end-of-file, an invisible "line break". */ 816 if (ch == tp->tty_termios.c_cc[VEOF]) ch |= IN_EOT | IN_EOF; 817 818 /* The line may be returned to the user after an LF. */ 819 if (ch == '\n') ch |= IN_EOT; 820 821 /* Same thing with EOL, whatever it may be. */ 822 if (ch == tp->tty_termios.c_cc[VEOL]) ch |= IN_EOT; 823 } 824 825 /* Start/stop input control? */ 826 if (tp->tty_termios.c_iflag & IXON) { 827 828 /* Output stops on STOP (^S). */ 829 if (ch == tp->tty_termios.c_cc[VSTOP]) { 830 tp->tty_inhibited = STOPPED; 831 tp->tty_events = 1; 832 continue; 833 } 834 835 /* Output restarts on START (^Q) or any character if IXANY. */ 836 if (tp->tty_inhibited) { 837 if (ch == tp->tty_termios.c_cc[VSTART] 838 || (tp->tty_termios.c_iflag & IXANY)) { 839 tp->tty_inhibited = RUNNING; 840 tp->tty_events = 1; 841 if (ch == tp->tty_termios.c_cc[VSTART]) 842 continue; 843 } 844 } 845 } 846 847 if (tp->tty_termios.c_lflag & ISIG) { 848 /* Check for INTR, QUIT and STATUS characters. */ 849 int sig = -1; 850 if (ch == tp->tty_termios.c_cc[VINTR]) 851 sig = SIGINT; 852 else if(ch == tp->tty_termios.c_cc[VQUIT]) 853 sig = SIGQUIT; 854 else if(ch == tp->tty_termios.c_cc[VSTATUS]) 855 sig = SIGINFO; 856 857 if(sig >= 0) { 858 sigchar(tp, sig, 1); 859 (void) tty_echo(tp, ch); 860 continue; 861 } 862 } 863 864 /* Is there space in the input buffer? */ 865 if (tp->tty_incount == buflen(tp->tty_inbuf)) { 866 /* No space; discard in canonical mode, keep in raw mode. */ 867 if (tp->tty_termios.c_lflag & ICANON) continue; 868 break; 869 } 870 871 if (!(tp->tty_termios.c_lflag & ICANON)) { 872 /* In raw mode all characters are "line breaks". */ 873 ch |= IN_EOT; 874 875 /* Start an inter-byte timer? */ 876 if (!timeset && tp->tty_termios.c_cc[VMIN] > 0 877 && tp->tty_termios.c_cc[VTIME] > 0) { 878 settimer(tp, TRUE); 879 timeset = TRUE; 880 } 881 } 882 883 /* Perform the intricate function of echoing. */ 884 if (tp->tty_termios.c_lflag & (ECHO|ECHONL)) ch = tty_echo(tp, ch); 885 886 /* Save the character in the input queue. */ 887 *tp->tty_inhead++ = ch; 888 if (tp->tty_inhead == bufend(tp->tty_inbuf)) 889 tp->tty_inhead = tp->tty_inbuf; 890 tp->tty_incount++; 891 if (ch & IN_EOT) tp->tty_eotct++; 892 893 /* Try to finish input if the queue threatens to overflow. */ 894 if (tp->tty_incount == buflen(tp->tty_inbuf)) in_transfer(tp); 895 } 896 return ct; 897 } 898 899 /*===========================================================================* 900 * echo * 901 *===========================================================================*/ 902 static int tty_echo(tp, ch) 903 register tty_t *tp; /* terminal on which to echo */ 904 register int ch; /* pointer to character to echo */ 905 { 906 /* Echo the character if echoing is on. Some control characters are echoed 907 * with their normal effect, other control characters are echoed as "^X", 908 * normal characters are echoed normally. EOF (^D) is echoed, but immediately 909 * backspaced over. Return the character with the echoed length added to its 910 * attributes. 911 */ 912 int len, rp; 913 914 ch &= ~IN_LEN; 915 if (!(tp->tty_termios.c_lflag & ECHO)) { 916 if (ch == ('\n' | IN_EOT) && (tp->tty_termios.c_lflag 917 & (ICANON|ECHONL)) == (ICANON|ECHONL)) 918 (*tp->tty_echo)(tp, '\n'); 919 return(ch); 920 } 921 922 /* "Reprint" tells if the echo output has been messed up by other output. */ 923 rp = tp->tty_incount == 0 ? FALSE : tp->tty_reprint; 924 925 if ((ch & IN_CHAR) < ' ') { 926 switch (ch & (IN_ESC|IN_EOF|IN_EOT|IN_CHAR)) { 927 case '\t': 928 len = 0; 929 do { 930 (*tp->tty_echo)(tp, ' '); 931 len++; 932 } while (len < TAB_SIZE && (tp->tty_position & TAB_MASK) != 0); 933 break; 934 case '\r' | IN_EOT: 935 case '\n' | IN_EOT: 936 (*tp->tty_echo)(tp, ch & IN_CHAR); 937 len = 0; 938 break; 939 default: 940 (*tp->tty_echo)(tp, '^'); 941 (*tp->tty_echo)(tp, '@' + (ch & IN_CHAR)); 942 len = 2; 943 } 944 } else 945 if ((ch & IN_CHAR) == '\177') { 946 /* A DEL prints as "^?". */ 947 (*tp->tty_echo)(tp, '^'); 948 (*tp->tty_echo)(tp, '?'); 949 len = 2; 950 } else { 951 (*tp->tty_echo)(tp, ch & IN_CHAR); 952 len = 1; 953 } 954 if (ch & IN_EOF) while (len > 0) { (*tp->tty_echo)(tp, '\b'); len--; } 955 956 tp->tty_reprint = rp; 957 return(ch | (len << IN_LSHIFT)); 958 } 959 960 /*===========================================================================* 961 * rawecho * 962 *===========================================================================*/ 963 static void rawecho(tp, ch) 964 register tty_t *tp; 965 int ch; 966 { 967 /* Echo without interpretation if ECHO is set. */ 968 int rp = tp->tty_reprint; 969 if (tp->tty_termios.c_lflag & ECHO) (*tp->tty_echo)(tp, ch); 970 tp->tty_reprint = rp; 971 } 972 973 /*===========================================================================* 974 * back_over * 975 *===========================================================================*/ 976 static int back_over(tp) 977 register tty_t *tp; 978 { 979 /* Backspace to previous character on screen and erase it. */ 980 u16_t *head; 981 int len; 982 983 if (tp->tty_incount == 0) return(0); /* queue empty */ 984 head = tp->tty_inhead; 985 if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf); 986 if (*--head & IN_EOT) return(0); /* can't erase "line breaks" */ 987 if (tp->tty_reprint) reprint(tp); /* reprint if messed up */ 988 tp->tty_inhead = head; 989 tp->tty_incount--; 990 if (tp->tty_termios.c_lflag & ECHOE) { 991 len = (*head & IN_LEN) >> IN_LSHIFT; 992 while (len > 0) { 993 rawecho(tp, '\b'); 994 rawecho(tp, ' '); 995 rawecho(tp, '\b'); 996 len--; 997 } 998 } 999 return(1); /* one character erased */ 1000 } 1001 1002 /*===========================================================================* 1003 * reprint * 1004 *===========================================================================*/ 1005 static void reprint(tp) 1006 register tty_t *tp; /* pointer to tty struct */ 1007 { 1008 /* Restore what has been echoed to screen before if the user input has been 1009 * messed up by output, or if REPRINT (^R) is typed. 1010 */ 1011 int count; 1012 u16_t *head; 1013 1014 tp->tty_reprint = FALSE; 1015 1016 /* Find the last line break in the input. */ 1017 head = tp->tty_inhead; 1018 count = tp->tty_incount; 1019 while (count > 0) { 1020 if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf); 1021 if (head[-1] & IN_EOT) break; 1022 head--; 1023 count--; 1024 } 1025 if (count == tp->tty_incount) return; /* no reason to reprint */ 1026 1027 /* Show REPRINT (^R) and move to a new line. */ 1028 (void) tty_echo(tp, tp->tty_termios.c_cc[VREPRINT] | IN_ESC); 1029 rawecho(tp, '\r'); 1030 rawecho(tp, '\n'); 1031 1032 /* Reprint from the last break onwards. */ 1033 do { 1034 if (head == bufend(tp->tty_inbuf)) head = tp->tty_inbuf; 1035 *head = tty_echo(tp, *head); 1036 head++; 1037 count++; 1038 } while (count < tp->tty_incount); 1039 } 1040 1041 /*===========================================================================* 1042 * out_process * 1043 *===========================================================================*/ 1044 void out_process(tp, bstart, bpos, bend, icount, ocount) 1045 tty_t *tp; 1046 char *bstart, *bpos, *bend; /* start/pos/end of circular buffer */ 1047 int *icount; /* # input chars / input chars used */ 1048 int *ocount; /* max output chars / output chars used */ 1049 { 1050 /* Perform output processing on a circular buffer. *icount is the number of 1051 * bytes to process, and the number of bytes actually processed on return. 1052 * *ocount is the space available on input and the space used on output. 1053 * (Naturally *icount < *ocount.) The column position is updated modulo 1054 * the TAB size, because we really only need it for tabs. 1055 */ 1056 1057 int tablen; 1058 int ict = *icount; 1059 int oct = *ocount; 1060 int pos = tp->tty_position; 1061 1062 while (ict > 0) { 1063 switch (*bpos) { 1064 case '\7': 1065 break; 1066 case '\b': 1067 pos--; 1068 break; 1069 case '\r': 1070 pos = 0; 1071 break; 1072 case '\n': 1073 if ((tp->tty_termios.c_oflag & (OPOST|ONLCR)) 1074 == (OPOST|ONLCR)) { 1075 /* Map LF to CR+LF if there is space. Note that the 1076 * next character in the buffer is overwritten, so 1077 * we stop at this point. 1078 */ 1079 if (oct >= 2) { 1080 *bpos = '\r'; 1081 if (++bpos == bend) bpos = bstart; 1082 *bpos = '\n'; 1083 pos = 0; 1084 ict--; 1085 oct -= 2; 1086 } 1087 goto out_done; /* no space or buffer got changed */ 1088 } 1089 break; 1090 case '\t': 1091 /* Best guess for the tab length. */ 1092 tablen = TAB_SIZE - (pos & TAB_MASK); 1093 1094 if ((tp->tty_termios.c_oflag & (OPOST|OXTABS)) 1095 == (OPOST|OXTABS)) { 1096 /* Tabs must be expanded. */ 1097 if (oct >= tablen) { 1098 pos += tablen; 1099 ict--; 1100 oct -= tablen; 1101 do { 1102 *bpos = ' '; 1103 if (++bpos == bend) bpos = bstart; 1104 } while (--tablen != 0); 1105 } 1106 goto out_done; 1107 } 1108 /* Tabs are output directly. */ 1109 pos += tablen; 1110 break; 1111 default: 1112 /* Assume any other character prints as one character. */ 1113 pos++; 1114 } 1115 if (++bpos == bend) bpos = bstart; 1116 ict--; 1117 oct--; 1118 } 1119 out_done: 1120 tp->tty_position = pos & TAB_MASK; 1121 1122 *icount -= ict; /* [io]ct are the number of chars not used */ 1123 *ocount -= oct; /* *[io]count are the number of chars that are used */ 1124 } 1125 1126 /*===========================================================================* 1127 * dev_ioctl * 1128 *===========================================================================*/ 1129 static void dev_ioctl(tp) 1130 tty_t *tp; 1131 { 1132 /* The ioctl's TCSETSW, TCSETSF and TIOCDRAIN wait for output to finish to make 1133 * sure that an attribute change doesn't affect the processing of current 1134 * output. Once output finishes the ioctl is executed as in do_ioctl(). 1135 */ 1136 int result = EINVAL; 1137 1138 if (tp->tty_outleft > 0) return; /* output not finished */ 1139 1140 if (tp->tty_ioreq != TIOCDRAIN) { 1141 if (tp->tty_ioreq == TIOCSETAF) tty_icancel(tp); 1142 result = sys_safecopyfrom(tp->tty_iocaller, tp->tty_iogrant, 0, 1143 (vir_bytes) &tp->tty_termios, 1144 (vir_bytes) sizeof(tp->tty_termios)); 1145 if (result == OK) setattr(tp); 1146 } 1147 tp->tty_ioreq = 0; 1148 chardriver_reply_task(tp->tty_iocaller, tp->tty_ioid, result); 1149 tp->tty_iocaller = NONE; 1150 } 1151 1152 /*===========================================================================* 1153 * setattr * 1154 *===========================================================================*/ 1155 static void setattr(tp) 1156 tty_t *tp; 1157 { 1158 /* Apply the new line attributes (raw/canonical, line speed, etc.) */ 1159 u16_t *inp; 1160 int count; 1161 1162 if (!(tp->tty_termios.c_lflag & ICANON)) { 1163 /* Raw mode; put a "line break" on all characters in the input queue. 1164 * It is undefined what happens to the input queue when ICANON is 1165 * switched off, a process should use TCSAFLUSH to flush the queue. 1166 * Keeping the queue to preserve typeahead is the Right Thing, however 1167 * when a process does use TCSANOW to switch to raw mode. 1168 */ 1169 count = tp->tty_eotct = tp->tty_incount; 1170 inp = tp->tty_intail; 1171 while (count > 0) { 1172 *inp |= IN_EOT; 1173 if (++inp == bufend(tp->tty_inbuf)) inp = tp->tty_inbuf; 1174 --count; 1175 } 1176 } 1177 1178 /* Inspect MIN and TIME. */ 1179 settimer(tp, FALSE); 1180 if (tp->tty_termios.c_lflag & ICANON) { 1181 /* No MIN & TIME in canonical mode. */ 1182 tp->tty_min = 1; 1183 } else { 1184 /* In raw mode MIN is the number of chars wanted, and TIME how long 1185 * to wait for them. With interesting exceptions if either is zero. 1186 */ 1187 tp->tty_min = tp->tty_termios.c_cc[VMIN]; 1188 if (tp->tty_min == 0 && tp->tty_termios.c_cc[VTIME] > 0) 1189 tp->tty_min = 1; 1190 } 1191 1192 if (!(tp->tty_termios.c_iflag & IXON)) { 1193 /* No start/stop output control, so don't leave output inhibited. */ 1194 tp->tty_inhibited = RUNNING; 1195 tp->tty_events = 1; 1196 } 1197 1198 /* Setting the output speed to zero hangs up the phone. */ 1199 if (tp->tty_termios.c_ospeed == B0) sigchar(tp, SIGHUP, 1); 1200 } 1201 1202 /*===========================================================================* 1203 * sigchar * 1204 *===========================================================================*/ 1205 void sigchar(tp, sig, mayflush) 1206 register tty_t *tp; 1207 int sig; /* SIGINT, SIGQUIT, SIGKILL or SIGHUP */ 1208 int mayflush; 1209 { 1210 /* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard or SIGHUP from 1211 * a tty close, "stty 0", or a real RS-232 hangup. PM will send the signal to 1212 * the process group (INT, QUIT), all processes (KILL), or the session leader 1213 * (HUP). 1214 */ 1215 int status; 1216 1217 if (tp->tty_pgrp != 0) { 1218 if (OK != (status = sys_kill(tp->tty_pgrp, sig))) { 1219 panic("Error; call to sys_kill failed: %d", status); 1220 } 1221 } 1222 1223 if (mayflush && !(tp->tty_termios.c_lflag & NOFLSH)) { 1224 tp->tty_incount = tp->tty_eotct = 0; /* kill earlier input */ 1225 tp->tty_intail = tp->tty_inhead; 1226 (*tp->tty_ocancel)(tp, 0); /* kill all output */ 1227 tp->tty_inhibited = RUNNING; 1228 tp->tty_events = 1; 1229 } 1230 } 1231 1232 /*===========================================================================* 1233 * tty_icancel * 1234 *===========================================================================*/ 1235 static void tty_icancel(tp) 1236 register tty_t *tp; 1237 { 1238 /* Discard all pending input, tty buffer or device. */ 1239 1240 tp->tty_incount = tp->tty_eotct = 0; 1241 tp->tty_intail = tp->tty_inhead; 1242 (*tp->tty_icancel)(tp, 0); 1243 } 1244 1245 /*===========================================================================* 1246 * tty_devnop * 1247 *===========================================================================*/ 1248 static int tty_devnop(tty_t *UNUSED(tp), int UNUSED(try)) 1249 { 1250 /* Some functions need not be implemented at the device level. */ 1251 return 0; 1252 } 1253 1254 /*===========================================================================* 1255 * tty_init * 1256 *===========================================================================*/ 1257 static int tty_init(int UNUSED(type), sef_init_info_t *UNUSED(info)) 1258 { 1259 /* Initialize tty structure and call device initialization routines. */ 1260 1261 register tty_t *tp; 1262 int s; 1263 1264 system_hz = sys_hz(); 1265 1266 tty_gid = -1; 1267 if (env_argc > 1) 1268 optset_parse(optset_table, env_argv[1]); 1269 1270 /* Initialize the terminal lines. */ 1271 memset(tty_table, '\0' , sizeof(tty_table)); 1272 1273 for (tp = FIRST_TTY,s=0; tp < END_TTY; tp++,s++) { 1274 1275 tp->tty_index = s; 1276 init_timer(&tp->tty_tmr); 1277 1278 tp->tty_intail = tp->tty_inhead = tp->tty_inbuf; 1279 tp->tty_min = 1; 1280 tp->tty_incaller = tp->tty_outcaller = tp->tty_iocaller = NONE; 1281 tp->tty_termios = termios_defaults; 1282 tp->tty_icancel = tp->tty_ocancel = tp->tty_close = 1283 tp->tty_open = tty_devnop; 1284 pty_init(tp); 1285 } 1286 1287 return OK; 1288 } 1289 1290 /*===========================================================================* 1291 * tty_timed_out * 1292 *===========================================================================*/ 1293 static void tty_timed_out(int arg) 1294 { 1295 /* This timer has expired. Set the events flag, to force processing. */ 1296 tty_t *tty_ptr; 1297 tty_ptr = &tty_table[arg]; 1298 tty_ptr->tty_min = 0; /* force read to succeed */ 1299 tty_ptr->tty_events = 1; 1300 } 1301 1302 /*===========================================================================* 1303 * settimer * 1304 *===========================================================================*/ 1305 static void settimer(tty_ptr, enable) 1306 tty_t *tty_ptr; /* line to set or unset a timer on */ 1307 int enable; /* set timer if true, otherwise unset */ 1308 { 1309 clock_t ticks; 1310 1311 if (enable) { 1312 ticks = tty_ptr->tty_termios.c_cc[VTIME] * (system_hz/10); 1313 1314 /* Set a new timer for enabling the TTY events flags. */ 1315 set_timer(&tty_ptr->tty_tmr, ticks, tty_timed_out, tty_ptr->tty_index); 1316 } else { 1317 /* Remove the timer from the active and expired lists. */ 1318 cancel_timer(&tty_ptr->tty_tmr); 1319 } 1320 } 1321