Project

General

Profile

Statistics
| Branch: | Revision:

root / prex-0.9.0 / bsp / drv / dev / base / tty.c @ 03e9c04a

History | View | Annotate | Download (12.6 KB)

1 03e9c04a Brad Neuman
/*-
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
/* Modified for Prex by Kohsuke Ohtani. */
38
39
/*
40
 * tty.c - TTY device helper
41
 */
42
43
#include <driver.h>
44
#include <sys/signal.h>
45
#include <sys/termios.h>
46
#include <tty.h>
47
#include <pm.h>
48
#include <kd.h>
49
50
/* #define DEBUG_TTY 1 */
51
52
#ifdef DEBUG_TTY
53
#define DPRINTF(a) printf a
54
#else
55
#define DPRINTF(a)
56
#endif
57
58
#define        FREAD                0x0001
59
#define        FWRITE                0x0002
60
61
static void tty_output(int c, struct tty *tp);
62
63
/* default control characters */
64
static const cc_t        ttydefchars[NCCS] = TTYDEFCHARS;
65
66
#define is_ctrl(c)   ((c) < 32 || (c) == 0x7f)
67
68
/*
69
 * TTY queue operations
70
 */
71
#define ttyq_next(i)        (((i) + 1) & (TTYQ_SIZE - 1))
72
#define ttyq_prev(i)        (((i) - 1) & (TTYQ_SIZE - 1))
73
#define ttyq_full(q)        ((q)->tq_count >= TTYQ_SIZE)
74
#define ttyq_empty(q)        ((q)->tq_count == 0)
75
76
/*
77
 * Get a character from a queue.
78
 */
79
int
80
tty_getc(struct tty_queue *tq)
81
{
82
        char c;
83
        int s;
84
85
        s = splhigh();
86
        if (ttyq_empty(tq)) {
87
                splx(s);
88
                return -1;
89
        }
90
        c = tq->tq_buf[tq->tq_head];
91
        tq->tq_head = ttyq_next(tq->tq_head);
92
        tq->tq_count--;
93
        splx(s);
94
        return (int)c;
95
}
96
97
/*
98
 * Put a character into a queue.
99
 */
100
static void
101
tty_putc(int c, struct tty_queue *tq)
102
{
103
        int s;
104
105
        s = splhigh();
106
        if (ttyq_full(tq)) {
107
                splx(s);
108
                return;
109
        }
110
        tq->tq_buf[tq->tq_tail] = (char)(c & 0xff);
111
        tq->tq_tail = ttyq_next(tq->tq_tail);
112
        tq->tq_count++;
113
        splx(s);
114
}
115
116
/*
117
 * Remove the last character in a queue and return it.
118
 */
119
static int
120
tty_unputc(struct tty_queue *tq)
121
{
122
        char c;
123
        int s;
124
125
        if (ttyq_empty(tq))
126
                return -1;
127
        s = splhigh();
128
        tq->tq_tail = ttyq_prev(tq->tq_tail);
129
        c = tq->tq_buf[tq->tq_tail];
130
        tq->tq_count--;
131
        splx(s);
132
        return (int)c;
133
}
134
135
/*
136
 * Put the chars in the from queue on the end of the to queue.
137
 */
138
static void
139
tty_catq(struct tty_queue *from, struct tty_queue *to)
140
{
141
        int c;
142
143
        while ((c = tty_getc(from)) != -1)
144
                tty_putc(c, to);
145
}
146
147
/*
148
 * Rubout one character from the rawq of tp
149
 */
150
static void
151
tty_rubout(int c, struct tty *tp)
152
{
153
154
        if (!(tp->t_lflag & ECHO))
155
                return;
156
        if (tp->t_lflag & ECHOE) {
157
                tty_output('\b', tp);
158
                tty_output(' ', tp);
159
                tty_output('\b', tp);
160
        } else
161
                tty_output(tp->t_cc[VERASE], tp);
162
}
163
164
/*
165
 * Echo char
166
 */
