1 /* This file contains the terminal driver, both for the IBM console and regular 2 * ASCII terminals. It handles only the device-independent part of a TTY, the 3 * device dependent parts are in console.c, rs232.c, etc. This file contains 4 * two main entry points, tty_task() and tty_wakeup(), and several minor entry 5 * points for use by the device-dependent code. 6 * 7 * The device-independent part accepts "keyboard" input from the device- 8 * dependent part, performs input processing (special key interpretation), 9 * and sends the input to a process reading from the TTY. Output to a TTY 10 * is sent to the device-dependent code for output processing and "screen" 11 * display. Input processing is done by the device by calling 'in_process' 12 * on the input characters, output processing may be done by the device itself 13 * or by calling 'out_process'. The TTY takes care of input queuing, the 14 * device does the output queuing. If a device receives an external signal, 15 * like an interrupt, then it causes tty_wakeup() to be run by the CLOCK task 16 * to, you guessed it, wake up the TTY to check if input or output can 17 * continue. 18 * 19 * Changes: 20 * Jan 20, 2004 moved TTY driver to user-space (Jorrit N. Herder) 21 * Sep 20, 2004 local timer management/ sync alarms (Jorrit N. Herder) 22 * Jul 13, 2004 support for function key observers (Jorrit N. Herder) 23 */ 24 25 #include <assert.h> 26 #include <minix/drivers.h> 27 #include <minix/driver.h> 28 #include <termios.h> 29 #include <sys/kbdio.h> 30 #include <sys/ttycom.h> 31 #include <sys/ttydefaults.h> 32 #include <sys/fcntl.h> 33 #include <signal.h> 34 #include <minix/keymap.h> 35 #include "tty.h" 36 37 #include <sys/time.h> 38 #include <sys/select.h> 39 40 unsigned long rs_irq_set = 0; 41 42 /* Address of a tty structure. */ 43 #define tty_addr(line) (&tty_table[line]) 44 45 /* Macros for magic tty types. */ 46 #define isconsole(tp) ((tp) < tty_addr(NR_CONS)) 47 48 /* Macros for magic tty structure pointers. */ 49 #define FIRST_TTY tty_addr(0) 50 #define END_TTY tty_addr(sizeof(tty_table) / sizeof(tty_table[0])) 51 52 /* A device exists if at least its 'devread' function is defined. */ 53 #define tty_active(tp) ((tp)->tty_devread != NULL) 54 55 /* RS232 lines or pseudo terminals can be completely configured out. */ 56 #if NR_RS_LINES == 0 57 #define rs_init(tp) ((void) 0) 58 #endif 59 60 struct kmessages kmess; 61 62 static void tty_timed_out(minix_timer_t *tp); 63 static void settimer(tty_t *tty_ptr, int enable); 64 static void in_transfer(tty_t *tp); 65 static int tty_echo(tty_t *tp, int ch); 66 static void rawecho(tty_t *tp, int ch); 67 static int back_over(tty_t *tp); 68 static void reprint(tty_t *tp); 69 static void dev_ioctl(tty_t *tp); 70 static void setattr(tty_t *tp); 71 static void tty_icancel(tty_t *tp); 72 static void tty_init(void); 73 static void do_new_kmess(void); 74 static void set_console_line(char term[CONS_ARG]); 75 static void set_kernel_color(char color[CONS_ARG]); 76 static void set_color(tty_t *tp, int color); 77 static void reset_color(tty_t *tp); 78 79 static int do_open(devminor_t minor, int access, endpoint_t user_endpt); 80 static int do_close(devminor_t minor); 81 static ssize_t do_read(devminor_t minor, u64_t position, endpoint_t endpt, 82 cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); 83 static ssize_t do_write(devminor_t minor, u64_t position, endpoint_t endpt, 84 cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); 85 static int do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, 86 cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id); 87 static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id); 88 static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt); 89 90 static struct chardriver tty_tab = { 91 .cdr_open = do_open, 92 .cdr_close = do_close, 93 .cdr_read = do_read, 94 .cdr_write = do_write, 95 .cdr_ioctl = do_ioctl, 96 .cdr_cancel = do_cancel, 97 .cdr_select = do_select 98 }; 99 100 /* Default attributes. */ 101 static struct termios termios_defaults = { 102 .c_iflag = TTYDEF_IFLAG, 103 .c_oflag = TTYDEF_OFLAG, 104 .c_cflag = TTYDEF_CFLAG, 105 .c_lflag = TTYDEF_LFLAG, 106 .c_ispeed = TTYDEF_SPEED, 107 .c_ospeed = TTYDEF_SPEED, 108 .c_cc = { 109 [VEOF] = CEOF, 110 [VEOL] = CEOL, 111 [VERASE] = CERASE, 112 [VINTR] = CINTR, 113 [VKILL] = CKILL, 114 [VMIN] = CMIN, 115 [VQUIT] = CQUIT, 116 [VTIME] = CTIME, 117 [VSUSP] = CSUSP, 118 [VSTART] = CSTART, 119 [VSTOP] = CSTOP, 120 [VREPRINT] = CREPRINT, 121 [VLNEXT] = CLNEXT, 122 [VDISCARD] = CDISCARD, 123 [VSTATUS] = CSTATUS 124 } 125 }; 126 static struct winsize winsize_defaults; /* = all zeroes */ 127 128 /* Global variables for the TTY task (declared extern in tty.h). */ 129 tty_t tty_table[NR_CONS+NR_RS_LINES]; 130 int ccurrent; /* currently active console */ 131 struct machine machine; /* kernel environment variables */ 132 u32_t system_hz; 133 u32_t consoleline = CONS_MINOR; 134 u32_t kernel_msg_color = 0; 135 136 /* SEF functions and variables. */ 137 static void sef_local_startup(void); 138 static int sef_cb_init_fresh(int type, sef_init_info_t *info); 139 static void sef_cb_signal_handler(int signo); 140 141 extern struct minix_kerninfo *_minix_kerninfo; 142 143 /*===========================================================================* 144 * tty_task * 145 *===========================================================================*/ 146 int main(void) 147 { 148 /* Main routine of the terminal task. */ 149 150 message tty_mess; /* buffer for all incoming messages */ 151 int ipc_status; 152 int line; 153 int r; 154 register tty_t *tp; 155 156 /* SEF local startup. */ 157 sef_local_startup(); 158 while (TRUE) { 159 /* Check for and handle any events on any of the ttys. */ 160 for (tp = FIRST_TTY; tp < END_TTY; tp++) { 161 if (tp->tty_events) handle_events(tp); 162 } 163 164 /* Get a request message. */ 165 r= driver_receive(ANY, &tty_mess, &ipc_status); 166 if (r != 0) 167 panic("driver_receive failed with: %d", r); 168 169 /* First handle all kernel notification types that the TTY supports. 170 * - An alarm went off, expire all timers and handle the events. 171 * - A hardware interrupt also is an invitation to check for events. 172 * - A new kernel message is available for printing. 173 * - Reset the console on system shutdown. 174 * Then see if this message is different from a normal device driver 175 * request and should be handled separately. These extra functions 176 * do not operate on a device, in constrast to the driver requests. 177 */ 178 179 if (is_ipc_notify(ipc_status)) { 180 switch (_ENDPOINT_P(tty_mess.m_source)) { 181 case CLOCK: 182 /* run watchdogs of expired timers */ 183 expire_timers(tty_mess.m_notify.timestamp); 184 break; 185 case HARDWARE: 186 /* hardware interrupt notification */ 187 188 #if NR_RS_LINES > 0 189 /* serial I/O */ 190 if (tty_mess.m_notify.interrupts & rs_irq_set) 191 rs_interrupt(&tty_mess); 192 #endif 193 /* run watchdogs of expired timers */ 194 expire_timers(tty_mess.m_notify.timestamp); 195 break; 196 default: 197 /* do nothing */ 198 break; 199 } 200 201 /* done, get new message */ 202 continue; 203 } 204 205 switch (tty_mess.m_type) { 206 case TTY_FKEY_CONTROL: /* (un)register a fkey observer */ 207 do_fkey_ctl(&tty_mess); 208 continue; 209 case TTY_INPUT_UP: 210 case TTY_INPUT_EVENT: 211 do_input(&tty_mess); 212 continue; 213 default: /* should be a driver request */ 214 ; /* do nothing; end switch */ 215 } 216 217 if (!IS_CDEV_RQ(tty_mess.m_type)) { 218 chardriver_process(&tty_tab, &tty_mess, ipc_status); 219 continue; 220 } 221 222 /* Only device requests should get to this point. 223 * All requests have a minor device number. 224 */ 225 if (OK != chardriver_get_minor(&tty_mess, &line)) 226 continue; 227 228 if (line == VIDEO_MINOR) { 229 do_video(&tty_mess, ipc_status); 230 continue; 231 } 232 233 /* Execute the requested device driver function. */ 234 chardriver_process(&tty_tab, &tty_mess, ipc_status); 235 } 236 237 return 0; 238 } 239 240 static void 241 set_color(tty_t *tp, int color) 242 { 243 char buf[8]; 244 245 buf[0] = '\033'; 246 snprintf(&buf[1], sizeof(buf) - 1, "[1;%dm", color); 247 do_write(tp->tty_minor, 0, KERNEL, (cp_grant_id_t) buf, sizeof(buf), 248 CDEV_NONBLOCK, 0); 249 } 250 251 static void 252 reset_color(tty_t *tp) 253 { 254 char buf[8]; 255 256 #define SGR_COLOR_RESET 39 257 buf[0] = '\033'; 258 snprintf(&buf[1], sizeof(buf) - 1, "[0;%dm", SGR_COLOR_RESET); 259 do_write(tp->tty_minor, 0, KERNEL, (cp_grant_id_t) buf, sizeof(buf), 260 CDEV_NONBLOCK, 0); 261 } 262 263 tty_t * 264 line2tty(devminor_t line) 265 { 266 /* Convert a terminal line to tty_table pointer */ 267 268 tty_t* tp; 269 270 /* /dev/log goes to /dev/console, and both may be redirected. */ 271 if (line == CONS_MINOR || line == LOG_MINOR) 272 line = consoleline; 273 274 if (line == VIDEO_MINOR) { 275 return(NULL); 276 } else if ((line - CONS_MINOR) < NR_CONS) { 277 tp = tty_addr(line - CONS_MINOR); 278 } else if ((line - RS232_MINOR) < NR_RS_LINES) { 279 tp = tty_addr(line - RS232_MINOR + NR_CONS); 280 } else { 281 tp = NULL; 282 } 283 284 if (tp != NULL && !tty_active(tp)) 285 tp = NULL; 286 287 return(tp); 288 } 289 290 /*===========================================================================* 291 * sef_local_startup * 292 *===========================================================================*/ 293 static void sef_local_startup() 294 { 295 /* Register init callbacks. */ 296 sef_setcb_init_fresh(sef_cb_init_fresh); 297 sef_setcb_init_restart(sef_cb_init_fresh); 298 299 /* No live update support for now. */ 300 301 /* Register signal callbacks. */ 302 sef_setcb_signal_handler(sef_cb_signal_handler); 303 304 /* Let SEF perform startup. */ 305 sef_startup(); 306 } 307 308 /*===========================================================================* 309 * sef_cb_init_fresh * 310 *===========================================================================*/ 311 static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info)) 312 { 313 /* Initialize the tty driver. */ 314 int r; 315 char val[CONS_ARG]; 316 317 /* Get kernel environment (protected_mode, pc_at and ega are needed). */ 318 if (OK != (r=sys_getmachine(&machine))) { 319 panic("Couldn't obtain kernel environment: %d", r); 320 } 321 322 if (env_get_param("console", val, sizeof(val)) == OK) { 323 set_console_line(val); 324 } 325 326 if ((r = env_get_param("kernelclr", val, sizeof(val))) == OK) { 327 set_kernel_color(val); 328 } 329 330 /* Initialize the TTY driver. */ 331 tty_init(); 332 333 /* Final one-time keyboard initialization. */ 334 kb_init_once(); 335 336 /* Register for diagnostics notifications. */ 337 sys_diagctl_register(); 338 339 return(OK); 340 } 341 342 static void 343 set_console_line(char term[CONS_ARG]) 344 { 345 /* Parse 'term' and redirect console output there. */ 346 int i; 347 348 /* Console */ 349 if (!strncmp(term, "console", CONS_ARG - 1)) { 350 consoleline = CONS_MINOR+0; 351 } 352 353 /* The other console terminals */ 354 for (i = 1; i < NR_CONS; i++) { 355 char cons[6]; 356 strlcpy(cons, "ttyc0", sizeof(cons)); 357 cons[4] += i; 358 if (!strncmp(term, cons, 359 CONS_ARG < sizeof(cons) ? CONS_ARG-1 : sizeof(cons) - 1)) 360 consoleline = CONS_MINOR + i; 361 } 362 363 /* Serial lines */ 364 assert(NR_RS_LINES <= 9);/* below assumes this is the case */ 365 for (i = 0; i < NR_RS_LINES; i++) { 366 char sercons[6]; 367 strlcpy(sercons, "tty00", sizeof(sercons)); 368 sercons[4] += i; 369 if (!strncmp(term, sercons, 370 CONS_ARG < sizeof(sercons) ? CONS_ARG-1:sizeof(sercons)-1)) 371 consoleline = RS232_MINOR + i; 372 } 373 } 374 375 static void 376 set_kernel_color(char color[CONS_ARG]) 377 { 378 int def_color; 379 380 #define SGR_COLOR_START 30 381 #define SGR_COLOR_END 37 382 383 def_color = atoi(color); 384 if ((SGR_COLOR_START + def_color) >= SGR_COLOR_START && 385 (SGR_COLOR_START + def_color) <= SGR_COLOR_END) { 386 kernel_msg_color = def_color + SGR_COLOR_START; 387 } 388 } 389 390 static void 391 do_new_kmess(void) 392 { 393 /* Kernel wants to print a new message */ 394 struct kmessages *kmess_ptr; /* kmessages structure */ 395 char kernel_buf_copy[_KMESS_BUF_SIZE]; 396 static int prev_next = 0; 397 int next, bytes, copy, restore = 0; 398 tty_t *tp, rtp; 399 400 assert(_minix_kerninfo); 401 kmess_ptr = _minix_kerninfo->kmessages; 402 403 /* The kernel buffer is circular; print only the new part. Determine 404 * how many new bytes there are with the help of current and 405 * previous 'next' index. This works fine if less than _KMESS_BUF_SIZE 406 * bytes is new data; else we miss % _KMESS_BUF_SIZE here. Obtain 407 * 'next' only once, since we are operating on shared memory here. 408 * Check for size being positive; the buffer might as well be emptied! 409 */ 410 next = kmess_ptr->km_next; 411 bytes = ((next + _KMESS_BUF_SIZE) - prev_next) % _KMESS_BUF_SIZE; 412 if (bytes > 0) { 413 /* Copy from current position toward end of buffer */ 414 copy = MIN(_KMESS_BUF_SIZE - prev_next, bytes); 415 memcpy(kernel_buf_copy, &kmess_ptr->km_buf[prev_next], copy); 416 417 /* Copy remainder from start of buffer */ 418 if (copy < bytes) { 419 memcpy(&kernel_buf_copy[copy], &kmess_ptr->km_buf[0], 420 bytes - copy); 421 } 422 423 tp = line2tty(consoleline); 424 if (tp == NULL) 425 panic("Don't know where to send kernel messages"); 426 if (tp->tty_outleft > 0) { 427 /* Terminal is already printing */ 428 rtp = *tp; /* Make backup */ 429 tp->tty_outleft = 0; /* So do_write is happy */ 430 restore = 1; 431 } 432 433 if (kernel_msg_color != 0) 434 set_color(tp, kernel_msg_color); 435 do_write(tp->tty_minor, 0, KERNEL, 436 (cp_grant_id_t) kernel_buf_copy, bytes, 437 CDEV_NONBLOCK, 0); 438 if (kernel_msg_color != 0) 439 reset_color(tp); 440 if (restore) { 441 *tp = rtp; 442 } 443 } 444 445 /* Store 'next' pointer so that we can determine what part of the 446 * kernel messages buffer to print next time a notification arrives. 447 */ 448 prev_next = next; 449 } 450 451 /*===========================================================================* 452 * sef_cb_signal_handler * 453 *===========================================================================*/ 454 static void sef_cb_signal_handler(int signo) 455 { 456 /* Check for known signals, ignore anything else. */ 457 switch(signo) { 458 /* There is a pending message from the kernel. */ 459 case SIGKMESS: 460 do_new_kmess(); 461 break; 462 /* Switch to primary console on termination. */ 463 case SIGTERM: 464 cons_stop(); 465 break; 466 } 467 } 468 469 /*===========================================================================* 470 * do_read * 471 *===========================================================================*/ 472 static ssize_t do_read(devminor_t minor, u64_t UNUSED(position), 473 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, 474 cdev_id_t id) 475 { 476 /* A process wants to read from a terminal. */ 477 tty_t *tp; 478 int r; 479 480 if ((tp = line2tty(minor)) == NULL) 481 return ENXIO; 482 483 /* Check if there is already a process hanging in a read, check if the 484 * parameters are correct, do I/O. 485 */ 486 if (tp->tty_incaller != NONE || tp->tty_inleft > 0) 487 return EIO; 488 if (size <= 0) 489 return EINVAL; 490 491 /* Copy information from the message to the tty struct. */ 492 tp->tty_incaller = endpt; 493 tp->tty_inid = id; 494 tp->tty_ingrant = grant; 495 assert(tp->tty_incum == 0); 496 tp->tty_inleft = size; 497 498 if (!(tp->tty_termios.c_lflag & ICANON) && tp->tty_termios.c_cc[VTIME] > 0) { 499 if (tp->tty_termios.c_cc[VMIN] == 0) { 500 /* MIN & TIME specify a read timer that finishes the 501 * read in TIME/10 seconds if no bytes are available. 502 */ 503 settimer(tp, TRUE); 504 tp->tty_min = 1; 505 } else { 506 /* MIN & TIME specify an inter-byte timer that may 507 * have to be cancelled if there are no bytes yet. 508 */ 509 if (tp->tty_eotct == 0) { 510 settimer(tp, FALSE); 511 tp->tty_min = tp->tty_termios.c_cc[VMIN]; 512 } 513 } 514 } 515 516 /* Anything waiting in the input buffer? Clear it out... */ 517 in_transfer(tp); 518 /* ...then go back for more. */ 519 handle_events(tp); 520 if (tp->tty_inleft == 0) 521 return EDONTREPLY; /* already done */ 522 523 /* There were no bytes in the input queue available. */ 524 if (flags & CDEV_NONBLOCK) { 525 tty_icancel(tp); 526 r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN; 527 tp->tty_inleft = tp->tty_incum = 0; 528 tp->tty_incaller = NONE; 529 return r; 530 } 531 532 if (tp->tty_select_ops) 533 select_retry(tp); 534 535 return EDONTREPLY; /* suspend the caller */ 536 } 537 538 /*===========================================================================* 539 * do_write * 540 *===========================================================================*/ 541 static ssize_t do_write(devminor_t minor, u64_t UNUSED(position), 542 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, 543 cdev_id_t id) 544 { 545 /* A process wants to write on a terminal. */ 546 tty_t *tp; 547 int r; 548 549 if ((tp = line2tty(minor)) == NULL) 550 return ENXIO; 551 552 /* Check if there is already a process hanging in a write, check if the 553 * parameters are correct, do I/O. 554 */ 555 if (tp->tty_outcaller != NONE || tp->tty_outleft > 0) 556 return EIO; 557 if (size <= 0) 558 return EINVAL; 559 560 /* Copy message parameters to the tty structure. */ 561 tp->tty_outcaller = endpt; 562 tp->tty_outid = id; 563 tp->tty_outgrant = grant; 564 assert(tp->tty_outcum == 0); 565 tp->tty_outleft = size; 566 567 /* Try to write. */ 568 handle_events(tp); 569 if (tp->tty_outleft == 0) 570 return EDONTREPLY; /* already done */ 571 572 /* None or not all the bytes could be written. */ 573 if (flags & CDEV_NONBLOCK) { 574 r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN; 575 tp->tty_outleft = tp->tty_outcum = 0; 576 tp->tty_outcaller = NONE; 577 return r; 578 } 579 580 if (tp->tty_select_ops) 581 select_retry(tp); 582 583 return EDONTREPLY; /* suspend the caller */ 584 } 585 586 /*===========================================================================* 587 * do_ioctl * 588 *===========================================================================*/ 589 static int do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, 590 cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id) 591 { 592 /* Perform an IOCTL on this terminal. POSIX termios calls are handled 593 * by the IOCTL system call. 594 */ 595 kio_bell_t bell; 596 clock_t ticks; 597 tty_t *tp; 598 int i, r; 599 600 if ((tp = line2tty(minor)) == NULL) 601 return ENXIO; 602 603 r = OK; 604 switch (request) { 605 case TIOCGETA: 606 /* Get the termios attributes. */ 607 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_termios, 608 sizeof(struct termios)); 609 break; 610 611 case TIOCSETAW: 612 case TIOCSETAF: 613 case TIOCDRAIN: 614 if (tp->tty_outleft > 0) { 615 if (flags & CDEV_NONBLOCK) 616 return EAGAIN; 617 /* Wait for all ongoing output processing to finish. */ 618 tp->tty_iocaller = endpt; 619 tp->tty_ioid = id; 620 tp->tty_ioreq = request; 621 tp->tty_iogrant = grant; 622 return EDONTREPLY; /* suspend the caller */ 623 } 624 if (request == TIOCDRAIN) break; 625 if (request == TIOCSETAF) tty_icancel(tp); 626 /*FALL THROUGH*/ 627 case TIOCSETA: 628 /* Set the termios attributes. */ 629 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_termios, 630 sizeof(struct termios)); 631 if (r != OK) break; 632 setattr(tp); 633 break; 634 635 case TIOCFLUSH: 636 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &i, sizeof(i)); 637 if (r != OK) break; 638 if(i & FREAD) { tty_icancel(tp); } 639 if(i & FWRITE) { (*tp->tty_ocancel)(tp, 0); } 640 break; 641 case TIOCSTART: 642 tp->tty_inhibited = 0; 643 tp->tty_events = 1; 644 break; 645 case TIOCSTOP: 646 tp->tty_inhibited = 1; 647 tp->tty_events = 1; 648 break; 649 case TIOCSBRK: /* tcsendbreak - turn break on */ 650 if (tp->tty_break_on != NULL) (*tp->tty_break_on)(tp,0); 651 break; 652 case TIOCCBRK: /* tcsendbreak - turn break off */ 653 if (tp->tty_break_off != NULL) (*tp->tty_break_off)(tp,0); 654 break; 655 656 case TIOCGWINSZ: 657 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_winsize, 658 sizeof(struct winsize)); 659 break; 660 661 case TIOCSWINSZ: 662 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_winsize, 663 sizeof(struct winsize)); 664 sigchar(tp, SIGWINCH, 0); 665 break; 666 case KIOCBELL: 667 /* Sound bell (only /dev/console). */ 668 if (!isconsole(tp)) 669 break; 670 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &bell, sizeof(bell)); 671 if (r != OK) 672 break; 673 ticks = bell.kb_duration.tv_usec * system_hz / 1000000; 674 ticks += bell.kb_duration.tv_sec * system_hz; 675 if (!ticks) 676 ticks++; 677 beep_x(bell.kb_pitch, ticks); 678 break; 679 case TIOCGETD: /* get line discipline */ 680 { 681 int disc = TTYDISC; 682 r = sys_safecopyto(endpt, grant, 0, 683 (vir_bytes) &disc, (vir_bytes) sizeof(disc)); 684 break; 685 } 686 case TIOCSETD: /* set line discipline */ 687 printf("TTY: TIOCSETD: can't set any other line discipline.\n"); 688 r = ENOTTY; 689 break; 690 case KIOCSMAP: 691 /* Load a new keymap (only /dev/console). */ 692 if (isconsole(tp)) r = kbd_loadmap(endpt, grant); 693 break; 694 695 case TIOCSFON: 696 /* Load a font into an EGA or VGA card (hs@hck.hr) */ 697 if (isconsole(tp)) r = con_loadfont(endpt, grant); 698 break; 699 700 case TIOCSCTTY: 701 /* Process sets this tty as its controlling tty */ 702 tp->tty_pgrp = user_endpt; 703 break; 704 705 /* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is 706 * not defined. 707 */ 708 case TIOCGPGRP: 709 case TIOCSPGRP: 710 default: 711 r = ENOTTY; 712 } 713 714 return r; 715 } 716 717 /*===========================================================================* 718 * do_open * 719 *===========================================================================*/ 720 static int do_open(devminor_t minor, int access, endpoint_t user_endpt) 721 { 722 /* A tty line has been opened. Make it the callers controlling tty if 723 * CDEV_NOCTTY is *not* set and it is not the log device. CDEV_CTTY is returned 724 * if the tty is made the controlling tty, otherwise OK or an error code. 725 */ 726 tty_t *tp; 727 int r = OK; 728 729 if ((tp = line2tty(minor)) == NULL) 730 return ENXIO; 731 732 if (minor == LOG_MINOR && isconsole(tp)) { 733 /* The log device is a write-only diagnostics device. */ 734 if (access & CDEV_R_BIT) return EACCES; 735 } else { 736 if (!(access & CDEV_NOCTTY)) { 737 tp->tty_pgrp = user_endpt; 738 r = CDEV_CTTY; 739 } 740 tp->tty_openct++; 741 if (tp->tty_openct == 1) { 742 /* Tell the device that the tty is opened */ 743 (*tp->tty_open)(tp, 0); 744 } 745 } 746 747 return r; 748 } 749 750 /*===========================================================================* 751 * do_close * 752 *===========================================================================*/ 753 static int do_close(devminor_t minor) 754 { 755 /* A tty line has been closed. Clean up the line if it is the last close. */ 756 tty_t *tp; 757 758 if ((tp = line2tty(minor)) == NULL) 759 return ENXIO; 760 761 if ((minor != LOG_MINOR || !isconsole(tp)) && --tp->tty_openct == 0) { 762 tp->tty_pgrp = 0; 763 tty_icancel(tp); 764 (*tp->tty_ocancel)(tp, 0); 765 (*tp->tty_close)(tp, 0); 766 tp->tty_termios = termios_defaults; 767 tp->tty_winsize = winsize_defaults; 768 setattr(tp); 769 } 770 771 return OK; 772 } 773 774 /*===========================================================================* 775 * do_cancel * 776 *===========================================================================*/ 777 static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id) 778 { 779 /* A signal has been sent to a process that is hanging trying to read or write. 780 * The pending read or write must be finished off immediately. 781 */ 782 tty_t *tp; 783 int r; 784 785 if ((tp = line2tty(minor)) == NULL) 786 return ENXIO; 787 788 /* Check the parameters carefully, to avoid cancelling twice. */ 789 r = EDONTREPLY; 790 if (tp->tty_inleft != 0 && endpt == tp->tty_incaller && id == tp->tty_inid) { 791 /* Process was reading when killed. Clean up input. */ 792 tty_icancel(tp); 793 r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN; 794 tp->tty_inleft = tp->tty_incum = 0; 795 tp->tty_incaller = NONE; 796 } else if (tp->tty_outleft != 0 && endpt == tp->tty_outcaller && 797 id == tp->tty_outid) { 798 /* Process was writing when killed. Clean up output. */ 799 r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN; 800 tp->tty_outleft = tp->tty_outcum = 0; 801 tp->tty_outcaller = NONE; 802 } else if (tp->tty_ioreq != 0 && endpt == tp->tty_iocaller && 803 id == tp->tty_ioid) { 804 /* Process was waiting for output to drain. */ 805 r = EINTR; 806 tp->tty_ioreq = 0; 807 tp->tty_iocaller = NONE; 808 } 809 if (r != EDONTREPLY) 810 tp->tty_events = 1; 811 /* Only reply if we found a matching request. */ 812 return r; 813 } 814 815 int select_try(struct tty *tp, int ops) 816 { 817 int ready_ops = 0; 818 819 /* Special case. If line is hung up, no operations will block. 820 * (and it can be seen as an exceptional condition.) 821 */ 822 if (tp->tty_termios.c_ospeed == B0) { 823 ready_ops |= ops; 824 } 825 826 if (ops & CDEV_OP_RD) { 827 /* will i/o not block on read? */ 828 if (tp->tty_inleft > 0) { 829 ready_ops |= CDEV_OP_RD; /* EIO - no blocking */ 830 } else if (tp->tty_incount > 0) { 831 /* Is a regular read possible? tty_incount 832 * says there is data. But a read will only succeed 833 * in canonical mode if a newline has been seen. 834 */ 835 if (!(tp->tty_termios.c_lflag & ICANON) || 836 tp->tty_eotct > 0) { 837 ready_ops |= CDEV_OP_RD; 838 } 839 } 840 } 841 842 if (ops & CDEV_OP_WR) { 843 if (tp->tty_outleft > 0) ready_ops |= CDEV_OP_WR; 844 else if ((*tp->tty_devwrite)(tp, 1)) ready_ops |= CDEV_OP_WR; 845 } 846 return ready_ops; 847 } 848 849 int select_retry(struct tty *tp) 850 { 851 int ops; 852 853 if (tp->tty_select_ops && (ops = select_try(tp, tp->tty_select_ops))) { 854 chardriver_reply_select(tp->tty_select_proc, 855 tp->tty_select_minor, ops); 856 tp->tty_select_ops &= ~ops; 857 } 858 return OK; 859 } 860 861 /*===========================================================================* 862 * do_select * 863 *===========================================================================*/ 864 static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt) 865 { 866 tty_t *tp; 867 int ready_ops, watch; 868 869 if ((tp = line2tty(minor)) == NULL) 870 return ENXIO; 871 872 watch = (ops & CDEV_NOTIFY); 873 ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR); 874 875 ready_ops = select_try(tp, ops); 876 877 ops &= ~ready_ops; 878 if (ops && watch) { 879 /* Translated minor numbers are a problem with late select replies. We 880 * have to save the minor number used to do the select, since otherwise 881 * VFS won't be able to make sense of those late replies. We do not 882 * support selecting on two different minors for the same object. 883 */ 884 if (tp->tty_select_ops != 0 && tp->tty_select_minor != minor) { 885 printf("TTY: select on one object with two minors (%d, %d)\n", 886 tp->tty_select_minor, minor); 887 return EBADF; 888 } 889 tp->tty_select_ops |= ops; 890 tp->tty_select_proc = endpt; 891 tp->tty_select_minor = minor; 892 } 893 894 return ready_ops; 895 } 896 897 /*===========================================================================* 898 * handle_events * 899 *===========================================================================*/ 900 void handle_events(tp) 901 tty_t *tp; /* TTY to check for events. */ 902 { 903 /* Handle any events pending on a TTY. These events are usually device 904 * interrupts. 905 * 906 * Two kinds of events are prominent: 907 * - a character has been received from the console or an RS232 line. 908 * - an RS232 line has completed a write request (on behalf of a user). 909 * The interrupt handler may delay the interrupt message at its discretion 910 * to avoid swamping the TTY task. Messages may be overwritten when the 911 * lines are fast or when there are races between different lines, input 912 * and output, because MINIX only provides single buffering for interrupt 913 * messages. This is handled by explicitly checking each line for fresh input 914 * and completed output on each interrupt. 915 */ 916 917 do { 918 tp->tty_events = 0; 919 920 /* Read input and perform input processing. */ 921 (*tp->tty_devread)(tp, 0); 922 923 /* Perform output processing and write output. */ 924 (*tp->tty_devwrite)(tp, 0); 925 926 /* Ioctl waiting for some event? */ 927 if (tp->tty_ioreq != 0) dev_ioctl(tp); 928 } while (tp->tty_events); 929 930 /* Transfer characters from the input queue to a waiting process. */ 931 in_transfer(tp); 932 933 /* Reply if enough bytes are available. */ 934 if (tp->tty_incum >= tp->tty_min && tp->tty_inleft > 0) { 935 chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum); 936 tp->tty_inleft = tp->tty_incum = 0; 937 tp->tty_incaller = NONE; 938 } 939 if (tp->tty_select_ops) 940 { 941 select_retry(tp); 942 } 943 } 944 945 /*===========================================================================* 946 * in_transfer * 947 *===========================================================================*/ 948 static void in_transfer(tp) 949 register tty_t *tp; /* pointer to terminal to read from */ 950 { 951 /* Transfer bytes from the input queue to a process reading from a terminal. */ 952 953 int ch; 954 int count; 955 char buf[64], *bp; 956 957 /* Force read to succeed if the line is hung up, looks like EOF to reader. */ 958 if (tp->tty_termios.c_ospeed == B0) tp->tty_min = 0; 959 960 /* Anything to do? */ 961 if (tp->tty_inleft == 0 || tp->tty_eotct < tp->tty_min) return; 962 963 bp = buf; 964 while (tp->tty_inleft > 0 && tp->tty_eotct > 0) { 965 ch = *tp->tty_intail; 966 967 if (!(ch & IN_EOF)) { 968 /* One character to be delivered to the user. */ 969 *bp = ch & IN_CHAR; 970 tp->tty_inleft--; 971 if (++bp == bufend(buf)) { 972 /* Temp buffer full, copy to user space. */ 973 sys_safecopyto(tp->tty_incaller, 974 tp->tty_ingrant, tp->tty_incum, 975 (vir_bytes) buf, (vir_bytes) buflen(buf)); 976 tp->tty_incum += buflen(buf); 977 bp = buf; 978 } 979 } 980 981 /* Remove the character from the input queue. */ 982 if (++tp->tty_intail == bufend(tp->tty_inbuf)) 983 tp->tty_intail = tp->tty_inbuf; 984 tp->tty_incount--; 985 if (ch & IN_EOT) { 986 tp->tty_eotct--; 987 /* Don't read past a line break in canonical mode. */ 988 if (tp->tty_termios.c_lflag & ICANON) tp->tty_inleft = 0; 989 } 990 } 991 992 if (bp > buf) { 993 /* Leftover characters in the buffer. */ 994 count = bp - buf; 995 sys_safecopyto(tp->tty_incaller, tp->tty_ingrant, tp->tty_incum, 996 (vir_bytes) buf, (vir_bytes) count); 997 tp->tty_incum += count; 998 } 999 1000 /* Usually reply to the reader, possibly even if incum == 0 (EOF). */ 1001 if (tp->tty_inleft == 0) { 1002 chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum); 1003 tp->tty_inleft = tp->tty_incum = 0; 1004 tp->tty_incaller = NONE; 1005 } 1006 } 1007 1008 /*===========================================================================* 1009 * in_process * 1010 *===========================================================================*/ 1011 int in_process(tp, buf, count) 1012 register tty_t *tp; /* terminal on which character has arrived */ 1013 char *buf; /* buffer with input characters */ 1014 int count; /* number of input characters */ 1015 { 1016 /* Characters have just been typed in. Process, save, and echo them. Return 1017 * the number of characters processed. 1018 */ 1019 1020 int ch, sig, ct; 1021 int timeset = FALSE; 1022 1023 for (ct = 0; ct < count; ct++) { 1024 /* Take one character. */ 1025 ch = *buf++ & BYTE; 1026 1027 /* Strip to seven bits? */ 1028 if (tp->tty_termios.c_iflag & ISTRIP) ch &= 0x7F; 1029 1030 /* Input extensions? */ 1031 if (tp->tty_termios.c_lflag & IEXTEN) { 1032 1033 /* Previous character was a character escape? */ 1034 if (tp->tty_escaped) { 1035 tp->tty_escaped = NOT_ESCAPED; 1036 ch |= IN_ESC; /* protect character */ 1037 } 1038 1039 /* LNEXT (^V) to escape the next character? */ 1040 if (ch == tp->tty_termios.c_cc[VLNEXT]) { 1041 tp->tty_escaped = ESCAPED; 1042 rawecho(tp, '^'); 1043 rawecho(tp, '\b'); 1044 continue; /* do not store the escape */ 1045 } 1046 1047 /* REPRINT (^R) to reprint echoed characters? */ 1048 if (ch == tp->tty_termios.c_cc[VREPRINT]) { 1049 reprint(tp); 1050 continue; 1051 } 1052 } 1053 1054 /* _POSIX_VDISABLE is a normal character value, so better escape it. */ 1055 if (ch == _POSIX_VDISABLE) ch |= IN_ESC; 1056 1057 /* Map CR to LF, ignore CR, or map LF to CR. */ 1058 if (ch == '\r') { 1059 if (tp->tty_termios.c_iflag & IGNCR) continue; 1060 if (tp->tty_termios.c_iflag & ICRNL) ch = '\n'; 1061 } else 1062 if (ch == '\n') { 1063 if (tp->tty_termios.c_iflag & INLCR) ch = '\r'; 1064 } 1065 1066 /* Canonical mode? */ 1067 if (tp->tty_termios.c_lflag & ICANON) { 1068 1069 /* Erase processing (rub out of last character). */ 1070 if (ch == tp->tty_termios.c_cc[VERASE]) { 1071 (void) back_over(tp); 1072 if (!(tp->tty_termios.c_lflag & ECHOE)) { 1073 (void) tty_echo(tp, ch); 1074 } 1075 continue; 1076 } 1077 1078 /* Kill processing (remove current line). */ 1079 if (ch == tp->tty_termios.c_cc[VKILL]) { 1080 while (back_over(tp)) {} 1081 if (!(tp->tty_termios.c_lflag & ECHOE)) { 1082 (void) tty_echo(tp, ch); 1083 if (tp->tty_termios.c_lflag & ECHOK) 1084 rawecho(tp, '\n'); 1085 } 1086 continue; 1087 } 1088 1089 /* EOF (^D) means end-of-file, an invisible "line break". */ 1090 if (ch == tp->tty_termios.c_cc[VEOF]) ch |= IN_EOT | IN_EOF; 1091 1092 /* The line may be returned to the user after an LF. */ 1093 if (ch == '\n') ch |= IN_EOT; 1094 1095 /* Same thing with EOL, whatever it may be. */ 1096 if (ch == tp->tty_termios.c_cc[VEOL]) ch |= IN_EOT; 1097 } 1098 1099 /* Start/stop input control? */ 1100 if (tp->tty_termios.c_iflag & IXON) { 1101 1102 /* Output stops on STOP (^S). */ 1103 if (ch == tp->tty_termios.c_cc[VSTOP]) { 1104 tp->tty_inhibited = STOPPED; 1105 tp->tty_events = 1; 1106 continue; 1107 } 1108 1109 /* Output restarts on START (^Q) or any character if IXANY. */ 1110 if (tp->tty_inhibited) { 1111 if (ch == tp->tty_termios.c_cc[VSTART] 1112 || (tp->tty_termios.c_iflag & IXANY)) { 1113 tp->tty_inhibited = RUNNING; 1114 tp->tty_events = 1; 1115 if (ch == tp->tty_termios.c_cc[VSTART]) 1116 continue; 1117 } 1118 } 1119 } 1120 1121 if (tp->tty_termios.c_lflag & ISIG) { 1122 /* Check for INTR, QUIT and STATUS characters. */ 1123 int sig = -1; 1124 if (ch == tp->tty_termios.c_cc[VINTR]) 1125 sig = SIGINT; 1126 else if(ch == tp->tty_termios.c_cc[VQUIT]) 1127 sig = SIGQUIT; 1128 else if(ch == tp->tty_termios.c_cc[VSTATUS]) 1129 sig = SIGINFO; 1130 1131 if(sig >= 0) { 1132 sigchar(tp, sig, 1); 1133 (void) tty_echo(tp, ch); 1134 continue; 1135 } 1136 } 1137 1138 /* Is there space in the input buffer? */ 1139 if (tp->tty_incount == buflen(tp->tty_inbuf)) { 1140 /* No space; discard in canonical mode, keep in raw mode. */ 1141 if (tp->tty_termios.c_lflag & ICANON) continue; 1142 break; 1143 } 1144 1145 if (!(tp->tty_termios.c_lflag & ICANON)) { 1146 /* In raw mode all characters are "line breaks". */ 1147 ch |= IN_EOT; 1148 1149 /* Start an inter-byte timer? */ 1150 if (!timeset && tp->tty_termios.c_cc[VMIN] > 0 1151 && tp->tty_termios.c_cc[VTIME] > 0) { 1152 settimer(tp, TRUE); 1153 timeset = TRUE; 1154 } 1155 } 1156 1157 /* Perform the intricate function of echoing. */ 1158 if (tp->tty_termios.c_lflag & (ECHO|ECHONL)) ch = tty_echo(tp, ch); 1159 1160 /* Save the character in the input queue. */ 1161 *tp->tty_inhead++ = ch; 1162 if (tp->tty_inhead == bufend(tp->tty_inbuf)) 1163 tp->tty_inhead = tp->tty_inbuf; 1164 tp->tty_incount++; 1165 if (ch & IN_EOT) tp->tty_eotct++; 1166 1167 /* Try to finish input if the queue threatens to overflow. */ 1168 if (tp->tty_incount == buflen(tp->tty_inbuf)) in_transfer(tp); 1169 } 1170 return ct; 1171 } 1172 1173 /*===========================================================================* 1174 * echo * 1175 *===========================================================================*/ 1176 static int tty_echo(tp, ch) 1177 register tty_t *tp; /* terminal on which to echo */ 1178 register int ch; /* pointer to character to echo */ 1179 { 1180 /* Echo the character if echoing is on. Some control characters are echoed 1181 * with their normal effect, other control characters are echoed as "^X", 1182 * normal characters are echoed normally. EOF (^D) is echoed, but immediately 1183 * backspaced over. Return the character with the echoed length added to its 1184 * attributes. 1185 */ 1186 int len, rp; 1187 1188 ch &= ~IN_LEN; 1189 if (!(tp->tty_termios.c_lflag & ECHO)) { 1190 if (ch == ('\n' | IN_EOT) && (tp->tty_termios.c_lflag 1191 & (ICANON|ECHONL)) == (ICANON|ECHONL)) 1192 (*tp->tty_echo)(tp, '\n'); 1193 return(ch); 1194 } 1195 1196 /* "Reprint" tells if the echo output has been messed up by other output. */ 1197 rp = tp->tty_incount == 0 ? FALSE : tp->tty_reprint; 1198 1199 if ((ch & IN_CHAR) < ' ') { 1200 switch (ch & (IN_ESC|IN_EOF|IN_EOT|IN_CHAR)) { 1201 case '\t': 1202 len = 0; 1203 do { 1204 (*tp->tty_echo)(tp, ' '); 1205 len++; 1206 } while (len < TAB_SIZE && (tp->tty_position & TAB_MASK) != 0); 1207 break; 1208 case '\r' | IN_EOT: 1209 case '\n' | IN_EOT: 1210 (*tp->tty_echo)(tp, ch & IN_CHAR); 1211 len = 0; 1212 break; 1213 default: 1214 (*tp->tty_echo)(tp, '^'); 1215 (*tp->tty_echo)(tp, '@' + (ch & IN_CHAR)); 1216 len = 2; 1217 } 1218 } else 1219 if ((ch & IN_CHAR) == '\177') { 1220 /* A DEL prints as "^?". */ 1221 (*tp->tty_echo)(tp, '^'); 1222 (*tp->tty_echo)(tp, '?'); 1223 len = 2; 1224 } else { 1225 (*tp->tty_echo)(tp, ch & IN_CHAR); 1226 len = 1; 1227 } 1228 if (ch & IN_EOF) while (len > 0) { (*tp->tty_echo)(tp, '\b'); len--; } 1229 1230 tp->tty_reprint = rp; 1231 return(ch | (len << IN_LSHIFT)); 1232 } 1233 1234 /*===========================================================================* 1235 * rawecho * 1236 *===========================================================================*/ 1237 static void rawecho(tp, ch) 1238 register tty_t *tp; 1239 int ch; 1240 { 1241 /* Echo without interpretation if ECHO is set. */ 1242 int rp = tp->tty_reprint; 1243 if (tp->tty_termios.c_lflag & ECHO) (*tp->tty_echo)(tp, ch); 1244 tp->tty_reprint = rp; 1245 } 1246 1247 /*===========================================================================* 1248 * back_over * 1249 *===========================================================================*/ 1250 static int back_over(tp) 1251 register tty_t *tp; 1252 { 1253 /* Backspace to previous character on screen and erase it. */ 1254 u16_t *head; 1255 int len; 1256 1257 if (tp->tty_incount == 0) return(0); /* queue empty */ 1258 head = tp->tty_inhead; 1259 if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf); 1260 if (*--head & IN_EOT) return(0); /* can't erase "line breaks" */ 1261 if (tp->tty_reprint) reprint(tp); /* reprint if messed up */ 1262 tp->tty_inhead = head; 1263 tp->tty_incount--; 1264 if (tp->tty_termios.c_lflag & ECHOE) { 1265 len = (*head & IN_LEN) >> IN_LSHIFT; 1266 while (len > 0) { 1267 rawecho(tp, '\b'); 1268 rawecho(tp, ' '); 1269 rawecho(tp, '\b'); 1270 len--; 1271 } 1272 } 1273 return(1); /* one character erased */ 1274 } 1275 1276 /*===========================================================================* 1277 * reprint * 1278 *===========================================================================*/ 1279 static void reprint(tp) 1280 register tty_t *tp; /* pointer to tty struct */ 1281 { 1282 /* Restore what has been echoed to screen before if the user input has been 1283 * messed up by output, or if REPRINT (^R) is typed. 1284 */ 1285 int count; 1286 u16_t *head; 1287 1288 tp->tty_reprint = FALSE; 1289 1290 /* Find the last line break in the input. */ 1291 head = tp->tty_inhead; 1292 count = tp->tty_incount; 1293 while (count > 0) { 1294 if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf); 1295 if (head[-1] & IN_EOT) break; 1296 head--; 1297 count--; 1298 } 1299 if (count == tp->tty_incount) return; /* no reason to reprint */ 1300 1301 /* Show REPRINT (^R) and move to a new line. */ 1302 (void) tty_echo(tp, tp->tty_termios.c_cc[VREPRINT] | IN_ESC); 1303 rawecho(tp, '\r'); 1304 rawecho(tp, '\n'); 1305 1306 /* Reprint from the last break onwards. */ 1307 do { 1308 if (head == bufend(tp->tty_inbuf)) head = tp->tty_inbuf; 1309 *head = tty_echo(tp, *head); 1310 head++; 1311 count++; 1312 } while (count < tp->tty_incount); 1313 } 1314 1315 /*===========================================================================* 1316 * out_process * 1317 *===========================================================================*/ 1318 void out_process(tp, bstart, bpos, bend, icount, ocount) 1319 tty_t *tp; 1320 char *bstart, *bpos, *bend; /* start/pos/end of circular buffer */ 1321 int *icount; /* # input chars / input chars used */ 1322 int *ocount; /* max output chars / output chars used */ 1323 { 1324 /* Perform output processing on a circular buffer. *icount is the number of 1325 * bytes to process, and the number of bytes actually processed on return. 1326 * *ocount is the space available on input and the space used on output. 1327 * (Naturally *icount < *ocount.) The column position is updated modulo 1328 * the TAB size, because we really only need it for tabs. 1329 */ 1330 1331 int tablen; 1332 int ict = *icount; 1333 int oct = *ocount; 1334 int pos = tp->tty_position; 1335 1336 while (ict > 0) { 1337 switch (*bpos) { 1338 case '\7': 1339 break; 1340 case '\b': 1341 pos--; 1342 break; 1343 case '\r': 1344 pos = 0; 1345 break; 1346 case '\n': 1347 if ((tp->tty_termios.c_oflag & (OPOST|ONLCR)) 1348 == (OPOST|ONLCR)) { 1349 /* Map LF to CR+LF if there is space. Note that the 1350 * next character in the buffer is overwritten, so 1351 * we stop at this point. 1352 */ 1353 if (oct >= 2) { 1354 *bpos = '\r'; 1355 if (++bpos == bend) bpos = bstart; 1356 *bpos = '\n'; 1357 pos = 0; 1358 ict--; 1359 oct -= 2; 1360 } 1361 goto out_done; /* no space or buffer got changed */ 1362 } 1363 break; 1364 case '\t': 1365 /* Best guess for the tab length. */ 1366 tablen = TAB_SIZE - (pos & TAB_MASK); 1367 1368 if ((tp->tty_termios.c_oflag & (OPOST|OXTABS)) 1369 == (OPOST|OXTABS)) { 1370 /* Tabs must be expanded. */ 1371 if (oct >= tablen) { 1372 pos += tablen; 1373 ict--; 1374 oct -= tablen; 1375 do { 1376 *bpos = ' '; 1377 if (++bpos == bend) bpos = bstart; 1378 } while (--tablen != 0); 1379 } 1380 goto out_done; 1381 } 1382 /* Tabs are output directly. */ 1383 pos += tablen; 1384 break; 1385 default: 1386 /* Assume any other character prints as one character. */ 1387 pos++; 1388 } 1389 if (++bpos == bend) bpos = bstart; 1390 ict--; 1391 oct--; 1392 } 1393 out_done: 1394 tp->tty_position = pos & TAB_MASK; 1395 1396 *icount -= ict; /* [io]ct are the number of chars not used */ 1397 *ocount -= oct; /* *[io]count are the number of chars that are used */ 1398 } 1399 1400 /*===========================================================================* 1401 * dev_ioctl * 1402 *===========================================================================*/ 1403 static void dev_ioctl(tp) 1404 tty_t *tp; 1405 { 1406 /* The ioctl's TCSETSW, TCSETSF and TIOCDRAIN wait for output to finish to make 1407 * sure that an attribute change doesn't affect the processing of current 1408 * output. Once output finishes the ioctl is executed as in do_ioctl(). 1409 */ 1410 int result = EINVAL; 1411 1412 if (tp->tty_outleft > 0) return; /* output not finished */ 1413 1414 if (tp->tty_ioreq != TIOCDRAIN) { 1415 if (tp->tty_ioreq == TIOCSETAF) tty_icancel(tp); 1416 result = sys_safecopyfrom(tp->tty_iocaller, tp->tty_iogrant, 0, 1417 (vir_bytes) &tp->tty_termios, 1418 (vir_bytes) sizeof(tp->tty_termios)); 1419 if (result == OK) setattr(tp); 1420 } 1421 tp->tty_ioreq = 0; 1422 chardriver_reply_task(tp->tty_iocaller, tp->tty_ioid, result); 1423 tp->tty_iocaller = NONE; 1424 } 1425 1426 /*===========================================================================* 1427 * setattr * 1428 *===========================================================================*/ 1429 static void setattr(tp) 1430 tty_t *tp; 1431 { 1432 /* Apply the new line attributes (raw/canonical, line speed, etc.) */ 1433 u16_t *inp; 1434 int count; 1435 1436 if (!(tp->tty_termios.c_lflag & ICANON)) { 1437 /* Raw mode; put a "line break" on all characters in the input queue. 1438 * It is undefined what happens to the input queue when ICANON is 1439 * switched off, a process should use TCSAFLUSH to flush the queue. 1440 * Keeping the queue to preserve typeahead is the Right Thing, however 1441 * when a process does use TCSANOW to switch to raw mode. 1442 */ 1443 count = tp->tty_eotct = tp->tty_incount; 1444 inp = tp->tty_intail; 1445 while (count > 0) { 1446 *inp |= IN_EOT; 1447 if (++inp == bufend(tp->tty_inbuf)) inp = tp->tty_inbuf; 1448 --count; 1449 } 1450 } 1451 1452 /* Inspect MIN and TIME. */ 1453 settimer(tp, FALSE); 1454 if (tp->tty_termios.c_lflag & ICANON) { 1455 /* No MIN & TIME in canonical mode. */ 1456 tp->tty_min = 1; 1457 } else { 1458 /* In raw mode MIN is the number of chars wanted, and TIME how long 1459 * to wait for them. With interesting exceptions if either is zero. 1460 */ 1461 tp->tty_min = tp->tty_termios.c_cc[VMIN]; 1462 if (tp->tty_min == 0 && tp->tty_termios.c_cc[VTIME] > 0) 1463 tp->tty_min = 1; 1464 } 1465 1466 if (!(tp->tty_termios.c_iflag & IXON)) { 1467 /* No start/stop output control, so don't leave output inhibited. */ 1468 tp->tty_inhibited = RUNNING; 1469 tp->tty_events = 1; 1470 } 1471 1472 /* Setting the output speed to zero hangs up the phone. */ 1473 if (tp->tty_termios.c_ospeed == B0) sigchar(tp, SIGHUP, 1); 1474 1475 /* Set new line speed, character size, etc at the device level. */ 1476 (*tp->tty_ioctl)(tp, 0); 1477 } 1478 1479 /*===========================================================================* 1480 * sigchar * 1481 *===========================================================================*/ 1482 void sigchar(tp, sig, mayflush) 1483 register tty_t *tp; 1484 int sig; /* SIGINT, SIGQUIT, SIGKILL or SIGHUP */ 1485 int mayflush; 1486 { 1487 /* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard or SIGHUP from 1488 * a tty close, "stty 0", or a real RS-232 hangup. PM will send the signal to 1489 * the process group (INT, QUIT), all processes (KILL), or the session leader 1490 * (HUP). 1491 */ 1492 int status; 1493 1494 if (tp->tty_pgrp != 0) { 1495 if (OK != (status = sys_kill(tp->tty_pgrp, sig))) { 1496 panic("Error; call to sys_kill failed: %d", status); 1497 } 1498 } 1499 1500 if (mayflush && !(tp->tty_termios.c_lflag & NOFLSH)) { 1501 tp->tty_incount = tp->tty_eotct = 0; /* kill earlier input */ 1502 tp->tty_intail = tp->tty_inhead; 1503 (*tp->tty_ocancel)(tp, 0); /* kill all output */ 1504 tp->tty_inhibited = RUNNING; 1505 tp->tty_events = 1; 1506 } 1507 } 1508 1509 /*===========================================================================* 1510 * tty_icancel * 1511 *===========================================================================*/ 1512 static void tty_icancel(tp) 1513 register tty_t *tp; 1514 { 1515 /* Discard all pending input, tty buffer or device. */ 1516 1517 tp->tty_incount = tp->tty_eotct = 0; 1518 tp->tty_intail = tp->tty_inhead; 1519 (*tp->tty_icancel)(tp, 0); 1520 } 1521 1522 /*===========================================================================* 1523 * tty_devnop * 1524 *===========================================================================*/ 1525 static int tty_devnop(tty_t *UNUSED(tp), int UNUSED(try)) 1526 { 1527 /* Some functions need not be implemented at the device level. */ 1528 return 0; 1529 } 1530 1531 /*===========================================================================* 1532 * tty_init * 1533 *===========================================================================*/ 1534 static void tty_init() 1535 { 1536 /* Initialize tty structure and call device initialization routines. */ 1537 1538 register tty_t *tp; 1539 int s; 1540 1541 system_hz = sys_hz(); 1542 1543 /* Initialize the terminal lines. */ 1544 memset(tty_table, '\0' , sizeof(tty_table)); 1545 1546 for (tp = FIRST_TTY,s=0; tp < END_TTY; tp++,s++) { 1547 1548 tp->tty_index = s; 1549 init_timer(&tp->tty_tmr); 1550 1551 tp->tty_intail = tp->tty_inhead = tp->tty_inbuf; 1552 tp->tty_min = 1; 1553 tp->tty_incaller = tp->tty_outcaller = tp->tty_iocaller = NONE; 1554 tp->tty_termios = termios_defaults; 1555 tp->tty_icancel = tp->tty_ocancel = tp->tty_ioctl = tp->tty_close = 1556 tp->tty_open = tty_devnop; 1557 if (tp < tty_addr(NR_CONS)) { 1558 scr_init(tp); 1559 1560 /* Initialize the keyboard driver. */ 1561 kb_init(tp); 1562 1563 tp->tty_minor = CONS_MINOR + s; 1564 } else { 1565 rs_init(tp); 1566 tp->tty_minor = RS232_MINOR + s-NR_CONS; 1567 } 1568 } 1569 1570 } 1571 1572 /*===========================================================================* 1573 * tty_timed_out * 1574 *===========================================================================*/ 1575 static void tty_timed_out(minix_timer_t *tp) 1576 { 1577 /* This timer has expired. Set the events flag, to force processing. */ 1578 tty_t *tty_ptr; 1579 tty_ptr = &tty_table[tmr_arg(tp)->ta_int]; 1580 tty_ptr->tty_min = 0; /* force read to succeed */ 1581 tty_ptr->tty_events = 1; 1582 } 1583 1584 /*===========================================================================* 1585 * settimer * 1586 *===========================================================================*/ 1587 static void settimer(tty_ptr, enable) 1588 tty_t *tty_ptr; /* line to set or unset a timer on */ 1589 int enable; /* set timer if true, otherwise unset */ 1590 { 1591 clock_t ticks; 1592 1593 if (enable) { 1594 ticks = tty_ptr->tty_termios.c_cc[VTIME] * (system_hz/10); 1595 1596 /* Set a new timer for enabling the TTY events flags. */ 1597 set_timer(&tty_ptr->tty_tmr, ticks, tty_timed_out, tty_ptr->tty_index); 1598 } else { 1599 /* Remove the timer from the active and expired lists. */ 1600 cancel_timer(&tty_ptr->tty_tmr); 1601 } 1602 } 1603