[BACK]Return to tty.c CVS log [TXT][DIR] Up to [local] / prex / dev / gen

Annotation of prex/dev/gen/tty.c, Revision 1.1.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/termios.h>
                     45:
                     46: #define        FREAD           0x0001
                     47: #define        FWRITE          0x0002
                     48:
                     49: void tty_output(int c, struct tty *tp);
                     50: static int tty_init(void);
                     51:
                     52: /*
                     53:  * Driver structure
                     54:  */
                     55: struct driver tty_drv = {
                     56:        /* name */      "TTY device",
                     57:        /* order */     10,             /* must be larger than console */
                     58:        /* init */      tty_init,
                     59: };
                     60:
                     61: static device_t tty_dev;
                     62:
                     63: /* task to dispatch the tty signal */
                     64: static task_t sig_task;
                     65:
                     66: /* default control characters */
                     67: static cc_t    ttydefchars[NCCS] = TTYDEFCHARS;
                     68:
                     69: #define is_ctrl(c)   ((c) < 32 || (c) == 0x7f)
                     70:
                     71: /*
                     72:  * TTY queue operations
                     73:  */
                     74: #define ttyq_next(i)   (((i) + 1) & (TTYQ_SIZE - 1))
                     75: #define ttyq_prev(i)   (((i) - 1) & (TTYQ_SIZE - 1))
                     76: #define ttyq_full(q)   ((q)->tq_count >= TTYQ_SIZE)
                     77: #define ttyq_empty(q)  ((q)->tq_count == 0)
                     78:
                     79: /*
                     80:  * Get a character from a queue.
                     81:  */
                     82: int
                     83: ttyq_getc(struct tty_queue *tq)
                     84: {
                     85:        int c;
                     86:
                     87:        if (ttyq_empty(tq))
                     88:                return -1;
                     89:        irq_lock();
                     90:        c = tq->tq_buf[tq->tq_head];
                     91:        tq->tq_head = ttyq_next(tq->tq_head);
                     92:        tq->tq_count--;
                     93:        irq_unlock();
                     94:        return c;
                     95: }
                     96:
                     97: /*
                     98:  * Put a character into a queue.
                     99:  */
                    100: void
                    101: ttyq_putc(int c, struct tty_queue *tq)
                    102: {
                    103:
                    104:        if (ttyq_full(tq))
                    105:                return;
                    106:        irq_lock();
                    107:        tq->tq_buf[tq->tq_tail] = c;
                    108:        tq->tq_tail = ttyq_next(tq->tq_tail);
                    109:        tq->tq_count++;
                    110:        irq_unlock();
                    111: }
                    112:
                    113: /*
                    114:  * Remove the last character in a queue and return it.
                    115:  */
                    116: int
                    117: ttyq_unputc(struct tty_queue *tq)
                    118: {
                    119:        int c;
                    120:
                    121:        if (ttyq_empty(tq))
                    122:                return -1;
                    123:        irq_lock();
                    124:        tq->tq_tail = ttyq_prev(tq->tq_tail);
                    125:        c = tq->tq_buf[tq->tq_tail];
                    126:        tq->tq_count--;
                    127:        irq_unlock();
                    128:        return c;
                    129: }
                    130:
                    131: /*
                    132:  * Put the chars in the from queue on the end of the to queue.
                    133:  */
                    134: static void
                    135: tty_catq(struct tty_queue *from, struct tty_queue *to)
                    136: {
                    137:        int c;
                    138:
                    139:        while ((c = ttyq_getc(from)) != -1)
                    140:                ttyq_putc(c, to);
                    141: }
                    142:
                    143: /*
                    144:  * Rubout one character from the rawq of tp
                    145:  */
                    146: static void
                    147: tty_rubout(struct tty *tp)
                    148: {
                    149:
                    150:        if (!(tp->t_lflag & ECHO))
                    151:                return;
                    152:        if (tp->t_lflag & ECHOE) {
                    153:                tty_output('\b', tp);
                    154:                tty_output(' ', tp);
                    155:                tty_output('\b', tp);
                    156:        } else
                    157:                tty_output(tp->t_cc[VERASE], tp);
                    158: }
                    159:
                    160: /*
                    161:  * Echo char
                    162:  */
                    163: static void
                    164: tty_echo(int c, struct tty *tp)
                    165: {
                    166:
                    167:        if (!(tp->t_lflag & ECHO)) {
                    168:                if (c == '\n' && (tp->t_lflag & ECHONL))
                    169:                        tty_output('\n', tp);
                    170:                return;
                    171:        }
                    172:        if (is_ctrl(c) && c != '\n' && c != '\t' && c != '\b') {
                    173:                tty_output('^', tp);
                    174:                tty_output(c + 'A' - 1, tp);
                    175:        } else
                    176:                tty_output(c, tp);
                    177: }
                    178:
                    179: /*
                    180:  * Start output.
                    181:  */
                    182: static void
                    183: tty_start(struct tty *tp)
                    184: {
                    185:
                    186:        if (tp->t_state & TS_TTSTOP)
                    187:                return;
                    188:        if (tp->t_output != NULL)
                    189:                (*tp->t_output)(tp);
                    190: }
                    191:
                    192: /*
                    193:  * Flush tty read and/or write queues, notifying anyone waiting.
                    194:  */
                    195: static void
                    196: tty_flush(struct tty *tp, int rw)
                    197: {
                    198:
                    199:        if (rw & FREAD)
                    200:                sched_wakeup(&tp->t_input_event);
                    201:        if (rw & FWRITE) {
                    202:                tp->t_state &= ~TS_TTSTOP;
                    203:                tty_start(tp);
                    204:        }
                    205: }
                    206:
                    207: /*
                    208:  * Process input of a single character received on a tty.
                    209:  * echo if required.
                    210:  * This will be called with interrupt level.
                    211:  */
                    212: void
                    213: tty_input(int c, struct tty *tp)
                    214: {
                    215:        unsigned char *cc;
                    216:        tcflag_t iflag, lflag;
                    217:        int sig = -1;
                    218:
                    219:        lflag = tp->t_lflag;
                    220:        iflag = tp->t_iflag;
                    221:        cc = tp->t_cc;
                    222:
                    223:        /* IGNCR, ICRNL, INLCR */
                    224:        if (c == '\r') {
                    225:                if (iflag & IGNCR)
                    226:                        goto endcase;
                    227:                else if (iflag & ICRNL)
                    228:                        c = '\n';
                    229:        } else if (c == '\n' && (iflag & INLCR))
                    230:                c = '\r';
                    231:
                    232:        if (iflag & IXON) {
                    233:                /* stop (^S) */
                    234:                if (c == cc[VSTOP]) {
                    235:                        if (!(tp->t_state & TS_TTSTOP)) {
                    236:                                tp->t_state |= TS_TTSTOP;
                    237:                                return;
                    238:                        }
                    239:                        if (c != cc[VSTART])
                    240:                                return;
                    241:                        /* if VSTART == VSTOP then toggle */
                    242:                        goto endcase;
                    243:                }
                    244:                /* start (^Q) */
                    245:                if (c == cc[VSTART])
                    246:                        goto restartoutput;
                    247:        }
                    248:        if (lflag & ICANON) {
                    249:                /* erase (^H / ^?) or backspace */
                    250:                if (c == cc[VERASE] || c == '\b') {
                    251:                        if (!ttyq_empty(&tp->t_rawq)) {
                    252:                                ttyq_unputc(&tp->t_rawq);
                    253:                                tty_rubout(tp);
                    254:                        }
                    255:                        goto endcase;
                    256:                }
                    257:                /* kill (^U) */
                    258:                if (c == cc[VKILL]) {
                    259:                        while (!ttyq_empty(&tp->t_rawq)) {
                    260:                                ttyq_unputc(&tp->t_rawq);
                    261:                                tty_rubout(tp);
                    262:                        }
                    263:                        goto endcase;
                    264:                }
                    265:        }
                    266:        if (lflag & ISIG) {
                    267:                /* quit (^C) */
                    268:                if (c == cc[VINTR] || c == cc[VQUIT]) {
                    269:                        if (!(lflag & NOFLSH))
                    270:                                tty_flush(tp, FREAD | FWRITE);
                    271:                        tty_echo(c, tp);
                    272:                        sig = (c == cc[VINTR]) ? SIGINT : SIGQUIT;
                    273:                        goto endcase;
                    274:                }
                    275:                /* suspend (^Z) */
                    276:                if (c == cc[VSUSP]) {
                    277:                        if (!(lflag & NOFLSH))
                    278:                                tty_flush(tp, FREAD | FWRITE);
                    279:                        tty_echo(c, tp);
                    280:                        sig = SIGTSTP;
                    281:                        goto endcase;
                    282:                }
                    283:        }
                    284:
                    285:        /*
                    286:         * Check for input buffer overflow
                    287:         */
                    288:        if (ttyq_full(&tp->t_rawq)) {
                    289:                tty_flush(tp, FREAD | FWRITE);
                    290:                goto endcase;
                    291:        }
                    292:        ttyq_putc(c, &tp->t_rawq);
                    293:
                    294:        if (lflag & ICANON) {
                    295:                if (c == '\n' || c == cc[VEOF] || c == cc[VEOL]) {
                    296:                        tty_catq(&tp->t_rawq, &tp->t_canq);
                    297:                        sched_wakeup(&tp->t_input_event);
                    298:                }
                    299:        } else
                    300:                sched_wakeup(&tp->t_input_event);
                    301:
                    302:        if (lflag & ECHO)
                    303:                tty_echo(c, tp);
                    304:  endcase:
                    305:        /*
                    306:         * IXANY means allow any character to restart output.
                    307:         */
                    308:        if ((tp->t_state & TS_TTSTOP) && (iflag & IXANY) == 0 &&
                    309:            cc[VSTART] != cc[VSTOP])
                    310:                return;
                    311:  restartoutput:
                    312:        tp->t_state &= ~TS_TTSTOP;
                    313:
                    314:        if (sig != -1) {
                    315:                if (sig_task)
                    316:                        exception_post(sig_task, sig);
                    317:        }
                    318:        tty_start(tp);
                    319: }
                    320:
                    321: /*
                    322:  * Output a single character on a tty, doing output processing
                    323:  * as needed (expanding tabs, newline processing, etc.).
                    324:  */
                    325: void
                    326: tty_output(int c, struct tty *tp)
                    327: {
                    328:        int i, col;
                    329:
                    330:        if ((tp->t_lflag & ICANON) == 0) {
                    331:                ttyq_putc(c, &tp->t_outq);
                    332:                return;
                    333:        }
                    334:        /* Expand tab */
                    335:        if (c == '\t' && (tp->t_oflag & OXTABS)) {
                    336:                i = 8 - (tp->t_column & 7);
                    337:                tp->t_column += i;
                    338:                do
                    339:                        ttyq_putc(' ', &tp->t_outq);
                    340:                while (--i > 0);
                    341:                return;
                    342:        }
                    343:        /* Translate newline into "\r\n" */
                    344:        if (c == '\n' && (tp->t_oflag & ONLCR))
                    345:                ttyq_putc('\r', &tp->t_outq);
                    346:
                    347:        ttyq_putc(c, &tp->t_outq);
                    348:
                    349:        col = tp->t_column;
                    350:        switch (c) {
                    351:        case '\b':      /* back space */
                    352:                if (col > 0)
                    353:                        --col;
                    354:                break;
                    355:        case '\t':      /* tab */
                    356:                col = (col + 8) & ~7;
                    357:                break;
                    358:        case '\n':      /* newline */
                    359:                col = 0;
                    360:                break;
                    361:        case '\r':      /* return */
                    362:                col = 0;
                    363:                break;
                    364:        default:
                    365:                if (!is_ctrl(c))
                    366:                    ++col;
                    367:                break;
                    368:        }
                    369:        tp->t_column = col;
                    370:        return;
                    371: }
                    372:
                    373: /*
                    374:  * Process a read call on a tty device.
                    375:  */
                    376: int
                    377: tty_read(struct tty *tp, char *buf, size_t *nbyte)
                    378: {
                    379:        unsigned char *cc;
                    380:        struct tty_queue *qp;
                    381:        int rc, c;
                    382:        size_t count = 0;
                    383:        tcflag_t lflag;
                    384:
                    385:        lflag = tp->t_lflag;
                    386:        cc = tp->t_cc;
                    387:        qp = (lflag & ICANON) ? &tp->t_canq : &tp->t_rawq;
                    388:
                    389:        /* If there is no input, wait it */
                    390:        while (ttyq_empty(qp)) {
                    391:                rc = sched_sleep(&tp->t_input_event);
                    392:                if (rc == SLP_INTR)
                    393:                        return EINTR;
                    394:        }
                    395:        while (count < *nbyte) {
                    396:                if ((c = ttyq_getc(qp)) == -1)
                    397:                        break;
                    398:                if (c == cc[VEOF] && (lflag & ICANON))
                    399:                        break;
                    400:                count++;
                    401:                if (umem_copyout(&c, buf, 1))
                    402:                        return EFAULT;
                    403:                if ((lflag & ICANON) && (c == '\n' || c == cc[VEOL]))
                    404:                        break;
                    405:                buf++;
                    406:        }
                    407:        *nbyte = count;
                    408:        return 0;
                    409: }
                    410:
                    411: /*
                    412:  * Process a write call on a tty device.
                    413:  */
                    414: int
                    415: tty_write(struct tty *tp, char *buf, size_t *nbyte)
                    416: {
                    417:        size_t remain, count = 0;
                    418:        int c;
                    419:
                    420:        remain = *nbyte;
                    421:        while (remain > 0) {
                    422:                if (tp->t_outq.tq_count >= TTYQ_HIWAT) {
                    423:                        tty_start(tp);
                    424:                        continue;
                    425:                }
                    426:                if (umem_copyin(buf, &c, 1))
                    427:                        return EFAULT;
                    428:                tty_output(c, tp);
                    429:                buf++;
                    430:                remain--;
                    431:                count++;
                    432:        }
                    433:        tty_start(tp);
                    434:        *nbyte = count;
                    435:        return 0;
                    436: }
                    437:
                    438: /*
                    439:  * Ioctls for all tty devices.
                    440:  */
                    441: int
                    442: tty_ioctl(struct tty *tp, u_long cmd, void *data)
                    443: {
                    444:        int flags;
                    445:
                    446:        switch (cmd) {
                    447:        case TIOCGETA:
                    448:                if (umem_copyout(&tp->t_termios, data,
                    449:                                 sizeof(struct termios)))
                    450:                        return EFAULT;
                    451:                break;
                    452:        case TIOCSETAW:
                    453:        case TIOCSETAF:
                    454:                tty_flush(tp, flags);
                    455:                /* FALLTHROUGH */
                    456:        case TIOCSETA:
                    457:                if (umem_copyin(data, &tp->t_termios,
                    458:                                 sizeof(struct termios)))
                    459:                        return EFAULT;
                    460:                break;
                    461:        case TIOCSPGRP:                 /* set pgrp of tty */
                    462:                if (umem_copyin(data, &tp->t_pgid, sizeof(pid_t)))
                    463:                        return EFAULT;
                    464:                break;
                    465:        case TIOCGPGRP:
                    466:                if (umem_copyout(&tp->t_pgid, data, sizeof(pid_t)))
                    467:                        return EFAULT;
                    468:                break;
                    469:        case TIOCFLUSH:
                    470:                if (umem_copyin(data, &flags, sizeof(flags)))
                    471:                        return EFAULT;
                    472:                break;
                    473:                if (flags == 0)
                    474:                        flags = FREAD | FWRITE;
                    475:                else
                    476:                        flags &= FREAD | FWRITE;
                    477:                tty_flush(tp, flags);
                    478:                break;
                    479:        case TIOCSTART:
                    480:                if (tp->t_state & TS_TTSTOP) {
                    481:                        tp->t_state &= ~TS_TTSTOP;
                    482:                        tty_start(tp);
                    483:                }
                    484:                break;
                    485:        case TIOCSTOP:
                    486:                if (!(tp->t_state & TS_TTSTOP)) {
                    487:                        tp->t_state |= TS_TTSTOP;
                    488:                }
                    489:                break;
                    490:        case TIOCGWINSZ:
                    491:                if (umem_copyout(&tp->t_winsize, data,
                    492:                                 sizeof(struct winsize)))
                    493:                        return EFAULT;
                    494:                break;
                    495:        case TIOCSWINSZ:
                    496:                if (umem_copyin(&tp->t_winsize, data,
                    497:                                 sizeof(struct winsize)))
                    498:                        return EFAULT;
                    499:                break;
                    500:        case TIOCSETSIGT:
                    501:                if (umem_copyin(data, &sig_task, sizeof(task_t)))
                    502:                        return EFAULT;
                    503:                break;
                    504:        }
                    505:        return 0;
                    506: }
                    507:
                    508: /*
                    509:  * Register tty device.
                    510:  */
                    511: int
                    512: tty_register(struct devio *io, struct tty *tp, void (*output)(struct tty*))
                    513: {
                    514:
                    515:        /* We support only one tty device */
                    516:        if (tty_dev != DEVICE_NULL)
                    517:                return -1;
                    518:
                    519:        /* Create TTY device as an alias of the registered device. */
                    520:        tty_dev = device_create(io, "tty", DF_CHR);
                    521:        if (tty_dev == DEVICE_NULL)
                    522:                return -1;
                    523:
                    524:        /* Initialize tty */
                    525:        memset(tp, 0, sizeof(struct tty));
                    526:        memcpy(&tp->t_termios.c_cc, ttydefchars, sizeof(ttydefchars));
                    527:        event_init(&tp->t_input_event, "TTY input");
                    528:        tp->t_iflag = TTYDEF_IFLAG;
                    529:        tp->t_oflag = TTYDEF_OFLAG;
                    530:        tp->t_cflag = TTYDEF_CFLAG;
                    531:        tp->t_lflag = TTYDEF_LFLAG;
                    532:        tp->t_ispeed = TTYDEF_SPEED;
                    533:        tp->t_ospeed = TTYDEF_SPEED;
                    534:        tp->t_output = output;
                    535:        return 0;
                    536: }
                    537:
                    538: /*
                    539:  * Init
                    540:  */
                    541: static int
                    542: tty_init(void)
                    543: {
                    544:
                    545:        return 0;
                    546: }

CVSweb