167
static void
168
tty_echo(int c, struct tty *tp)
169
{
170
171
        if (!(tp->t_lflag & ECHO)) {
172
                if (c == '\n' && (tp->t_lflag & ECHONL))
173
                        tty_output('\n', tp);
174
                return;
175
        }
176
        if (is_ctrl(c) && c != '\n' && c != '\t' && c != '\b') {
177
                tty_output('^', tp);
178
                tty_output(c + 'A' - 1, tp);
179
        } else
180
                tty_output(c, tp);
181
}
182
183
/*
184
 * Start output.
185
 */
186
static void
187
tty_start(struct tty *tp)
188
{
189
190
        DPRINTF(("tty_start\n"));
191
192
        if (tp->t_state & (TS_TTSTOP|TS_BUSY))
193
                return;
194
        if (tp->t_oproc)
195
                (*tp->t_oproc)(tp);
196
}
197
198
/*
199
 * Flush tty read and/or write queues, notifying anyone waiting.
200
 */
201
static void
202
tty_flush(struct tty *tp, int rw)
203
{
204
205
        DPRINTF(("tty_flush rw=%d\n", rw));
206
207
        if (rw & FREAD) {
208
                while (tty_getc(&tp->t_canq) != -1)
209
                        ;
210
                while (tty_getc(&tp->t_rawq) != -1)
211
                        ;
212
                sched_wakeup(&tp->t_input);
213
        }
214
        if (rw & FWRITE) {
215
                tp->t_state &= ~TS_TTSTOP;
216
                tty_start(tp);
217
        }
218
}
219
220
/*
221
 * Output is completed.
222
 */
223
void
224
tty_done(struct tty *tp)
225
{
226
227
        if (tp->t_outq.tq_count == 0)
228
                tp->t_state &= ~TS_BUSY;
229
        if (tp->t_state & TS_ASLEEP) {
230
                tp->t_state &= ~TS_ASLEEP;
231
                sched_wakeup(&tp->t_output);
232
        }
233
}
234
235
/*
236
 * Wait for output to drain.
237
 */
238
static void
239
tty_wait(struct tty *tp)
240
{
241
242
        /*        DPRINTF(("tty_wait\n")); */
243
244
        if ((tp->t_outq.tq_count > 0) && tp->t_oproc) {
245
                tp->t_state |= TS_BUSY;
246
                while (1) {
247
                        (*tp->t_oproc)(tp);
248
                        if ((tp->t_state & TS_BUSY) == 0)
249
                                break;
250
                        tp->t_state |= TS_ASLEEP;
251
                        sched_sleep(&tp->t_output);
252
                }
253
        }
254
}
255
256
/*
257
 * Send TTY signal at DPC level.
258
 */
259
static void
260
tty_signal(void *arg)
261
{
262
        struct tty *tp = arg;
263
264
        DPRINTF(("tty_signal sig=%d\n", tp->t_signo));
265
        exception_post(tp->t_sigtask, tp->t_signo);
266
}
267
268
/*
269
 * Process input of a single character received on a tty.
270
 * echo if required.
271
 * This may be called with interrupt level.
272
 */
