Annotation of prex-old/dev/gen/tty.c, Revision 1.1
1.1 ! nbrk 1: /*-
! 2: * Copyright (c) 1982, 1986, 1990, 1991, 1993
! 3: * The Regents of the University of California. All rights reserved.
! 4: * (c) UNIX System Laboratories, Inc.
! 5: * All or some portions of this file are derived from material licensed
! 6: * to the University of California by American Telephone and Telegraph
! 7: * Co. or Unix System Laboratories, Inc. and are reproduced herein with
! 8: * the permission of UNIX System Laboratories, Inc.
! 9: *
! 10: * Redistribution and use in source and binary forms, with or without
! 11: * modification, are permitted provided that the following conditions
! 12: * are met:
! 13: * 1. Redistributions of source code must retain the above copyright
! 14: * notice, this list of conditions and the following disclaimer.
! 15: * 2. Redistributions in binary form must reproduce the above copyright
! 16: * notice, this list of conditions and the following disclaimer in the
! 17: * documentation and/or other materials provided with the distribution.
! 18: * 3. Neither the name of the University nor the names of its contributors
! 19: * may be used to endorse or promote products derived from this software
! 20: * without specific prior written permission.
! 21: *
! 22: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 25: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 28: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 32: * SUCH DAMAGE.
! 33: *
! 34: * @(#)tty.c 8.13 (Berkeley) 1/9/95
! 35: */
! 36:
! 37: /*
! 38: * tty.c - TTY device
! 39: */
! 40:
! 41: #include <driver.h>
! 42: #include <sys/signal.h>
! 43: #include <sys/tty.h>
! 44: #include <sys/ttycom.h>
! 45: #include <sys/termios.h>
! 46:
! 47: #define FREAD 0x0001
! 48: #define FWRITE 0x0002
! 49:
! 50: void tty_output(int c, struct tty *tp);
! 51: static int tty_init(void);
! 52:
! 53: /*
! 54: * Driver structure
! 55: */
! 56: struct driver tty_drv = {
! 57: /* name */ "TTY device",
! 58: /* order */ 10, /* must be larger than console */
! 59: /* init */ tty_init,
! 60: };
! 61:
! 62: static device_t tty_dev;
! 63:
! 64: /* task to dispatch the tty signal */
! 65: static task_t sig_task;
! 66:
! 67: /* default control characters */
! 68: static cc_t ttydefchars[NCCS] = TTYDEFCHARS;
! 69:
! 70: #define is_ctrl(c) ((c) < 32 || (c) == 0x7f)
! 71:
! 72: /*
! 73: * TTY queue operations
! 74: */
! 75: #define ttyq_next(i) (((i) + 1) & (TTYQ_SIZE - 1))
! 76: #define ttyq_prev(i) (((i) - 1) & (TTYQ_SIZE - 1))
! 77: #define ttyq_full(q) ((q)->count >= TTYQ_SIZE)
! 78: #define ttyq_empty(q) ((q)->count == 0)
! 79:
! 80: /*
! 81: * Get a character from a queue.
! 82: */
! 83: int
! 84: ttyq_getc(struct tty_queue *tq)
! 85: {
! 86: int c;
! 87:
! 88: if (ttyq_empty(tq))
! 89: return -1;
! 90: irq_lock();
! 91: c = tq->buf[tq->head];
! 92: tq->head = ttyq_next(tq->head);
! 93: tq->count--;
! 94: irq_unlock();
! 95: return c;
! 96: }
! 97:
! 98: /*
! 99: * Put a character into a queue.
! 100: */
! 101: void
! 102: ttyq_putc(int c, struct tty_queue *tq)
! 103: {
! 104:
! 105: if (ttyq_full(tq))
! 106: return;
! 107: irq_lock();
! 108: tq->buf[tq->tail] = c;
! 109: tq->tail = ttyq_next(tq->tail);
! 110: tq->count++;
! 111: irq_unlock();
! 112: }
! 113:
! 114: /*
! 115: * Remove the last character in a queue and return it.
! 116: */
! 117: int
! 118: ttyq_unputc(struct tty_queue *tq)
! 119: {
! 120: int c;
! 121:
! 122: if (ttyq_empty(tq))
! 123: return -1;
! 124: irq_lock();
! 125: tq->tail = ttyq_prev(tq->tail);
! 126: c = tq->buf[tq->tail];
! 127: tq->count--;
! 128: irq_unlock();
! 129: return c;
! 130: }
! 131:
! 132: /*
! 133: * Put the chars in the from queue on the end of the to queue.
! 134: */
! 135: static void
! 136: tty_catq(struct tty_queue *from, struct tty_queue *to)
! 137: {
! 138: int c;
! 139:
! 140: while ((c = ttyq_getc(from)) != -1)
! 141: ttyq_putc(c, to);
! 142: }
! 143:
! 144: /*
! 145: * Rubout one character from the rawq of tp
! 146: */
! 147: static void
! 148: tty_rubout(struct tty *tp)
! 149: {
! 150:
! 151: if (!(tp->t_lflag & ECHO))
! 152: return;
! 153: if (tp->t_lflag & ECHOE) {
! 154: tty_output('\b', tp);
! 155: tty_output(' ', tp);
! 156: tty_output('\b', tp);
! 157: } else
! 158: tty_output(tp->t_cc[VERASE], tp);
! 159: }
! 160:
! 161: /*
! 162: * Echo char
! 163: */
! 164: static void
! 165: tty_echo(int c, struct tty *tp)
! 166: {
! 167:
! 168: if (!(tp->t_lflag & ECHO)) {
! 169: if (c == '\n' && (tp->t_lflag & ECHONL))
! 170: tty_output('\n', tp);
! 171: return;
! 172: }
! 173: if (is_ctrl(c) && c != '\n' && c != '\t' && c != '\b') {
! 174: tty_output('^', tp);
! 175: tty_output(c + 'A' - 1, tp);
! 176: } else
! 177: tty_output(c, tp);
! 178: }
! 179:
! 180: /*
! 181: * Start output.
! 182: */
! 183: void
! 184: tty_start(struct tty *tp)
! 185: {
! 186:
! 187: if (tp->t_state & TS_TTSTOP)
! 188: return;
! 189: if (tp->t_output != NULL)
! 190: (*tp->t_output)(tp);
! 191: }
! 192:
! 193: /*
! 194: * Flush tty read and/or write queues, notifying anyone waiting.
! 195: */
! 196: void
! 197: tty_flush(struct tty *tp, int rw)
! 198: {
! 199:
! 200: if (rw & FREAD)
! 201: sched_wakeup(&tp->t_input_event);
! 202: if (rw & FWRITE) {
! 203: tp->t_state &= ~TS_TTSTOP;
! 204: tty_start(tp);
! 205: }
! 206: }
! 207:
! 208: /*
! 209: * Process input of a single character received on a tty.
! 210: * echo if required.
! 211: * This will be called with interrupt level.
! 212: */
! 213: void
! 214: tty_input(int c, struct tty *tp)
! 215: {
! 216: unsigned char *cc;
! 217: tcflag_t iflag, lflag;
! 218: int sig = -1;
! 219:
! 220: lflag = tp->t_lflag;
! 221: iflag = tp->t_iflag;
! 222: cc = tp->t_cc;
! 223:
! 224: /* IGNCR, ICRNL, INLCR */
! 225: if (c == '\r') {
! 226: if (iflag & IGNCR)
! 227: goto endcase;
! 228: else if (iflag & ICRNL)
! 229: c = '\n';
! 230: } else if (c == '\n' && (iflag & INLCR))
! 231: c = '\r';
! 232:
! 233: if (iflag & IXON) {
! 234: /* stop (^S) */
! 235: if (c == cc[VSTOP]) {
! 236: if (!(tp->t_state & TS_TTSTOP)) {
! 237: tp->t_state |= TS_TTSTOP;
! 238: return;
! 239: }
! 240: if (c != cc[VSTART])
! 241: return;
! 242: /* if VSTART == VSTOP then toggle */
! 243: goto endcase;
! 244: }
! 245: /* start (^Q) */
! 246: if (c == cc[VSTART])
! 247: goto restartoutput;
! 248: }
! 249: if (lflag & ICANON) {
! 250: /* erase (^H / ^?) or backspace */
! 251: if (c == cc[VERASE] || c == '\b') {
! 252: if (!ttyq_empty(&tp->t_rawq)) {
! 253: ttyq_unputc(&tp->t_rawq);
! 254: tty_rubout(tp);
! 255: }
! 256: goto endcase;
! 257: }
! 258: /* kill (^U) */
! 259: if (c == cc[VKILL]) {
! 260: while (!ttyq_empty(&tp->t_rawq)) {
! 261: ttyq_unputc(&tp->t_rawq);
! 262: tty_rubout(tp);
! 263: }
! 264: goto endcase;
! 265: }
! 266: }
! 267: if (lflag & ISIG) {
! 268: /* quit (^C) */
! 269: if (c == cc[VINTR] || c == cc[VQUIT]) {
! 270: if (!(lflag & NOFLSH))
! 271: tty_flush(tp, FREAD | FWRITE);
! 272: tty_echo(c, tp);
! 273: sig = (c == cc[VINTR]) ? SIGINT : SIGQUIT;
! 274: goto endcase;
! 275: }
! 276: /* suspend (^Z) */
! 277: if (c == cc[VSUSP]) {
! 278: if (!(lflag & NOFLSH))
! 279: tty_flush(tp, FREAD | FWRITE);
! 280: tty_echo(c, tp);
! 281: sig = SIGTSTP;
! 282: goto endcase;
! 283: }
! 284: }
! 285:
! 286: /*
! 287: * Check for input buffer overflow
! 288: */
! 289: if (ttyq_full(&tp->t_rawq)) {
! 290: tty_flush(tp, FREAD | FWRITE);
! 291: goto endcase;
! 292: }
! 293: ttyq_putc(c, &tp->t_rawq);
! 294:
! 295: if (lflag & ICANON) {
! 296: if (c == '\n' || c == cc[VEOF] || c == cc[VEOL]) {
! 297: tty_catq(&tp->t_rawq, &tp->t_canq);
! 298: sched_wakeup(&tp->t_input_event);
! 299: }
! 300: } else
! 301: sched_wakeup(&tp->t_input_event);
! 302:
! 303: if (lflag & ECHO)
! 304: tty_echo(c, tp);
! 305: endcase:
! 306: /*
! 307: * IXANY means allow any character to restart output.
! 308: */
! 309: if ((tp->t_state & TS_TTSTOP) && (iflag & IXANY) == 0 &&
! 310: cc[VSTART] != cc[VSTOP])
! 311: return;
! 312: restartoutput:
! 313: tp->t_state &= ~TS_TTSTOP;
! 314:
! 315: if (sig != -1) {
! 316: if (sig_task)
! 317: exception_post(sig_task, sig);
! 318: }
! 319: tty_start(tp);
! 320: }
! 321:
! 322: /*
! 323: * Output a single character on a tty, doing output processing
! 324: * as needed (expanding tabs, newline processing, etc.).
! 325: */
! 326: void
! 327: tty_output(int c, struct tty *tp)
! 328: {
! 329: int i, col;
! 330:
! 331: if ((tp->t_lflag & ICANON) == 0) {
! 332: ttyq_putc(c, &tp->t_outq);
! 333: return;
! 334: }
! 335: /* Expand tab */
! 336: if (c == '\t' && (tp->t_oflag & OXTABS)) {
! 337: i = 8 - (tp->t_column & 7);
! 338: tp->t_column += i;
! 339: do
! 340: ttyq_putc(' ', &tp->t_outq);
! 341: while (--i > 0);
! 342: return;
! 343: }
! 344: /* Translate newline into "\r\n" */
! 345: if (c == '\n' && (tp->t_oflag & ONLCR))
! 346: ttyq_putc('\r', &tp->t_outq);
! 347:
! 348: ttyq_putc(c, &tp->t_outq);
! 349:
! 350: col = tp->t_column;
! 351: switch (c) {
! 352: case '\b': /* back space */
! 353: if (col > 0)
! 354: --col;
! 355: break;
! 356: case '\t': /* tab */
! 357: col = (col + 8) & ~7;
! 358: break;
! 359: case '\n': /* newline */
! 360: col = 0;
! 361: break;
! 362: case '\r': /* return */
! 363: col = 0;
! 364: break;
! 365: default:
! 366: if (!is_ctrl(c))
! 367: ++col;
! 368: break;
! 369: }
! 370: tp->t_column = col;
! 371: return;
! 372: }
! 373:
! 374: /*
! 375: * Read
! 376: */
! 377: int
! 378: tty_read(struct tty *tp, char *buf, size_t *nbytes)
! 379: {
! 380: unsigned char *cc;
! 381: struct tty_queue *qp;
! 382: int rc, c;
! 383: size_t count = 0;
! 384: tcflag_t lflag;
! 385:
! 386: lflag = tp->t_lflag;
! 387: cc = tp->t_cc;
! 388: qp = (lflag & ICANON) ? &tp->t_canq : &tp->t_rawq;
! 389:
! 390: /* If there is no input, wait it */
! 391: while (ttyq_empty(qp)) {
! 392: rc = sched_sleep(&tp->t_input_event);
! 393: if (rc == SLP_INTR)
! 394: return EINTR;
! 395: }
! 396: while (count < *nbytes) {
! 397: if ((c = ttyq_getc(qp)) == -1)
! 398: break;
! 399: count++;
! 400: if (c == cc[VEOF] && (lflag & ICANON))
! 401: break;
! 402: *buf = c;
! 403: if ((lflag & ICANON) && (c == '\n' || c == cc[VEOL]))
! 404: break;
! 405: buf++;
! 406: }
! 407: *nbytes = count;
! 408: return 0;
! 409: }
! 410:
! 411: /*
! 412: * Write
! 413: */
! 414: int
! 415: tty_write(struct tty *tp, char *buf, size_t *nbyte)
! 416: {
! 417: size_t remain, count = 0;
! 418:
! 419: remain = *nbyte;
! 420: while (remain > 0) {
! 421: if (tp->t_outq.count >= TTYQ_HIWAT) {
! 422: tty_start(tp);
! 423: continue;
! 424: }
! 425: tty_output(*buf, tp);
! 426: buf++;
! 427: remain--;
! 428: count++;
! 429: }
! 430: tty_start(tp);
! 431: *nbyte = count;
! 432: return 0;
! 433: }
! 434:
! 435: /*
! 436: * Ioctls for all tty devices.
! 437: */
! 438: int
! 439: tty_ioctl(struct tty *tp, u_long cmd, void *data)
! 440: {
! 441: int flags;
! 442:
! 443: switch (cmd) {
! 444: case TIOCGETA:
! 445: if (umem_copyout(&tp->t_termios, data,
! 446: sizeof(struct termios)))
! 447: return EFAULT;
! 448: break;
! 449: case TIOCSETAW:
! 450: case TIOCSETAF:
! 451: tty_flush(tp, flags);
! 452: /* fallthrouth */
! 453: case TIOCSETA:
! 454: if (umem_copyin(data, &tp->t_termios,
! 455: sizeof(struct termios)))
! 456: return EFAULT;
! 457: break;
! 458: case TIOCSPGRP: /* set pgrp of tty */
! 459: if (umem_copyin(data, &tp->t_pgid, sizeof(pid_t)))
! 460: return EFAULT;
! 461: break;
! 462: case TIOCGPGRP:
! 463: if (umem_copyout(&tp->t_pgid, data, sizeof(pid_t)))
! 464: return EFAULT;
! 465: break;
! 466: case TIOCFLUSH:
! 467: if (umem_copyin(data, &flags, sizeof(flags)))
! 468: return EFAULT;
! 469: break;
! 470: if (flags == 0)
! 471: flags = FREAD | FWRITE;
! 472: else
! 473: flags &= FREAD | FWRITE;
! 474: tty_flush(tp, flags);
! 475: break;
! 476: case TIOCSTART:
! 477: if (tp->t_state & TS_TTSTOP) {
! 478: tp->t_state &= ~TS_TTSTOP;
! 479: tty_start(tp);
! 480: }
! 481: break;
! 482: case TIOCSTOP:
! 483: if (!(tp->t_state & TS_TTSTOP)) {
! 484: tp->t_state |= TS_TTSTOP;
! 485: }
! 486: break;
! 487: case TIOCGWINSZ:
! 488: if (umem_copyout(&tp->t_winsize, data,
! 489: sizeof(struct winsize)))
! 490: return EFAULT;
! 491: break;
! 492: case TIOCSWINSZ:
! 493: if (umem_copyin(&tp->t_winsize, data,
! 494: sizeof(struct winsize)))
! 495: return EFAULT;
! 496: break;
! 497: case TIOCSETSIGT:
! 498: if (umem_copyin(data, &sig_task, sizeof(task_t)))
! 499: return EFAULT;
! 500: break;
! 501: }
! 502: return 0;
! 503: }
! 504:
! 505: /*
! 506: * Register tty device.
! 507: */
! 508: int
! 509: tty_register(struct devio *io, struct tty *tp, void (*output)(struct tty*))
! 510: {
! 511:
! 512: /* We support only one tty device */
! 513: if (tty_dev != NULL_DEVICE)
! 514: return -1;
! 515:
! 516: /* Create TTY device as an alias of the registered device. */
! 517: tty_dev = device_create(io, "tty", DF_CHR);
! 518: if (tty_dev == NULL_DEVICE)
! 519: return -1;
! 520:
! 521: /* Initialize tty */
! 522: memset(tp, 0, sizeof(struct tty));
! 523: memcpy(&tp->t_termios.c_cc, ttydefchars, sizeof(ttydefchars));
! 524: event_init(&tp->t_input_event, "TTY input");
! 525: tp->t_iflag = TTYDEF_IFLAG;
! 526: tp->t_oflag = TTYDEF_OFLAG;
! 527: tp->t_cflag = TTYDEF_CFLAG;
! 528: tp->t_lflag = TTYDEF_LFLAG;
! 529: tp->t_ispeed = TTYDEF_SPEED;
! 530: tp->t_ospeed = TTYDEF_SPEED;
! 531: tp->t_output = output;
! 532: return 0;
! 533: }
! 534:
! 535: /*
! 536: * Init
! 537: */
! 538: static int
! 539: tty_init(void)
! 540: {
! 541:
! 542: return 0;
! 543: }
CVSweb