Annotation of prex-old/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/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