273
void
274
tty_input(int c, struct tty *tp)
275
{
276
        unsigned char *cc;
277
        tcflag_t iflag, lflag;
278
        int sig = -1;
279
280
        DPRINTF(("tty_input: %d\n", c));
281
282
        /* Reload power management timer */
283
        pm_notify(PME_USER_ACTIVITY);
284
285
        lflag = tp->t_lflag;
286
        iflag = tp->t_iflag;
287
        cc = tp->t_cc;
288
289
#if defined(DEBUG) && defined(CONFIG_KD)
290
        if (c == cc[VDDB]) {
291
                kd_enter();
292
                tty_flush(tp, FREAD | FWRITE);
293
                goto endcase;
294
        }
295
#endif /* !CONFIG_KD*/
296
297
        /* IGNCR, ICRNL, INLCR */
298
        if (c == '\r') {
299
                if (iflag & IGNCR)
300
                        goto endcase;
301
                else if (iflag & ICRNL)
302
                        c = '\n';
303
        } else if (c == '\n' && (iflag & INLCR))
304
                c = '\r';
305
306
        if (iflag & IXON) {
307
                /* stop (^S) */
308
                if (c == cc[VSTOP]) {
309
                        if (!(tp->t_state & TS_TTSTOP)) {
310
                                tp->t_state |= TS_TTSTOP;
311
                                return;
312
                        }
313
                        if (c != cc[VSTART])
314
                                return;
315
                        /* if VSTART == VSTOP then toggle */
316
                        goto endcase;
317
                }
318
                /* start (^Q) */
319
                if (c == cc[VSTART])
320
                        goto restartoutput;
321
        }
322
        if (lflag & ICANON) {
323
                /* erase (^H / ^?) or backspace */
324
                if (c == cc[VERASE] || c == '\b') {
325
                        if (!ttyq_empty(&tp->t_rawq))
326
                                tty_rubout(tty_unputc(&tp->t_rawq), tp);
327
                        goto endcase;
328
                }
329
                /* kill (^U) */
330
                if (c == cc[VKILL]) {
331
                        while (!ttyq_empty(&tp->t_rawq))
332
                                tty_rubout(tty_unputc(&tp->t_rawq), tp);
333
                        goto endcase;
334
                }
335
        }
336
        if (lflag & ISIG) {
337
                /* quit (^C) */
338
                if (c == cc[VINTR] || c == cc[VQUIT]) {
339
                        if (!(lflag & NOFLSH)) {
340
                                tp->t_state |= TS_ISIG;
341
                                tty_flush(tp, FREAD | FWRITE);
342
                        }
343
                        tty_echo(c, tp);
344
                        sig = (c == cc[VINTR]) ? SIGINT : SIGQUIT;
345
                        goto endcase;
346
                }
347
                /* suspend (^Z) */
348
                if (c == cc[VSUSP]) {
349
                        if (!(lflag & NOFLSH)) {
350
                                tp->t_state |= TS_ISIG;
351
                                tty_flush(tp, FREAD | FWRITE);
352
                        }
353
                        tty_echo(c, tp);
354
                        sig = SIGTSTP;
355
                        goto endcase;
356
                }
357
        }
358
359
        /*
360
         * Check for input buffer overflow
361
         */
362
        if (ttyq_full(&tp->t_rawq)) {
363
                tty_flush(tp, FREAD | FWRITE);
364
                goto endcase;
365
        }
366
        tty_putc(c, &tp->t_rawq);
367
368
        if (lflag & ICANON) {
369
                if (c == '\n' || c == cc[VEOF] || c == cc[VEOL]) {
370
                        tty_catq(&tp->t_rawq, &tp->t_canq);
371
                        sched_wakeup(&tp->t_input);
372
                }
373
        } else
374
                sched_wakeup(&tp->t_input);
375
376
        if (lflag & ECHO)
377
                tty_echo(c, tp);
378
 endcase:
379
        /*
380
         * IXANY means allow any character to restart output.
381
         */
382
        if ((tp->t_state & TS_TTSTOP) && (iflag & IXANY) == 0 &&
383
            cc[VSTART] != cc[VSTOP])
384
                return;
385
 restartoutput:
386
        tp->t_state &= ~TS_TTSTOP;
387
388
        if (sig != -1) {
389
                if (tp->t_sigtask) {
390
                        tp->t_signo = sig;
391
                        sched_dpc(&tp->t_dpc, &tty_signal, tp);
392
                }
393
        }
394
        tty_start(tp);
395
}
396
397
/*
398
 * Output a single character on a tty, doing output processing
399
 * as needed (expanding tabs, newline processing, etc.).
400
 */
