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: * The valid messages and their parameters are: 20: * 21: * HARD_INT: output has been completed or input has arrived 22: * DEV_READ: a process wants to read from a terminal 23: * DEV_WRITE: a process wants to write on a terminal 24: * DEV_IOCTL: a process wants to change a terminal's parameters 25: * DEV_OPEN: a tty line has been opened 26: * DEV_CLOSE: a tty line has been closed 27: * CANCEL: terminate a previous incomplete system call immediately 28: * 29: * m_type TTY_LINE PROC_NR COUNT TTY_SPEK TTY_FLAGS ADDRESS 30: * --------------------------------------------------------------------------- 31: * | HARD_INT | | | | | | | 32: * |-------------+---------+---------+---------+---------+---------+---------| 33: * | DEV_READ |minor dev| proc nr | count | O_NONBLOCK| buf ptr | 34: * |-------------+---------+---------+---------+---------+---------+---------| 35: * | DEV_WRITE |minor dev| proc nr | count | | | buf ptr | 36: * |-------------+---------+---------+---------+---------+---------+---------| 37: * | DEV_IOCTL |minor dev| proc nr |func code|erase etc| flags | | 38: * |-------------+---------+---------+---------+---------+---------+---------| 39: * | DEV_OPEN |minor dev| proc nr | O_NOCTTY| | | | 40: * |-------------+---------+---------+---------+---------+---------+---------| 41: * | DEV_CLOSE |minor dev| proc nr | | | | | 42: * |-------------+---------+---------+---------+---------+---------+---------| 43: * | CANCEL |minor dev| proc nr | | | | | 44: * --------------------------------------------------------------------------- 45: */ 46: 47: #include "kernel.h" 48: #include <termios.h> 49: #if ENABLE_SRCCOMPAT || ENABLE_BINCOMPAT 50: #include <sgtty.h> 51: #endif 52: #include <sys/ioc_tty.h> 53: #include <signal.h> 54: #include <minix/callnr.h> 55: #include <minix/com.h> 56: #if (CHIP == INTEL) 57: #include <minix/keymap.h> 58: #endif 59: #include "tty.h" 60: #include "proc.h" 61: 62: /* Address of a tty structure. */ 63: #define tty_addr(line) (&tty_table[line]) 64: 65: /* First minor numbers for the various classes of TTY devices. */ 66: #define CONS_MINOR 0 67: #define LOG_MINOR 15 68: #define RS232_MINOR 16 69: #define TTYPX_MINOR 128 70: #define PTYPX_MINOR 192 71: 72: /* Macros for magic tty types. */ 73: #define isconsole(tp) ((tp) < tty_addr(NR_CONS)) 74: 75: /* Macros for magic tty structure pointers. */ 76: #define FIRST_TTY tty_addr(0) 77: #define END_TTY tty_addr(sizeof(tty_table) / sizeof(tty_table[0])) 78: 79: /* A device exists if at least its 'devread' function is defined. */ 80: #define tty_active(tp) ((tp)->tty_devread != NULL) 81: 82: /* RS232 lines or pseudo terminals can be completely configured out. */ 83: #if NR_RS_LINES == 0 84: #define rs_init(tp) ((void) 0) 85: #endif 86: #if NR_PTYS == 0 87: #define pty_init(tp) ((void) 0) 88: #define do_pty(tp, mp) ((void) 0) 89: #endif 90: 91: FORWARD _PROTOTYPE( void do_cancel, (tty_t *tp, message *m_ptr) ); 92: FORWARD _PROTOTYPE( void do_ioctl, (tty_t *tp, message *m_ptr) ); 93: FORWARD _PROTOTYPE( void do_open, (tty_t *tp, message *m_ptr) ); 94: FORWARD _PROTOTYPE( void do_close, (tty_t *tp, message *m_ptr) ); 95: FORWARD _PROTOTYPE( void do_read, (tty_t *tp, message *m_ptr) ); 96: FORWARD _PROTOTYPE( void do_write, (tty_t *tp, message *m_ptr) ); 97: FORWARD _PROTOTYPE( void in_transfer, (tty_t *tp) ); 98: FORWARD _PROTOTYPE( int echo, (tty_t *tp, int ch) ); 99: FORWARD _PROTOTYPE( void rawecho, (tty_t *tp, int ch) ); 100: FORWARD _PROTOTYPE( int back_over, (tty_t *tp) ); 101: FORWARD _PROTOTYPE( void reprint, (tty_t *tp) ); 102: FORWARD _PROTOTYPE( void dev_ioctl, (tty_t *tp) ); 103: FORWARD _PROTOTYPE( void setattr, (tty_t *tp) ); 104: FORWARD _PROTOTYPE( void tty_icancel, (tty_t *tp) ); 105: FORWARD _PROTOTYPE( void tty_init, (tty_t *tp) ); 106: FORWARD _PROTOTYPE( void settimer, (tty_t *tp, int on) ); 107: #if ENABLE_SRCCOMPAT || ENABLE_BINCOMPAT 108: FORWARD _PROTOTYPE( int compat_getp, (tty_t *tp, struct sgttyb *sg) ); 109: FORWARD _PROTOTYPE( int compat_getc, (tty_t *tp, struct tchars *sg) ); 110: FORWARD _PROTOTYPE( int compat_setp, (tty_t *tp, struct sgttyb *sg) ); 111: FORWARD _PROTOTYPE( int compat_setc, (tty_t *tp, struct tchars *sg) ); 112: FORWARD _PROTOTYPE( int tspd2sgspd, (speed_t tspd) ); 113: FORWARD _PROTOTYPE( speed_t sgspd2tspd, (int sgspd) ); 114: #if ENABLE_BINCOMPAT 115: FORWARD _PROTOTYPE( void do_ioctl_compat, (tty_t *tp, message *m_ptr) ); 116: #endif 117: #endif 118: 119: /* Default attributes. */ 120: PRIVATE struct termios termios_defaults = { 121: TINPUT_DEF, TOUTPUT_DEF, TCTRL_DEF, TLOCAL_DEF, TSPEED_DEF, TSPEED_DEF, 122: { 123: TEOF_DEF, TEOL_DEF, TERASE_DEF, TINTR_DEF, TKILL_DEF, TMIN_DEF, 124: TQUIT_DEF, TTIME_DEF, TSUSP_DEF, TSTART_DEF, TSTOP_DEF, 125: TREPRINT_DEF, TLNEXT_DEF, TDISCARD_DEF, 126: }, 127: }; 128: PRIVATE struct winsize winsize_defaults; /* = all zeroes */ 129: 130: 131: /*===========================================================================* 132: * tty_task * 133: *===========================================================================*/ 134: PUBLIC void tty_task() 135: { 136: /* Main routine of the terminal task. */ 137: 138: message tty_mess; /* buffer for all incoming messages */ 139: register tty_t *tp; 140: unsigned line; 141: 142: /* Initialize the terminal lines. */ 143: for (tp = FIRST_TTY; tp < END_TTY; tp++) tty_init(tp); 144: 145: /* Display the Minix startup banner. */ 146: printf("Minix %s.%s Copyright 2001 Prentice-Hall, Inc.\n\n", 147: OS_RELEASE, OS_VERSION); 148: 149: #if (CHIP == INTEL) 150: /* Real mode, or 16/32-bit protected mode? */ 151: #if _WORD_SIZE == 4 152: printf("Executing in 32-bit protected mode\n\n"); 153: #else 154: printf("Executing in %s mode\n\n", 155: protected_mode ? "16-bit protected" : "real"); 156: #endif 157: #endif 158: 159: while (TRUE) { 160: /* Check if a timer expired. */ 161: if (cproc_addr(TTY)->p_exptimers != NULL) tmr_exptimers(); 162: 163: /* Handle any events on any of the ttys. */ 164: for (tp = FIRST_TTY; tp < END_TTY; tp++) { 165: if (tp->tty_events) handle_events(tp); 166: } 167: 168: receive(ANY, &tty_mess); 169: 170: /* A hardware interrupt is an invitation to check for events. */ 171: if (tty_mess.m_type == HARD_INT) continue; 172: 173: /* Check the minor device number. */ 174: line = tty_mess.TTY_LINE; 175: if ((line - CONS_MINOR) < NR_CONS) { 176: tp = tty_addr(line - CONS_MINOR); 177: } else 178: if (line == LOG_MINOR) { 179: tp = tty_addr(0); 180: } else 181: if ((line - RS232_MINOR) < NR_RS_LINES) { 182: tp = tty_addr(line - RS232_MINOR + NR_CONS); 183: } else 184: if ((line - TTYPX_MINOR) < NR_PTYS) { 185: tp = tty_addr(line - TTYPX_MINOR + NR_CONS + NR_RS_LINES); 186: } else 187: if ((line - PTYPX_MINOR) < NR_PTYS) { 188: tp = tty_addr(line - PTYPX_MINOR + NR_CONS + NR_RS_LINES); 189: if (tty_mess.m_type != DEV_IOCTL) { 190: do_pty(tp, &tty_mess); 191: continue; 192: } 193: } else { 194: tp = NULL; 195: } 196: 197: /* If the device doesn't exist or is not configured return ENXIO. */ 198: if (tp == NULL || !tty_active(tp)) { 199: tty_reply(TASK_REPLY, tty_mess.m_source, 200: tty_mess.PROC_NR, ENXIO); 201: continue; 202: } 203: 204: /* Execute the requested function. */ 205: switch (tty_mess.m_type) { 206: case DEV_READ: do_read(tp, &tty_mess); break; 207: case DEV_WRITE: do_write(tp, &tty_mess); break; 208: case DEV_IOCTL: do_ioctl(tp, &tty_mess); break; 209: case DEV_OPEN: do_open(tp, &tty_mess); break; 210: case DEV_CLOSE: do_close(tp, &tty_mess); break; 211: case CANCEL: do_cancel(tp, &tty_mess); break; 212: default: tty_reply(TASK_REPLY, tty_mess.m_source, 213: tty_mess.PROC_NR, EINVAL); 214: } 215: } 216: } 217: 218: 219: /*===========================================================================* 220: * do_read * 221: *===========================================================================*/ 222: PRIVATE void do_read(tp, m_ptr) 223: register tty_t *tp; /* pointer to tty struct */ 224: message *m_ptr; /* pointer to message sent to the task */ 225: { 226: /* A process wants to read from a terminal. */ 227: int r; 228: 229: /* Check if there is already a process hanging in a read, check if the 230: * parameters are correct, do I/O. 231: */ 232: if (tp->tty_inleft > 0) { 233: r = EIO; 234: } else 235: if (m_ptr->COUNT <= 0) { 236: r = EINVAL; 237: } else 238: if (numap(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, m_ptr->COUNT) == 0) { 239: r = EFAULT; 240: } else { 241: /* Copy information from the message to the tty struct. */ 242: tp->tty_inrepcode = TASK_REPLY; 243: tp->tty_incaller = m_ptr->m_source; 244: tp->tty_inproc = m_ptr->PROC_NR; 245: tp->tty_in_vir = (vir_bytes) m_ptr->ADDRESS; 246: tp->tty_inleft = m_ptr->COUNT; 247: 248: if (!(tp->tty_termios.c_lflag & ICANON) 249: && tp->tty_termios.c_cc[VTIME] > 0) { 250: if (tp->tty_termios.c_cc[VMIN] == 0) { 251: /* MIN & TIME specify a read timer that finishes the 252: * read in TIME/10 seconds if no bytes are available. 253: */ 254: lock(); 255: settimer(tp, TRUE); 256: tp->tty_min = 1; 257: unlock(); 258: } else { 259: /* MIN & TIME specify an inter-byte timer that may 260: * have to be cancelled if there are no bytes yet. 261: */ 262: if (tp->tty_eotct == 0) { 263: lock(); 264: settimer(tp, FALSE); 265: unlock(); 266: tp->tty_min = tp->tty_termios.c_cc[VMIN]; 267: } 268: } 269: } 270: 271: /* Anything waiting in the input buffer? Clear it out... */ 272: in_transfer(tp); 273: /* ...then go back for more */ 274: handle_events(tp); 275: if (tp->tty_inleft == 0) return; /* already done */ 276: 277: /* There were no bytes in the input queue available, so either suspend 278: * the caller or break off the read if nonblocking. 279: */ 280: if (m_ptr->TTY_FLAGS & O_NONBLOCK) { 281: r = EAGAIN; /* cancel the read */ 282: tp->tty_inleft = tp->tty_incum = 0; 283: } else { 284: r = SUSPEND; /* suspend the caller */ 285: tp->tty_inrepcode = REVIVE; 286: } 287: } 288: tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r); 289: } 290: 291: 292: /*===========================================================================* 293: * do_write * 294: *===========================================================================*/ 295: PRIVATE void do_write(tp, m_ptr) 296: register tty_t *tp; 297: register message *m_ptr; /* pointer to message sent to the task */ 298: { 299: /* A process wants to write on a terminal. */ 300: int r; 301: 302: /* Check if there is already a process hanging in a write, check if the 303: * parameters are correct, do I/O. 304: */ 305: if (tp->tty_outleft > 0) { 306: r = EIO; 307: } else 308: if (m_ptr->COUNT <= 0) { 309: r = EINVAL; 310: } else 311: if (numap(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, m_ptr->COUNT) == 0) { 312: r = EFAULT; 313: } else { 314: /* Copy message parameters to the tty structure. */ 315: tp->tty_outrepcode = TASK_REPLY; 316: tp->tty_outcaller = m_ptr->m_source; 317: tp->tty_outproc = m_ptr->PROC_NR; 318: tp->tty_out_vir = (vir_bytes) m_ptr->ADDRESS; 319: tp->tty_outleft = m_ptr->COUNT; 320: 321: /* Try to write. */ 322: handle_events(tp); 323: if (tp->tty_outleft == 0) return; /* already done */ 324: 325: /* None or not all the bytes could be written, so either suspend the 326: * caller or break off the write if nonblocking. 327: */ 328: if (m_ptr->TTY_FLAGS & O_NONBLOCK) { /* cancel the write */ 329: r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN; 330: tp->tty_outleft = tp->tty_outcum = 0; 331: } else { 332: r = SUSPEND; /* suspend the caller */ 333: tp->tty_outrepcode = REVIVE; 334: } 335: } 336: tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r); 337: } 338: 339: 340: /*===========================================================================* 341: * do_ioctl * 342: *===========================================================================*/ 343: PRIVATE void do_ioctl(tp, m_ptr) 344: register tty_t *tp; 345: message *m_ptr; /* pointer to message sent to task */ 346: { 347: /* Perform an IOCTL on this terminal. Posix termios calls are handled 348: * by the IOCTL system call 349: */ 350: 351: int r; 352: union { 353: int i; 354: #if ENABLE_SRCCOMPAT 355: struct sgttyb sg; 356: struct tchars tc; 357: #endif 358: } param; 359: phys_bytes user_phys; 360: size_t size; 361: 362: /* Size of the ioctl parameter. */ 363: switch (m_ptr->TTY_REQUEST) { 364: case TCGETS: /* Posix tcgetattr function */ 365: case TCSETS: /* Posix tcsetattr function, TCSANOW option */ 366: case TCSETSW: /* Posix tcsetattr function, TCSADRAIN option */ 367: case TCSETSF: /* Posix tcsetattr function, TCSAFLUSH option */ 368: size = sizeof(struct termios); 369: break; 370: 371: case TCSBRK: /* Posix tcsendbreak function */ 372: case TCFLOW: /* Posix tcflow function */ 373: case TCFLSH: /* Posix tcflush function */ 374: case TIOCGPGRP: /* Posix tcgetpgrp function */ 375: case TIOCSPGRP: /* Posix tcsetpgrp function */ 376: size = sizeof(int); 377: break; 378: 379: case TIOCGWINSZ: /* get window size (not Posix) */ 380: case TIOCSWINSZ: /* set window size (not Posix) */ 381: size = sizeof(struct winsize); 382: break; 383: 384: #if ENABLE_SRCCOMPAT 385: case TIOCGETP: /* BSD-style get terminal properties */ 386: case TIOCSETP: /* BSD-style set terminal properties */ 387: size = sizeof(struct sgttyb); 388: break; 389: 390: case TIOCGETC: /* BSD-style get terminal special characters */ 391: case TIOCSETC: /* BSD-style get terminal special characters */ 392: size = sizeof(struct tchars); 393: break; 394: #endif 395: #if (MACHINE == IBM_PC) 396: case KIOCSMAP: /* load keymap (Minix extension) */ 397: size = sizeof(keymap_t); 398: break; 399: 400: case TIOCSFON: /* load font (Minix extension) */ 401: size = sizeof(u8_t [8192]); 402: break; 403: 404: #endif 405: case TCDRAIN: /* Posix tcdrain function -- no parameter */ 406: default: size = 0; 407: } 408: 409: if (size != 0) { 410: user_phys = numap(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, size); 411: if (user_phys == 0) { 412: tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EFAULT); 413: return; 414: } 415: } 416: 417: r = OK; 418: switch (m_ptr->TTY_REQUEST) { 419: case TCGETS: 420: /* Get the termios attributes. */ 421: phys_copy(vir2phys(&tp->tty_termios), user_phys, (phys_bytes) size); 422: break; 423: 424: case TCSETSW: 425: case TCSETSF: 426: case TCDRAIN: 427: if (tp->tty_outleft > 0) { 428: /* Wait for all ongoing output processing to finish. */ 429: tp->tty_iocaller = m_ptr->m_source; 430: tp->tty_ioproc = m_ptr->PROC_NR; 431: tp->tty_ioreq = m_ptr->REQUEST; 432: tp->tty_iovir = (vir_bytes) m_ptr->ADDRESS; 433: r = SUSPEND; 434: break; 435: } 436: if (m_ptr->TTY_REQUEST == TCDRAIN) break; 437: if (m_ptr->TTY_REQUEST == TCSETSF) tty_icancel(tp); 438: /*FALL THROUGH*/ 439: case TCSETS: 440: /* Set the termios attributes. */ 441: phys_copy(user_phys, vir2phys(&tp->tty_termios), (phys_bytes) size); 442: setattr(tp); 443: break; 444: 445: case TCFLSH: 446: phys_copy(user_phys, vir2phys(¶m.i), (phys_bytes) size); 447: switch (param.i) { 448: case TCIFLUSH: tty_icancel(tp); break; 449: case TCOFLUSH: (*tp->tty_ocancel)(tp); break; 450: case TCIOFLUSH: tty_icancel(tp); (*tp->tty_ocancel)(tp);break; 451: default: r = EINVAL; 452: } 453: break; 454: 455: case TCFLOW: 456: phys_copy(user_phys, vir2phys(¶m.i), (phys_bytes) size); 457: switch (param.i) { 458: case TCOOFF: 459: case TCOON: 460: tp->tty_inhibited = (param.i == TCOOFF); 461: tp->tty_events = 1; 462: break; 463: case TCIOFF: 464: (*tp->tty_echo)(tp, tp->tty_termios.c_cc[VSTOP]); 465: break; 466: case TCION: 467: (*tp->tty_echo)(tp, tp->tty_termios.c_cc[VSTART]); 468: break; 469: default: 470: r = EINVAL; 471: } 472: break; 473: 474: case TCSBRK: 475: if (tp->tty_break != NULL) (*tp->tty_break)(tp); 476: break; 477: 478: case TIOCGWINSZ: 479: phys_copy(vir2phys(&tp->tty_winsize), user_phys, (phys_bytes) size); 480: break; 481: 482: case TIOCSWINSZ: 483: phys_copy(user_phys, vir2phys(&tp->tty_winsize), (phys_bytes) size); 484: /* SIGWINCH... */ 485: break; 486: 487: #if ENABLE_SRCCOMPAT 488: case TIOCGETP: 489: compat_getp(tp, ¶m.sg); 490: phys_copy(vir2phys(¶m.sg), user_phys, (phys_bytes) size); 491: break; 492: 493: case TIOCSETP: 494: phys_copy(user_phys, vir2phys(¶m.sg), (phys_bytes) size); 495: compat_setp(tp, ¶m.sg); 496: break; 497: 498: case TIOCGETC: 499: compat_getc(tp, ¶m.tc); 500: phys_copy(vir2phys(¶m.tc), user_phys, (phys_bytes) size); 501: break; 502: 503: case TIOCSETC: 504: phys_copy(user_phys, vir2phys(¶m.tc), (phys_bytes) size); 505: compat_setc(tp, ¶m.tc); 506: break; 507: #endif 508: 509: #if (MACHINE == IBM_PC) 510: case KIOCSMAP: 511: /* Load a new keymap (only /dev/console). */ 512: if (isconsole(tp)) r = kbd_loadmap(user_phys); 513: break; 514: 515: case TIOCSFON: 516: /* Load a font into an EGA or VGA card (hs@hck.hr) */ 517: if (isconsole(tp)) r = con_loadfont(user_phys); 518: break; 519: #endif 520: 521: #if (MACHINE == ATARI) 522: case VDU_LOADFONT: 523: r = vdu_loadfont(m_ptr); 524: break; 525: #endif 526: 527: /* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is 528: * not defined. 529: */ 530: case TIOCGPGRP: 531: case TIOCSPGRP: 532: default: 533: #if ENABLE_BINCOMPAT 534: do_ioctl_compat(tp, m_ptr); 535: return; 536: #else 537: r = ENOTTY; 538: #endif 539: } 540: 541: /* Send the reply. */ 542: tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r); 543: } 544: 545: 546: /*===========================================================================* 547: * do_open * 548: *===========================================================================*/ 549: PRIVATE void do_open(tp, m_ptr) 550: register tty_t *tp; 551: message *m_ptr; /* pointer to message sent to task */ 552: { 553: /* A tty line has been opened. Make it the callers controlling tty if 554: * O_NOCTTY is *not* set and it is not the log device. 1 is returned if 555: * the tty is made the controlling tty, otherwise OK or an error code. 556: */ 557: int r = OK; 558: 559: if (m_ptr->TTY_LINE == LOG_MINOR) { 560: /* The log device is a write-only diagnostics device. */ 561: if (m_ptr->COUNT & R_BIT) r = EACCES; 562: } else { 563: if (!(m_ptr->COUNT & O_NOCTTY)) { 564: tp->tty_pgrp = m_ptr->PROC_NR; 565: r = 1; 566: } 567: tp->tty_openct++; 568: } 569: tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r); 570: } 571: 572: 573: /*===========================================================================* 574: * do_close * 575: *===========================================================================*/ 576: PRIVATE void do_close(tp, m_ptr) 577: register tty_t *tp; 578: message *m_ptr; /* pointer to message sent to task */ 579: { 580: /* A tty line has been closed. Clean up the line if it is the last close. */ 581: 582: if (m_ptr->TTY_LINE != LOG_MINOR && --tp->tty_openct == 0) { 583: tp->tty_pgrp = 0; 584: tty_icancel(tp); 585: (*tp->tty_ocancel)(tp); 586: (*tp->tty_close)(tp); 587: tp->tty_termios = termios_defaults; 588: tp->tty_winsize = winsize_defaults; 589: setattr(tp); 590: } 591: tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, OK); 592: } 593: 594: 595: /*===========================================================================* 596: * do_cancel * 597: *===========================================================================*/ 598: PRIVATE void do_cancel(tp, m_ptr) 599: register tty_t *tp; 600: message *m_ptr; /* pointer to message sent to task */ 601: { 602: /* A signal has been sent to a process that is hanging trying to read or write. 603: * The pending read or write must be finished off immediately. 604: */ 605: 606: int proc_nr; 607: int mode; 608: 609: /* Check the parameters carefully, to avoid cancelling twice. */ 610: proc_nr = m_ptr->PROC_NR; 611: mode = m_ptr->COUNT; 612: if ((mode & R_BIT) && tp->tty_inleft != 0 && proc_nr == tp->tty_inproc) { 613: /* Process was reading when killed. Clean up input. */ 614: tty_icancel(tp); 615: tp->tty_inleft = tp->tty_incum = 0; 616: } 617: if ((mode & W_BIT) && tp->tty_outleft != 0 && proc_nr == tp->tty_outproc) { 618: /* Process was writing when killed. Clean up output. */ 619: (*tp->tty_ocancel)(tp); 620: tp->tty_outleft = tp->tty_outcum = 0; 621: } 622: if (tp->tty_ioreq != 0 && proc_nr == tp->tty_ioproc) { 623: /* Process was waiting for output to drain. */ 624: tp->tty_ioreq = 0; 625: } 626: tp->tty_events = 1; 627: tty_reply(TASK_REPLY, m_ptr->m_source, proc_nr, EINTR); 628: } 629: 630: 631: /*===========================================================================* 632: * handle_events * 633: *===========================================================================*/ 634: PUBLIC void handle_events(tp) 635: tty_t *tp; /* TTY to check for events. */ 636: { 637: /* Handle any events pending on a TTY. These events are usually device 638: * interrupts. 639: * 640: * Two kinds of events are prominent: 641: * - a character has been received from the console or an RS232 line. 642: * - an RS232 line has completed a write request (on behalf of a user). 643: * The interrupt handler may delay the interrupt message at its discretion 644: * to avoid swamping the TTY task. Messages may be overwritten when the 645: * lines are fast or when there are races between different lines, input 646: * and output, because MINIX only provides single buffering for interrupt 647: * messages (in proc.c). This is handled by explicitly checking each line 648: * for fresh input and completed output on each interrupt. 649: */ 650: char *buf; 651: unsigned count; 652: 653: do { 654: tp->tty_events = 0; 655: 656: /* Read input and perform input processing. */ 657: (*tp->tty_devread)(tp); 658: 659: /* Perform output processing and write output. */ 660: (*tp->tty_devwrite)(tp); 661: 662: /* Ioctl waiting for some event? */ 663: if (tp->tty_ioreq != 0) dev_ioctl(tp); 664: } while (tp->tty_events); 665: 666: /* Transfer characters from the input queue to a waiting process. */ 667: in_transfer(tp); 668: 669: /* Reply if enough bytes are available. */ 670: if (tp->tty_incum >= tp->tty_min && tp->tty_inleft > 0) { 671: tty_reply(tp->tty_inrepcode, tp->tty_incaller, tp->tty_inproc, 672: tp->tty_incum); 673: tp->tty_inleft = tp->tty_incum = 0; 674: } 675: } 676: 677: 678: /*===========================================================================* 679: * in_transfer * 680: *===========================================================================*/ 681: PRIVATE void in_transfer(tp) 682: register tty_t *tp; /* pointer to terminal to read from */ 683: { 684: /* Transfer bytes from the input queue to a process reading from a terminal. */ 685: 686: int ch; 687: int count; 688: phys_bytes buf_phys, user_base; 689: char buf[64], *bp; 690: 691: /* Force read to succeed if the line is hung up, looks like EOF to reader. */ 692: if (tp->tty_termios.c_ospeed == B0) tp->tty_min = 0; 693: 694: /* Anything to do? */ 695: if (tp->tty_inleft == 0 || tp->tty_eotct < tp->tty_min) return; 696: 697: buf_phys = vir2phys(buf); 698: user_base = proc_vir2phys(proc_addr(tp->tty_inproc), 0); 699: bp = buf; 700: while (tp->tty_inleft > 0 && tp->tty_eotct > 0) { 701: ch = *tp->tty_intail; 702: 703: if (!(ch & IN_EOF)) { 704: /* One character to be delivered to the user. */ 705: *bp = ch & IN_CHAR; 706: tp->tty_inleft--; 707: if (++bp == bufend(buf)) { 708: /* Temp buffer full, copy to user space. */ 709: phys_copy(buf_phys, user_base + tp->tty_in_vir, 710: (phys_bytes) buflen(buf)); 711: tp->tty_in_vir += buflen(buf); 712: tp->tty_incum += buflen(buf); 713: bp = buf; 714: } 715: } 716: 717: /* Remove the character from the input queue. */ 718: if (++tp->tty_intail == bufend(tp->tty_inbuf)) 719: tp->tty_intail = tp->tty_inbuf; 720: tp->tty_incount--; 721: if (ch & IN_EOT) { 722: tp->tty_eotct--; 723: /* Don't read past a line break in canonical mode. */ 724: if (tp->tty_termios.c_lflag & ICANON) tp->tty_inleft = 0; 725: } 726: } 727: 728: if (bp > buf) { 729: /* Leftover characters in the buffer. */ 730: count = bp - buf; 731: phys_copy(buf_phys, user_base + tp->tty_in_vir, (phys_bytes) count); 732: tp->tty_in_vir += count; 733: tp->tty_incum += count; 734: } 735: 736: /* Usually reply to the reader, possibly even if incum == 0 (EOF). */ 737: if (tp->tty_inleft == 0) { 738: tty_reply(tp->tty_inrepcode, tp->tty_incaller, tp->tty_inproc, 739: tp->tty_incum); 740: tp->tty_inleft = tp->tty_incum = 0; 741: } 742: } 743: 744: 745: /*===========================================================================* 746: * in_process * 747: *===========================================================================*/ 748: PUBLIC int in_process(tp, buf, count) 749: register tty_t *tp; /* terminal on which character has arrived */ 750: char *buf; /* buffer with input characters */ 751: int count; /* number of input characters */ 752: { 753: /* Characters have just been typed in. Process, save, and echo them. Return 754: * the number of characters processed. 755: */ 756: 757: int ch, sig, ct; 758: int timeset = FALSE; 759: static unsigned char csize_mask[] = { 0x1F, 0x3F, 0x7F, 0xFF }; 760: 761: for (ct = 0; ct < count; ct++) { 762: /* Take one character. */ 763: ch = *buf++ & BYTE; 764: 765: /* Strip to seven bits? */ 766: if (tp->tty_termios.c_iflag & ISTRIP) ch &= 0x7F; 767: 768: /* Input extensions? */ 769: if (tp->tty_termios.c_lflag & IEXTEN) { 770: 771: /* Previous character was a character escape? */ 772: if (tp->tty_escaped) { 773: tp->tty_escaped = NOT_ESCAPED; 774: ch |= IN_ESC; /* protect character */ 775: } 776: 777: /* LNEXT (^V) to escape the next character? */ 778: if (ch == tp->tty_termios.c_cc[VLNEXT]) { 779: tp->tty_escaped = ESCAPED; 780: rawecho(tp, '^'); 781: rawecho(tp, '\b'); 782: continue; /* do not store the escape */ 783: } 784: 785: /* REPRINT (^R) to reprint echoed characters? */ 786: if (ch == tp->tty_termios.c_cc[VREPRINT]) { 787: reprint(tp); 788: continue; 789: } 790: } 791: 792: /* _POSIX_VDISABLE is a normal character value, so better escape it. */ 793: if (ch == _POSIX_VDISABLE) ch |= IN_ESC; 794: 795: /* Map CR to LF, ignore CR, or map LF to CR. */ 796: if (ch == '\r') { 797: if (tp->tty_termios.c_iflag & IGNCR) continue; 798: if (tp->tty_termios.c_iflag & ICRNL) ch = '\n'; 799: } else 800: if (ch == '\n') { 801: if (tp->tty_termios.c_iflag & INLCR) ch = '\r'; 802: } 803: 804: /* Canonical mode? */ 805: if (tp->tty_termios.c_lflag & ICANON) { 806: 807: /* Erase processing (rub out of last character). */ 808: if (ch == tp->tty_termios.c_cc[VERASE]) { 809: (void) back_over(tp); 810: if (!(tp->tty_termios.c_lflag & ECHOE)) { 811: (void) echo(tp, ch); 812: } 813: continue; 814: } 815: 816: /* Kill processing (remove current line). */ 817: if (ch == tp->tty_termios.c_cc[VKILL]) { 818: while (back_over(tp)) {} 819: if (!(tp->tty_termios.c_lflag & ECHOE)) { 820: (void) echo(tp, ch); 821: if (tp->tty_termios.c_lflag & ECHOK) 822: rawecho(tp, '\n'); 823: } 824: continue; 825: } 826: 827: /* EOF (^D) means end-of-file, an invisible "line break". */ 828: if (ch == tp->tty_termios.c_cc[VEOF]) ch |= IN_EOT | IN_EOF; 829: 830: /* The line may be returned to the user after an LF. */ 831: if (ch == '\n') ch |= IN_EOT; 832: 833: /* Same thing with EOL, whatever it may be. */ 834: if (ch == tp->tty_termios.c_cc[VEOL]) ch |= IN_EOT; 835: } 836: 837: /* Start/stop input control? */ 838: if (tp->tty_termios.c_iflag & IXON) { 839: 840: /* Output stops on STOP (^S). */ 841: if (ch == tp->tty_termios.c_cc[VSTOP]) { 842: tp->tty_inhibited = STOPPED; 843: tp->tty_events = 1; 844: continue; 845: } 846: 847: /* Output restarts on START (^Q) or any character if IXANY. */ 848: if (tp->tty_inhibited) { 849: if (ch == tp->tty_termios.c_cc[VSTART] 850: || (tp->tty_termios.c_iflag & IXANY)) { 851: tp->tty_inhibited = RUNNING; 852: tp->tty_events = 1; 853: if (ch == tp->tty_termios.c_cc[VSTART]) 854: continue; 855: } 856: } 857: } 858: 859: if (tp->tty_termios.c_lflag & ISIG) { 860: /* Check for INTR (^?) and QUIT (^\) characters. */ 861: if (ch == tp->tty_termios.c_cc[VINTR] 862: || ch == tp->tty_termios.c_cc[VQUIT]) { 863: sig = SIGINT; 864: if (ch == tp->tty_termios.c_cc[VQUIT]) sig = SIGQUIT; 865: sigchar(tp, sig); 866: (void) echo(tp, ch); 867: continue; 868: } 869: } 870: 871: /* Is there space in the input buffer? */ 872: if (tp->tty_incount == buflen(tp->tty_inbuf)) { 873: /* No space; discard in canonical mode, keep in raw mode. */ 874: if (tp->tty_termios.c_lflag & ICANON) continue; 875: break; 876: } 877: 878: if (!(tp->tty_termios.c_lflag & ICANON)) { 879: /* In raw mode all characters are "line breaks". */ 880: ch |= IN_EOT; 881: 882: /* Start an inter-byte timer? */ 883: if (!timeset && tp->tty_termios.c_cc[VMIN] > 0 884: && tp->tty_termios.c_cc[VTIME] > 0) { 885: lock(); 886: settimer(tp, TRUE); 887: unlock(); 888: timeset = TRUE; 889: } 890: } 891: 892: /* Perform the intricate function of echoing. */ 893: if (tp->tty_termios.c_lflag & (ECHO|ECHONL)) ch = echo(tp, ch); 894: 895: /* Save the character in the input queue. */ 896: *tp->tty_inhead++ = ch; 897: if (tp->tty_inhead == bufend(tp->tty_inbuf)) 898: tp->tty_inhead = tp->tty_inbuf; 899: tp->tty_incount++; 900: if (ch & IN_EOT) tp->tty_eotct++; 901: 902: /* Try to finish input if the queue threatens to overflow. */ 903: if (tp->tty_incount == buflen(tp->tty_inbuf)) in_transfer(tp); 904: } 905: return ct; 906: } 907: 908: 909: /*===========================================================================* 910: * echo * 911: *===========================================================================*/ 912: PRIVATE int echo(tp, ch) 913: register tty_t *tp; /* terminal on which to echo */ 914: register int ch; /* pointer to character to echo */ 915: { 916: /* Echo the character if echoing is on. Some control characters are echoed 917: * with their normal effect, other control characters are echoed as "^X", 918: * normal characters are echoed normally. EOF (^D) is echoed, but immediately 919: * backspaced over. Return the character with the echoed length added to its 920: * attributes. 921: */ 922: int len, rp; 923: 924: ch &= ~IN_LEN; 925: if (!(tp->tty_termios.c_lflag & ECHO)) { 926: if (ch == ('\n' | IN_EOT) && (tp->tty_termios.c_lflag 927: & (ICANON|ECHONL)) == (ICANON|ECHONL)) 928: (*tp->tty_echo)(tp, '\n'); 929: return(ch); 930: } 931: 932: /* "Reprint" tells if the echo output has been messed up by other output. */ 933: rp = tp->tty_incount == 0 ? FALSE : tp->tty_reprint; 934: 935: if ((ch & IN_CHAR) < ' ') { 936: switch (ch & (IN_ESC|IN_EOF|IN_EOT|IN_CHAR)) { 937: case '\t': 938: len = 0; 939: do { 940: (*tp->tty_echo)(tp, ' '); 941: len++; 942: } while (len < TAB_SIZE && (tp->tty_position & TAB_MASK) != 0); 943: break; 944: case '\r' | IN_EOT: 945: case '\n' | IN_EOT: 946: (*tp->tty_echo)(tp, ch & IN_CHAR); 947: len = 0; 948: break; 949: default: 950: (*tp->tty_echo)(tp, '^'); 951: (*tp->tty_echo)(tp, '@' + (ch & IN_CHAR)); 952: len = 2; 953: } 954: } else 955: if ((ch & IN_CHAR) == '\177') { 956: /* A DEL prints as "^?". */ 957: (*tp->tty_echo)(tp, '^'); 958: (*tp->tty_echo)(tp, '?'); 959: len = 2; 960: } else { 961: (*tp->tty_echo)(tp, ch & IN_CHAR); 962: len = 1; 963: } 964: if (ch & IN_EOF) while (len > 0) { (*tp->tty_echo)(tp, '\b'); len--; } 965: 966: tp->tty_reprint = rp; 967: return(ch | (len << IN_LSHIFT)); 968: } 969: 970: 971: /*==========================================================================* 972: * rawecho * 973: *==========================================================================*/ 974: PRIVATE void rawecho(tp, ch) 975: register tty_t *tp; 976: int ch; 977: { 978: /* Echo without interpretation if ECHO is set. */ 979: int rp = tp->tty_reprint; 980: if (tp->tty_termios.c_lflag & ECHO) (*tp->tty_echo)(tp, ch); 981: tp->tty_reprint = rp; 982: } 983: 984: 985: /*==========================================================================* 986: * back_over * 987: *==========================================================================*/ 988: PRIVATE int back_over(tp) 989: register tty_t *tp; 990: { 991: /* Backspace to previous character on screen and erase it. */ 992: u16_t *head; 993: int len; 994: 995: if (tp->tty_incount == 0) return(0); /* queue empty */ 996: head = tp->tty_inhead; 997: if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf); 998: if (*--head & IN_EOT) return(0); /* can't erase "line breaks" */ 999: if (tp->tty_reprint) reprint(tp); /* reprint if messed up */ 1000: tp->tty_inhead = head; 1001: tp->tty_incount--; 1002: if (tp->tty_termios.c_lflag & ECHOE) { 1003: len = (*head & IN_LEN) >> IN_LSHIFT; 1004: while (len > 0) { 1005: rawecho(tp, '\b'); 1006: rawecho(tp, ' '); 1007: rawecho(tp, '\b'); 1008: len--; 1009: } 1010: } 1011: return(1); /* one character erased */ 1012: } 1013: 1014: 1015: /*==========================================================================* 1016: * reprint * 1017: *==========================================================================*/ 1018: PRIVATE void reprint(tp) 1019: register tty_t *tp; /* pointer to tty struct */ 1020: { 1021: /* Restore what has been echoed to screen before if the user input has been 1022: * messed up by output, or if REPRINT (^R) is typed. 1023: */ 1024: int count; 1025: u16_t *head; 1026: 1027: tp->tty_reprint = FALSE; 1028: 1029: /* Find the last line break in the input. */ 1030: head = tp->tty_inhead; 1031: count = tp->tty_incount; 1032: while (count > 0) { 1033: if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf); 1034: if (head[-1] & IN_EOT) break; 1035: head--; 1036: count--; 1037: } 1038: if (count == tp->tty_incount) return; /* no reason to reprint */ 1039: 1040: /* Show REPRINT (^R) and move to a new line. */ 1041: (void) echo(tp, tp->tty_termios.c_cc[VREPRINT] | IN_ESC); 1042: rawecho(tp, '\r'); 1043: rawecho(tp, '\n'); 1044: 1045: /* Reprint from the last break onwards. */ 1046: do { 1047: if (head == bufend(tp->tty_inbuf)) head = tp->tty_inbuf; 1048: *head = echo(tp, *head); 1049: head++; 1050: count++; 1051: } while (count < tp->tty_incount); 1052: } 1053: 1054: 1055: /*==========================================================================* 1056: * out_process * 1057: *==========================================================================*/ 1058: PUBLIC void out_process(tp, bstart, bpos, bend, icount, ocount) 1059: tty_t *tp; 1060: char *bstart, *bpos, *bend; /* start/pos/end of circular buffer */ 1061: int *icount; /* # input chars / input chars used */ 1062: int *ocount; /* max output chars / output chars used */ 1063: { 1064: /* Perform output processing on a circular buffer. *icount is the number of 1065: * bytes to process, and the number of bytes actually processed on return. 1066: * *ocount is the space available on input and the space used on output. 1067: * (Naturally *icount < *ocount.) The column position is updated modulo 1068: * the TAB size, because we really only need it for tabs. 1069: */ 1070: 1071: int tablen; 1072: int ict = *icount; 1073: int oct = *ocount; 1074: int pos = tp->tty_position; 1075: 1076: while (ict > 0) { 1077: switch (*bpos) { 1078: case '\7': 1079: break; 1080: case '\b': 1081: pos--; 1082: break; 1083: case '\r': 1084: pos = 0; 1085: break; 1086: case '\n': 1087: if ((tp->tty_termios.c_oflag & (OPOST|ONLCR)) 1088: == (OPOST|ONLCR)) { 1089: /* Map LF to CR+LF if there is space. Note that the 1090: * next character in the buffer is overwritten, so 1091: * we stop at this point. 1092: */ 1093: if (oct >= 2) { 1094: *bpos = '\r'; 1095: if (++bpos == bend) bpos = bstart; 1096: *bpos = '\n'; 1097: pos = 0; 1098: ict--; 1099: oct -= 2; 1100: } 1101: goto out_done; /* no space or buffer got changed */ 1102: } 1103: break; 1104: case '\t': 1105: /* Best guess for the tab length. */ 1106: tablen = TAB_SIZE - (pos & TAB_MASK); 1107: 1108: if ((tp->tty_termios.c_oflag & (OPOST|XTABS)) 1109: == (OPOST|XTABS)) { 1110: /* Tabs must be expanded. */ 1111: if (oct >= tablen) { 1112: pos += tablen; 1113: ict--; 1114: oct -= tablen; 1115: do { 1116: *bpos = ' '; 1117: if (++bpos == bend) bpos = bstart; 1118: } while (--tablen != 0); 1119: } 1120: goto out_done; 1121: } 1122: /* Tabs are output directly. */ 1123: pos += tablen; 1124: break; 1125: default: 1126: /* Assume any other character prints as one character. */ 1127: pos++; 1128: } 1129: if (++bpos == bend) bpos = bstart; 1130: ict--; 1131: oct--; 1132: } 1133: out_done: 1134: tp->tty_position = pos & TAB_MASK; 1135: 1136: *icount -= ict; /* [io]ct are the number of chars not used */ 1137: *ocount -= oct; /* *[io]count are the number of chars that are used */ 1138: } 1139: 1140: 1141: /*===========================================================================* 1142: * dev_ioctl * 1143: *===========================================================================*/ 1144: PRIVATE void dev_ioctl(tp) 1145: tty_t *tp; 1146: { 1147: /* The ioctl's TCSETSW, TCSETSF and TCDRAIN wait for output to finish to make 1148: * sure that an attribute change doesn't affect the processing of current 1149: * output. Once output finishes the ioctl is executed as in do_ioctl(). 1150: */ 1151: phys_bytes user_phys; 1152: 1153: if (tp->tty_outleft > 0) return; /* output not finished */ 1154: 1155: if (tp->tty_ioreq != TCDRAIN) { 1156: if (tp->tty_ioreq == TCSETSF) tty_icancel(tp); 1157: user_phys = proc_vir2phys(proc_addr(tp->tty_ioproc), tp->tty_iovir); 1158: phys_copy(user_phys, vir2phys(&tp->tty_termios), 1159: (phys_bytes) sizeof(tp->tty_termios)); 1160: setattr(tp); 1161: } 1162: tp->tty_ioreq = 0; 1163: tty_reply(REVIVE, tp->tty_iocaller, tp->tty_ioproc, OK); 1164: } 1165: 1166: 1167: /*===========================================================================* 1168: * setattr * 1169: *===========================================================================*/ 1170: PRIVATE void setattr(tp) 1171: tty_t *tp; 1172: { 1173: /* Apply the new line attributes (raw/canonical, line speed, etc.) */ 1174: u16_t *inp; 1175: int count; 1176: 1177: if (!(tp->tty_termios.c_lflag & ICANON)) { 1178: /* Raw mode; put a "line break" on all characters in the input queue. 1179: * It is undefined what happens to the input queue when ICANON is 1180: * switched off, a process should use TCSAFLUSH to flush the queue. 1181: * Keeping the queue to preserve typeahead is the Right Thing, however 1182: * when a process does use TCSANOW to switch to raw mode. 1183: */ 1184: count = tp->tty_eotct = tp->tty_incount; 1185: inp = tp->tty_intail; 1186: while (count > 0) { 1187: *inp |= IN_EOT; 1188: if (++inp == bufend(tp->tty_inbuf)) inp = tp->tty_inbuf; 1189: --count; 1190: } 1191: } 1192: 1193: /* Inspect MIN and TIME. */ 1194: lock(); 1195: settimer(tp, FALSE); 1196: unlock(); 1197: if (tp->tty_termios.c_lflag & ICANON) { 1198: /* No MIN & TIME in canonical mode. */ 1199: tp->tty_min = 1; 1200: } else { 1201: /* In raw mode MIN is the number of chars wanted, and TIME how long 1202: * to wait for them. With interesting exceptions if either is zero. 1203: */ 1204: tp->tty_min = tp->tty_termios.c_cc[VMIN]; 1205: if (tp->tty_min == 0 && tp->tty_termios.c_cc[VTIME] > 0) 1206: tp->tty_min = 1; 1207: } 1208: 1209: if (!(tp->tty_termios.c_iflag & IXON)) { 1210: /* No start/stop output control, so don't leave output inhibited. */ 1211: tp->tty_inhibited = RUNNING; 1212: tp->tty_events = 1; 1213: } 1214: 1215: /* Setting the output speed to zero hangs up the phone. */ 1216: if (tp->tty_termios.c_ospeed == B0) sigchar(tp, SIGHUP); 1217: 1218: /* Set new line speed, character size, etc at the device level. */ 1219: (*tp->tty_ioctl)(tp); 1220: } 1221: 1222: 1223: /*===========================================================================* 1224: * tty_reply * 1225: *===========================================================================*/ 1226: PUBLIC void tty_reply(code, replyee, proc_nr, status) 1227: int code; /* TASK_REPLY or REVIVE */ 1228: int replyee; /* destination address for the reply */ 1229: int proc_nr; /* to whom should the reply go? */ 1230: int status; /* reply code */ 1231: { 1232: /* Send a reply to a process that wanted to read or write data. */ 1233: 1234: message tty_mess; 1235: 1236: tty_mess.m_type = code; 1237: tty_mess.REP_PROC_NR = proc_nr; 1238: tty_mess.REP_STATUS = status; 1239: if ((status = send(replyee, &tty_mess)) != OK) 1240: panic("tty_reply failed, status\n", status); 1241: } 1242: 1243: 1244: /*===========================================================================* 1245: * sigchar * 1246: *===========================================================================*/ 1247: PUBLIC void sigchar(tp, sig) 1248: register tty_t *tp; 1249: int sig; /* SIGINT, SIGQUIT, SIGKILL or SIGHUP */ 1250: { 1251: /* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard or SIGHUP from 1252: * a tty close, "stty 0", or a real RS-232 hangup. MM will send the signal to 1253: * the process group (INT, QUIT), all processes (KILL), or the session leader 1254: * (HUP). 1255: */ 1256: 1257: if (tp->tty_pgrp != 0) cause_sig(tp->tty_pgrp, sig); 1258: 1259: if (!(tp->tty_termios.c_lflag & NOFLSH)) { 1260: tp->tty_incount = tp->tty_eotct = 0; /* kill earlier input */ 1261: tp->tty_intail = tp->tty_inhead; 1262: (*tp->tty_ocancel)(tp); /* kill all output */ 1263: tp->tty_inhibited = RUNNING; 1264: tp->tty_events = 1; 1265: } 1266: } 1267: 1268: 1269: /*==========================================================================* 1270: * tty_icancel * 1271: *==========================================================================*/ 1272: PRIVATE void tty_icancel(tp) 1273: register tty_t *tp; 1274: { 1275: /* Discard all pending input, tty buffer or device. */ 1276: 1277: tp->tty_incount = tp->tty_eotct = 0; 1278: tp->tty_intail = tp->tty_inhead; 1279: (*tp->tty_icancel)(tp); 1280: } 1281: 1282: 1283: /*==========================================================================* 1284: * tty_init * 1285: *==========================================================================*/ 1286: PRIVATE void tty_init(tp) 1287: tty_t *tp; /* TTY line to initialize. */ 1288: { 1289: /* Initialize tty structure and call device initialization routines. */ 1290: 1291: tp->tty_intail = tp->tty_inhead = tp->tty_inbuf; 1292: tp->tty_min = 1; 1293: tp->tty_termios = termios_defaults; 1294: tp->tty_icancel = tp->tty_ocancel = tp->tty_ioctl = tp->tty_close = 1295: tty_devnop; 1296: if (tp < tty_addr(NR_CONS)) { 1297: scr_init(tp); 1298: } else 1299: if (tp < tty_addr(NR_CONS+NR_RS_LINES)) { 1300: rs_init(tp); 1301: } else { 1302: pty_init(tp); 1303: } 1304: } 1305: 1306: 1307: /*==========================================================================* 1308: * tty_wakeup * 1309: *==========================================================================*/ 1310: PUBLIC void tty_wakeup(now) 1311: clock_t now; /* current time */ 1312: { 1313: /* Wake up TTY when something interesting is happening on one of the terminal 1314: * lines, like a character arriving on an RS232 line, a key being typed, or 1315: * a timer on a line expiring by TIME. 1316: */ 1317: tty_t *tp; 1318: 1319: /* Scan the timerlist for expired timers and compute the next timeout time. */ 1320: tty_timeout = TIME_NEVER; 1321: while ((tp = tty_timelist) != NULL) { 1322: if (tp->tty_time > now) { 1323: tty_timeout = tp->tty_time; /* this timer is next */ 1324: break; 1325: } 1326: tp->tty_min = 0; /* force read to succeed */ 1327: tp->tty_events = 1; 1328: tty_timelist = tp->tty_timenext; 1329: } 1330: 1331: /* Let TTY know there is something afoot. */ 1332: interrupt(TTY); 1333: } 1334: 1335: 1336: /*===========================================================================* 1337: * settimer * 1338: *===========================================================================*/ 1339: PRIVATE void settimer(tp, on) 1340: tty_t *tp; /* line to set or unset a timer on */ 1341: int on; /* set timer if true, otherwise unset */ 1342: { 1343: /* Set or unset a TIME inspired timer. This function is interrupt sensitive 1344: * due to tty_wakeup(), so it must be called from within lock()/unlock(). 1345: */ 1346: tty_t **ptp; 1347: 1348: /* Take tp out of the timerlist if present. */ 1349: for (ptp = &tty_timelist; *ptp != NULL; ptp = &(*ptp)->tty_timenext) { 1350: if (tp == *ptp) { 1351: *ptp = tp->tty_timenext; /* take tp out of the list */ 1352: break; 1353: } 1354: } 1355: if (!on) return; /* unsetting it is enough */ 1356: 1357: /* Timeout occurs TIME deciseconds from now. */ 1358: tp->tty_time = get_uptime() + tp->tty_termios.c_cc[VTIME] * (HZ/10); 1359: 1360: /* Find a new place in the list. */ 1361: for (ptp = &tty_timelist; *ptp != NULL; ptp = &(*ptp)->tty_timenext) { 1362: if (tp->tty_time <= (*ptp)->tty_time) break; 1363: } 1364: tp->tty_timenext = *ptp; 1365: *ptp = tp; 1366: if (tp->tty_time < tty_timeout) tty_timeout = tp->tty_time; 1367: } 1368: 1369: 1370: /*==========================================================================* 1371: * tty_devnop * 1372: *==========================================================================*/ 1373: PUBLIC void tty_devnop(tp) 1374: tty_t *tp; 1375: { 1376: /* Some functions need not be implemented at the device level. */ 1377: } 1378: 1379: 1380: #if ENABLE_SRCCOMPAT || ENABLE_BINCOMPAT 1381: /*===========================================================================* 1382: * compat_getp * 1383: *===========================================================================*/ 1384: PRIVATE int compat_getp(tp, sg) 1385: tty_t *tp; 1386: struct sgttyb *sg; 1387: { 1388: /* Translate an old TIOCGETP to the termios equivalent. */ 1389: int flgs; 1390: 1391: sg->sg_erase = tp->tty_termios.c_cc[VERASE]; 1392: sg->sg_kill = tp->tty_termios.c_cc[VKILL]; 1393: sg->sg_ospeed = tspd2sgspd(cfgetospeed(&tp->tty_termios)); 1394: sg->sg_ispeed = tspd2sgspd(cfgetispeed(&tp->tty_termios)); 1395: 1396: flgs = 0; 1397: 1398: /* XTABS - if OPOST and XTABS */ 1399: if ((tp->tty_termios.c_oflag & (OPOST|XTABS)) == (OPOST|XTABS)) 1400: flgs |= 0006000; 1401: 1402: /* BITS5..BITS8 - map directly to CS5..CS8 */ 1403: flgs |= (tp->tty_termios.c_cflag & CSIZE) << (8-2); 1404: 1405: /* EVENP - if PARENB and not PARODD */ 1406: if ((tp->tty_termios.c_cflag & (PARENB|PARODD)) == PARENB) 1407: flgs |= 0000200; 1408: 1409: /* ODDP - if PARENB and PARODD */ 1410: if ((tp->tty_termios.c_cflag & (PARENB|PARODD)) == (PARENB|PARODD)) 1411: flgs |= 0000100; 1412: 1413: /* RAW - if not ICANON and not ISIG */ 1414: if (!(tp->tty_termios.c_lflag & (ICANON|ISIG))) 1415: flgs |= 0000040; 1416: 1417: /* CRMOD - if ICRNL */ 1418: if (tp->tty_termios.c_iflag & ICRNL) 1419: flgs |= 0000020; 1420: 1421: /* ECHO - if ECHO */ 1422: if (tp->tty_termios.c_lflag & ECHO) 1423: flgs |= 0000010; 1424: 1425: /* CBREAK - if not ICANON and ISIG */ 1426: if ((tp->tty_termios.c_lflag & (ICANON|ISIG)) == ISIG) 1427: flgs |= 0000002; 1428: 1429: sg->sg_flags = flgs; 1430: return(OK); 1431: } 1432: 1433: 1434: /*===========================================================================* 1435: * compat_getc * 1436: *===========================================================================*/ 1437: PRIVATE int compat_getc(tp, tc) 1438: tty_t *tp; 1439: struct tchars *tc; 1440: { 1441: /* Translate an old TIOCGETC to the termios equivalent. */ 1442: 1443: tc->t_intrc = tp->tty_termios.c_cc[VINTR]; 1444: tc->t_quitc = tp->tty_termios.c_cc[VQUIT]; 1445: tc->t_startc = tp->tty_termios.c_cc[VSTART]; 1446: tc->t_stopc = tp->tty_termios.c_cc[VSTOP]; 1447: tc->t_brkc = tp->tty_termios.c_cc[VEOL]; 1448: tc->t_eofc = tp->tty_termios.c_cc[VEOF]; 1449: return(OK); 1450: } 1451: 1452: 1453: /*===========================================================================* 1454: * compat_setp * 1455: *===========================================================================*/ 1456: PRIVATE int compat_setp(tp, sg) 1457: tty_t *tp; 1458: struct sgttyb *sg; 1459: { 1460: /* Translate an old TIOCSETP to the termios equivalent. */ 1461: struct termios termios; 1462: int flags; 1463: 1464: termios = tp->tty_termios; 1465: 1466: termios.c_cc[VERASE] = sg->sg_erase; 1467: termios.c_cc[VKILL] = sg->sg_kill; 1468: cfsetispeed(&termios, sgspd2tspd(sg->sg_ispeed & BYTE)); 1469: cfsetospeed(&termios, sgspd2tspd(sg->sg_ospeed & BYTE)); 1470: flags = sg->sg_flags; 1471: 1472: /* Input flags */ 1473: 1474: /* BRKINT - not changed */ 1475: /* ICRNL - set if CRMOD is set and not RAW */ 1476: /* (CRMOD also controls output) */ 1477: termios.c_iflag &= ~ICRNL; 1478: if ((flags & 0000020) && !(flags & 0000040)) 1479: termios.c_iflag |= ICRNL; 1480: 1481: /* IGNBRK - not changed */ 1482: /* IGNCR - forced off (ignoring cr's is not supported) */ 1483: termios.c_iflag &= ~IGNCR; 1484: 1485: /* IGNPAR - not changed */ 1486: /* INLCR - forced off (mapping nl's to cr's is not supported) */ 1487: termios.c_iflag &= ~INLCR; 1488: 1489: /* INPCK - not changed */ 1490: /* ISTRIP - not changed */ 1491: /* IXOFF - not changed */ 1492: /* IXON - forced on if not RAW */ 1493: termios.c_iflag &= ~IXON; 1494: if (!(flags & 0000040)) 1495: termios.c_iflag |= IXON; 1496: 1497: /* PARMRK - not changed */ 1498: 1499: /* Output flags */ 1500: 1501: /* OPOST - forced on if not RAW */ 1502: termios.c_oflag &= ~OPOST; 1503: if (!(flags & 0000040)) 1504: termios.c_oflag |= OPOST; 1505: 1506: /* ONLCR - forced on if CRMOD */ 1507: termios.c_oflag &= ~ONLCR; 1508: if (flags & 0000020) 1509: termios.c_oflag |= ONLCR; 1510: 1511: /* XTABS - forced on if XTABS */ 1512: termios.c_oflag &= ~XTABS; 1513: if (flags & 0006000) 1514: termios.c_oflag |= XTABS; 1515: 1516: /* CLOCAL - not changed */ 1517: /* CREAD - forced on (receiver is always enabled) */ 1518: termios.c_cflag |= CREAD; 1519: 1520: /* CSIZE - CS5-CS8 correspond directly to BITS5-BITS8 */ 1521: termios.c_cflag = (termios.c_cflag & ~CSIZE) | ((flags & 0001400) >> (8-2)); 1522: 1523: /* CSTOPB - not changed */ 1524: /* HUPCL - not changed */ 1525: /* PARENB - set if EVENP or ODDP is set */ 1526: termios.c_cflag &= ~PARENB; 1527: if (flags & (0000200|0000100)) 1528: termios.c_cflag |= PARENB; 1529: 1530: /* PARODD - set if ODDP is set */ 1531: termios.c_cflag &= ~PARODD; 1532: if (flags & 0000100) 1533: termios.c_cflag |= PARODD; 1534: 1535: /* Local flags */ 1536: 1537: /* ECHO - set if ECHO is set */ 1538: termios.c_lflag &= ~ECHO; 1539: if (flags & 0000010) 1540: termios.c_lflag |= ECHO; 1541: 1542: /* ECHOE - not changed */ 1543: /* ECHOK - not changed */ 1544: /* ECHONL - not changed */ 1545: /* ICANON - set if neither CBREAK nor RAW */ 1546: termios.c_lflag &= ~ICANON; 1547: if (!(flags & (0000002|0000040))) 1548: termios.c_lflag |= ICANON; 1549: 1550: /* IEXTEN - set if not RAW */ 1551: /* ISIG - set if not RAW */ 1552: termios.c_lflag &= ~(IEXTEN|ISIG); 1553: if (!(flags & 0000040)) 1554: termios.c_lflag |= (IEXTEN|ISIG); 1555: 1556: /* NOFLSH - not changed */ 1557: /* TOSTOP - not changed */ 1558: 1559: tp->tty_termios = termios; 1560: setattr(tp); 1561: return(OK); 1562: } 1563: 1564: 1565: /*===========================================================================* 1566: * compat_setc * 1567: *===========================================================================*/ 1568: PRIVATE int compat_setc(tp, tc) 1569: tty_t *tp; 1570: struct tchars *tc; 1571: { 1572: /* Translate an old TIOCSETC to the termios equivalent. */ 1573: struct termios termios; 1574: 1575: termios = tp->tty_termios; 1576: 1577: termios.c_cc[VINTR] = tc->t_intrc; 1578: termios.c_cc[VQUIT] = tc->t_quitc; 1579: termios.c_cc[VSTART] = tc->t_startc; 1580: termios.c_cc[VSTOP] = tc->t_stopc; 1581: termios.c_cc[VEOL] = tc->t_brkc; 1582: termios.c_cc[VEOF] = tc->t_eofc; 1583: 1584: tp->tty_termios = termios; 1585: setattr(tp); 1586: return(OK); 1587: } 1588: 1589: 1590: /* Table of termios line speed to sgtty line speed translations. All termios 1591: * speeds are present even if sgtty didn't know about them. (Now it does.) 1592: */ 1593: PRIVATE struct s2s { 1594: speed_t tspd; 1595: u8_t sgspd; 1596: } ts2sgs[] = { 1597: { B0, 0 }, 1598: { B50, 50 }, 1599: { B75, 75 }, 1600: { B110, 1 }, 1601: { B134, 134 }, 1602: { B200, 2 }, 1603: { B300, 3 }, 1604: { B600, 6 }, 1605: { B1200, 12 }, 1606: { B1800, 18 }, 1607: { B2400, 24 }, 1608: { B4800, 48 }, 1609: { B9600, 96 }, 1610: { B19200, 192 }, 1611: { B38400, 195 }, 1612: { B57600, 194 }, 1613: { B115200, 193 }, 1614: }; 1615: 1616: /*===========================================================================* 1617: * tspd2sgspd * 1618: *===========================================================================*/ 1619: PRIVATE int tspd2sgspd(tspd) 1620: speed_t tspd; 1621: { 1622: /* Translate a termios speed to sgtty speed. */ 1623: struct s2s *s; 1624: 1625: for (s = ts2sgs; s < ts2sgs + sizeof(ts2sgs)/sizeof(ts2sgs[0]); s++) { 1626: if (s->tspd == tspd) return(s->sgspd); 1627: } 1628: return 96; 1629: } 1630: 1631: 1632: /*===========================================================================* 1633: * sgspd2tspd * 1634: *===========================================================================*/ 1635: PRIVATE speed_t sgspd2tspd(sgspd) 1636: int sgspd; 1637: { 1638: /* Translate a sgtty speed to termios speed. */ 1639: struct s2s *s; 1640: 1641: for (s = ts2sgs; s < ts2sgs + sizeof(ts2sgs)/sizeof(ts2sgs[0]); s++) { 1642: if (s->sgspd == sgspd) return(s->tspd); 1643: } 1644: return B9600; 1645: } 1646: 1647: 1648: #if ENABLE_BINCOMPAT 1649: /*===========================================================================* 1650: * do_ioctl_compat * 1651: *===========================================================================*/ 1652: PRIVATE void do_ioctl_compat(tp, m_ptr) 1653: tty_t *tp; 1654: message *m_ptr; 1655: { 1656: /* Handle the old sgtty ioctl's that packed the sgtty or tchars struct into 1657: * the Minix message. Efficient then, troublesome now. 1658: */ 1659: int minor, proc, func, result, r; 1660: long flags, erki, spek; 1661: u8_t erase, kill, intr, quit, xon, xoff, brk, eof, ispeed, ospeed; 1662: struct sgttyb sg; 1663: struct tchars tc; 1664: message reply_mess; 1665: 1666: minor = m_ptr->TTY_LINE; 1667: proc = m_ptr->PROC_NR; 1668: func = m_ptr->REQUEST; 1669: spek = m_ptr->m2_l1; 1670: flags = m_ptr->m2_l2; 1671: 1672: switch(func) 1673: { 1674: case (('t'<<8) | 8): /* TIOCGETP */ 1675: r = compat_getp(tp, &sg); 1676: erase = sg.sg_erase; 1677: kill = sg.sg_kill; 1678: ispeed = sg.sg_ispeed; 1679: ospeed = sg.sg_ospeed; 1680: flags = sg.sg_flags; 1681: erki = ((long)ospeed<<24) | ((long)ispeed<<16) | ((long)erase<<8) |kill; 1682: break; 1683: case (('t'<<8) | 18): /* TIOCGETC */ 1684: r = compat_getc(tp, &tc); 1685: intr = tc.t_intrc; 1686: quit = tc.t_quitc; 1687: xon = tc.t_startc; 1688: xoff = tc.t_stopc; 1689: brk = tc.t_brkc; 1690: eof = tc.t_eofc; 1691: erki = ((long)intr<<24) | ((long)quit<<16) | ((long)xon<<8) | xoff; 1692: flags = (eof << 8) | brk; 1693: break; 1694: case (('t'<<8) | 17): /* TIOCSETC */ 1695: tc.t_stopc = (spek >> 0) & 0xFF; 1696: tc.t_startc = (spek >> 8) & 0xFF; 1697: tc.t_quitc = (spek >> 16) & 0xFF; 1698: tc.t_intrc = (spek >> 24) & 0xFF; 1699: tc.t_brkc = (flags >> 0) & 0xFF; 1700: tc.t_eofc = (flags >> 8) & 0xFF; 1701: r = compat_setc(tp, &tc); 1702: break; 1703: case (('t'<<8) | 9): /* TIOCSETP */ 1704: sg.sg_erase = (spek >> 8) & 0xFF; 1705: sg.sg_kill = (spek >> 0) & 0xFF; 1706: sg.sg_ispeed = (spek >> 16) & 0xFF; 1707: sg.sg_ospeed = (spek >> 24) & 0xFF; 1708: sg.sg_flags = flags; 1709: r = compat_setp(tp, &sg); 1710: break; 1711: default: 1712: r = ENOTTY; 1713: } 1714: reply_mess.m_type = TASK_REPLY; 1715: reply_mess.REP_PROC_NR = m_ptr->PROC_NR; 1716: reply_mess.REP_STATUS = r; 1717: reply_mess.m2_l1 = erki; 1718: reply_mess.m2_l2 = flags; 1719: send(m_ptr->m_source, &reply_mess); 1720: } 1721: #endif /* ENABLE_BINCOMPAT */ 1722: #endif /* ENABLE_SRCCOMPAT || ENABLE_BINCOMPAT */ 1723: 1724: 1725: