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