401
static void
402
tty_output(int c, struct tty *tp)
403
{
404
        int i, col;
405
406
        if ((tp->t_lflag & ICANON) == 0) {
407
                tty_putc(c, &tp->t_outq);
408
                return;
409
        }
410
        /* Expand tab */
411
        if (c == '\t' && (tp->t_oflag & OXTABS)) {
412
                i = 8 - (tp->t_column & 7);
413
                tp->t_column += i;
414
                do
415
                        tty_putc(' ', &tp->t_outq);
416
                while (--i > 0);
417
                return;
418
        }
419
        /* Translate newline into "\r\n" */
420
        if (c == '\n' && (tp->t_oflag & ONLCR))
421
                tty_putc('\r', &tp->t_outq);
422
423
        tty_putc(c, &tp->t_outq);
424
425
        col = tp->t_column;
426
        switch (c) {
427
        case '\b':        /* back space */
428
                if (col > 0)
429
                        --col;
430
                break;
431
        case '\t':        /* tab */
432
                col = (col + 8) & ~7;
433
                break;
434
        case '\n':        /* newline */
435
                col = 0;
436
                break;
437
        case '\r':        /* return */
438
                col = 0;
439
                break;
440
        default:
441
                if (!is_ctrl(c))
442
                    ++col;
443
                break;
444
        }
445
        tp->t_column = col;
446
        return;
447
}
448
449
/*
450
 * Process a read call on a tty device.
451
 */
452
int
453
tty_read(struct tty *tp, char *buf, size_t *nbyte)
454
{
455
        unsigned char *cc;
456
        struct tty_queue *qp;
457
        int rc, tmp;
458
        u_char c;
459
        size_t count = 0;
460
        tcflag_t lflag;
461
462
        DPRINTF(("tty_read\n"));
463
464
        lflag = tp->t_lflag;
465
        cc = tp->t_cc;
466
        qp = (lflag & ICANON) ? &tp->t_canq : &tp->t_rawq;
467
468
        /* If there is no input, wait it */
469
        while (ttyq_empty(qp)) {
470
                rc = sched_sleep(&tp->t_input);
471
                if (rc == SLP_INTR || tp->t_state & TS_ISIG) {
472
                        tp->t_state &= ~TS_ISIG;
473
                        return EINTR;
474
                }
475
        }
476
        while (count < *nbyte) {
477
                if ((tmp = tty_getc(qp)) == -1)
478
                        break;
479
                c = (u_char)tmp;
480
                if (c == cc[VEOF] && (lflag & ICANON))
481
                        break;
482
                count++;
483
                if (copyout(&c, buf, 1))
484
                        return EFAULT;
485
                if ((lflag & ICANON) && (c == '\n' || c == cc[VEOL]))
486
                        break;
487
                buf++;
488
        }
489
        *nbyte = count;
490
        return 0;
491
}
492
493
/*
494
 * Process a write call on a tty device.
495
 */
496
int
497
tty_write(struct tty *tp, char *buf, size_t *nbyte)
498
{
499
        size_t remain, count = 0;
500
        u_char c;
501
502
        DPRINTF(("tty_write\n"));
503
504
        remain = *nbyte;
505
        while (remain > 0) {
506
                if (tp->t_outq.tq_count > TTYQ_HIWAT) {
507
                        tty_start(tp);
508
                        if (tp->t_outq.tq_count <= TTYQ_HIWAT)
509
                                continue;
510
                        tp->t_state |= TS_ASLEEP;
511
                        sched_sleep(&tp->t_output);
512
                        continue;
513
                }
514
                if (copyin(buf, &c, 1))
515
                        return EFAULT;
516
                tty_output((int)c, tp);
517
                buf++;
518
                remain--;
519
                count++;
520
        }
521
        tty_start(tp);
522
        *nbyte = count;
523
        return 0;
524
}
525
526
/*
527
 * Ioctls for all tty devices.
528
 */
529
int
530
tty_ioctl(struct tty *tp, u_long cmd, void *data)
531
{
532
        int flags;
533
        struct tty_queue *qp;
534
535
        switch (cmd) {
536
        case TIOCGETA:
537
                if (copyout(&tp->t_termios, data,
538
                                 sizeof(struct termios)))
539
                        return EFAULT;
540
                break;
541
        case TIOCSETAW:
542
        case TIOCSETAF:
543
                tty_wait(tp);
544
                if (cmd == TIOCSETAF)
545
                        tty_flush(tp, FREAD);
546
                /* FALLTHROUGH */
547
        case TIOCSETA:
548
                if (copyin(data, &tp->t_termios,
549
                           sizeof(struct termios)))
550
                        return EFAULT;
551
                break;
552
        case TIOCSPGRP:                        /* set pgrp of tty */
553
                if (copyin(data, &tp->t_pgid, sizeof(pid_t)))
554
                        return EFAULT;
555
                break;
556
        case TIOCGPGRP:
557
                if (copyout(&tp->t_pgid, data, sizeof(pid_t)))
558
                        return EFAULT;
559
                break;
560
        case TIOCFLUSH:
561
                if (copyin(data, &flags, sizeof(flags)))
562
                        return EFAULT;
563
                if (flags == 0)
564
                        flags = FREAD | FWRITE;
565
                else
566
                        flags &= FREAD | FWRITE;
567
                tty_flush(tp, flags);
568
                break;
569
        case TIOCSTART:
570
                if (tp->t_state & TS_TTSTOP) {
571
                        tp->t_state &= ~TS_TTSTOP;
572
                        tty_start(tp);
573
                }
574
                break;
575
        case TIOCSTOP:
576
                if (!(tp->t_state & TS_TTSTOP)) {
577
                        tp->t_state |= TS_TTSTOP;
578
                }
579
                break;
580
        case TIOCGWINSZ:
581
                if (copyout(&tp->t_winsize, data,
582
                            sizeof(struct winsize)))
583
                        return EFAULT;
584
                break;
585
        case TIOCSWINSZ:
586
                if (copyin(data, &tp->t_winsize,
587
                           sizeof(struct winsize)))
588
                        return EFAULT;
589
                break;
590
        case TIOCSETSIGT:        /* Prex */
591
                if (copyin(data, &tp->t_sigtask, sizeof(task_t)))
592
                        return EFAULT;
593
                break;
594
        case TIOCINQ:
595
                qp = (tp->t_lflag & ICANON) ? &tp->t_canq : &tp->t_rawq;
596
                if (copyout(&qp->tq_count, data, sizeof(int)))
597
                        return EFAULT;
598
                break;
599
        case TIOCOUTQ:
600
                if (copyout(&tp->t_outq.tq_count, data, sizeof(int)))
601
                        return EFAULT;
602
                break;
603
        }
604
        return 0;
605
}
606
607
/*
608
 * Attach a tty to the tty list.
609
 */
610
void
611
tty_attach(struct tty *tp)
612
{
613
        struct bootinfo *bi;
614
615
        /* Initialize tty */
616
        memset(tp, 0, sizeof(struct tty));
617
        memcpy(&tp->t_termios.c_cc, ttydefchars, sizeof(ttydefchars));
618
619
        event_init(&tp->t_input, "TTY input");
620
        event_init(&tp->t_output, "TTY output");
621
622
        tp->t_iflag = TTYDEF_IFLAG;
623
        tp->t_oflag = TTYDEF_OFLAG;
624
        tp->t_cflag = TTYDEF_CFLAG;
625
        tp->t_lflag = TTYDEF_LFLAG;
626
        tp->t_ispeed = TTYDEF_SPEED;
627
        tp->t_ospeed = TTYDEF_SPEED;
628
629
        machine_bootinfo(&bi);
630
        tp->t_winsize.ws_row = (u_short)bi->video.text_y;
631
        tp->t_winsize.ws_col = (u_short)bi->video.text_x;
632
}