1 /* This file contains the terminal driver, both for the IBM console and regular
2 * ASCII terminals. It handles only the device-independent part of a TTY, the
3 * device dependent parts are in console.c, rs232.c, etc. This file contains
4 * two main entry points, tty_task() and tty_wakeup(), and several minor entry
5 * points for use by the device-dependent code.
6 *
7 * The device-independent part accepts "keyboard" input from the device-
8 * dependent part, performs input processing (special key interpretation),
9 * and sends the input to a process reading from the TTY. Output to a TTY
10 * is sent to the device-dependent code for output processing and "screen"
11 * display. Input processing is done by the device by calling 'in_process'
12 * on the input characters, output processing may be done by the device itself
13 * or by calling 'out_process'. The TTY takes care of input queuing, the
14 * device does the output queuing. If a device receives an external signal,
15 * like an interrupt, then it causes tty_wakeup() to be run by the CLOCK task
16 * to, you guessed it, wake up the TTY to check if input or output can
17 * continue.
18 *
19 * Changes:
20 * Jan 20, 2004 moved TTY driver to user-space (Jorrit N. Herder)
21 * Sep 20, 2004 local timer management/ sync alarms (Jorrit N. Herder)
22 * Jul 13, 2004 support for function key observers (Jorrit N. Herder)
23 */
24
25 #include <assert.h>
26 #include <minix/drivers.h>
27 #include <minix/driver.h>
28 #include <termios.h>
29 #include <sys/kbdio.h>
30 #include <sys/ttycom.h>
31 #include <sys/ttydefaults.h>
32 #include <sys/fcntl.h>
33 #include <signal.h>
34 #include <minix/keymap.h>
35 #include "tty.h"
36
37 #include <sys/time.h>
38 #include <sys/select.h>
39
40 unsigned long rs_irq_set = 0;
41
42 /* Address of a tty structure. */
43 #define tty_addr(line) (&tty_table[line])
44
45 /* Macros for magic tty types. */
46 #define isconsole(tp) ((tp) < tty_addr(NR_CONS))
47
48 /* Macros for magic tty structure pointers. */
49 #define FIRST_TTY tty_addr(0)
50 #define END_TTY tty_addr(sizeof(tty_table) / sizeof(tty_table[0]))
51
52 /* A device exists if at least its 'devread' function is defined. */
53 #define tty_active(tp) ((tp)->tty_devread != NULL)
54
55 /* RS232 lines or pseudo terminals can be completely configured out. */
56 #if NR_RS_LINES == 0
57 #define rs_init(tp) ((void) 0)
58 #endif
59
60 struct kmessages kmess;
61
62 static void tty_timed_out(int arg);
63 static void settimer(tty_t *tty_ptr, int enable);
64 static void in_transfer(tty_t *tp);
65 static int tty_echo(tty_t *tp, int ch);
66 static void rawecho(tty_t *tp, int ch);
67 static int back_over(tty_t *tp);
68 static void reprint(tty_t *tp);
69 static void dev_ioctl(tty_t *tp);
70 static void setattr(tty_t *tp);
71 static void tty_icancel(tty_t *tp);
72 static void tty_init(void);
73 static void do_new_kmess(void);
74 static void set_console_line(char term[CONS_ARG]);
75 static void set_kernel_color(char color[CONS_ARG]);
76 static void set_color(tty_t *tp, int color);
77 static void reset_color(tty_t *tp);
78
79 static int do_open(devminor_t minor, int access, endpoint_t user_endpt);
80 static int do_close(devminor_t minor);
81 static ssize_t do_read(devminor_t minor, u64_t position, endpoint_t endpt,
82 cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
83 static ssize_t do_write(devminor_t minor, u64_t position, endpoint_t endpt,
84 cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
85 static int do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
86 cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id);
87 static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
88 static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt);
89
90 static struct chardriver tty_tab = {
91 .cdr_open = do_open,
92 .cdr_close = do_close,
93 .cdr_read = do_read,
94 .cdr_write = do_write,
95 .cdr_ioctl = do_ioctl,
96 .cdr_cancel = do_cancel,
97 .cdr_select = do_select
98 };
99
100 /* Default attributes. */
101 static struct termios termios_defaults = {
102 .c_iflag = TTYDEF_IFLAG,
103 .c_oflag = TTYDEF_OFLAG,
104 .c_cflag = TTYDEF_CFLAG,
105 .c_lflag = TTYDEF_LFLAG,
106 .c_ispeed = TTYDEF_SPEED,
107 .c_ospeed = TTYDEF_SPEED,
108 .c_cc = {
109 [VEOF] = CEOF,
110 [VEOL] = CEOL,
111 [VERASE] = CERASE,
112 [VINTR] = CINTR,
113 [VKILL] = CKILL,
114 [VMIN] = CMIN,
115 [VQUIT] = CQUIT,
116 [VTIME] = CTIME,
117 [VSUSP] = CSUSP,
118 [VSTART] = CSTART,
119 [VSTOP] = CSTOP,
120 [VREPRINT] = CREPRINT,
121 [VLNEXT] = CLNEXT,
122 [VDISCARD] = CDISCARD,
123 [VSTATUS] = CSTATUS
124 }
125 };
126 static struct winsize winsize_defaults; /* = all zeroes */
127
128 /* Global variables for the TTY task (declared extern in tty.h). */
129 tty_t tty_table[NR_CONS+NR_RS_LINES];
130 int ccurrent; /* currently active console */
131 struct machine machine; /* kernel environment variables */
132 u32_t system_hz;
133 u32_t consoleline = CONS_MINOR;
134 u32_t kernel_msg_color = 0;
135
136 static const char lined[TTLINEDNAMELEN] = "termios"; /* line discipline */
137
138 /* SEF functions and variables. */
139 static void sef_local_startup(void);
140 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
141 static void sef_cb_signal_handler(int signo);
142
143 /*===========================================================================*
144 * tty_task *
145 *===========================================================================*/
main(void)146 int main(void)
147 {
148 /* Main routine of the terminal task. */
149
150 message tty_mess; /* buffer for all incoming messages */
151 int ipc_status;
152 int line;
153 int r;
154 register tty_t *tp;
155
156 /* SEF local startup. */
157 sef_local_startup();
158 while (TRUE) {
159 /* Check for and handle any events on any of the ttys. */
160 for (tp = FIRST_TTY; tp < END_TTY; tp++) {
161 if (tp->tty_events) handle_events(tp);
162 }
163
164 /* Get a request message. */
165 r= driver_receive(ANY, &tty_mess, &ipc_status);
166 if (r != 0)
167 panic("driver_receive failed with: %d", r);
168
169 /* First handle all kernel notification types that the TTY supports.
170 * - An alarm went off, expire all timers and handle the events.
171 * - A hardware interrupt also is an invitation to check for events.
172 * - A new kernel message is available for printing.
173 * - Reset the console on system shutdown.
174 * Then see if this message is different from a normal device driver
175 * request and should be handled separately. These extra functions
176 * do not operate on a device, in constrast to the driver requests.
177 */
178
179 if (is_ipc_notify(ipc_status)) {
180 switch (_ENDPOINT_P(tty_mess.m_source)) {
181 case CLOCK:
182 /* run watchdogs of expired timers */
183 expire_timers(tty_mess.m_notify.timestamp);
184 break;
185 case HARDWARE:
186 /* hardware interrupt notification */
187
188 #if NR_RS_LINES > 0
189 /* serial I/O */
190 if (tty_mess.m_notify.interrupts & rs_irq_set)
191 rs_interrupt(&tty_mess);
192 #endif
193 /* run watchdogs of expired timers */
194 expire_timers(tty_mess.m_notify.timestamp);
195 break;
196 default:
197 /* do nothing */
198 break;
199 }
200
201 /* done, get new message */
202 continue;
203 }
204
205 switch (tty_mess.m_type) {
206 case TTY_FKEY_CONTROL: /* (un)register a fkey observer */
207 do_fkey_ctl(&tty_mess);
208 continue;
209 case TTY_INPUT_UP:
210 case TTY_INPUT_EVENT:
211 do_input(&tty_mess);
212 continue;
213 default: /* should be a driver request */
214 ; /* do nothing; end switch */
215 }
216
217 if (!IS_CDEV_RQ(tty_mess.m_type)) {
218 chardriver_process(&tty_tab, &tty_mess, ipc_status);
219 continue;
220 }
221
222 /* Only device requests should get to this point.
223 * All requests have a minor device number.
224 */
225 if (OK != chardriver_get_minor(&tty_mess, &line))
226 continue;
227
228 if (line == VIDEO_MINOR) {
229 do_video(&tty_mess, ipc_status);
230 continue;
231 }
232
233 /* Execute the requested device driver function. */
234 chardriver_process(&tty_tab, &tty_mess, ipc_status);
235 }
236
237 return 0;
238 }
239
240 static void
set_color(tty_t * tp,int color)241 set_color(tty_t *tp, int color)
242 {
243 char buf[8];
244
245 buf[0] = '\033';
246 snprintf(&buf[1], sizeof(buf) - 1, "[1;%dm", color);
247 do_write(tp->tty_minor, 0, KERNEL, (cp_grant_id_t) buf, sizeof(buf),
248 CDEV_NONBLOCK, 0);
249 }
250
251 static void
reset_color(tty_t * tp)252 reset_color(tty_t *tp)
253 {
254 char buf[8];
255
256 #define SGR_COLOR_RESET 39
257 buf[0] = '\033';
258 snprintf(&buf[1], sizeof(buf) - 1, "[0;%dm", SGR_COLOR_RESET);
259 do_write(tp->tty_minor, 0, KERNEL, (cp_grant_id_t) buf, sizeof(buf),
260 CDEV_NONBLOCK, 0);
261 }
262
263 tty_t *
line2tty(devminor_t line)264 line2tty(devminor_t line)
265 {
266 /* Convert a terminal line to tty_table pointer */
267
268 tty_t* tp;
269
270 /* /dev/log goes to /dev/console, and both may be redirected. */
271 if (line == CONS_MINOR || line == LOG_MINOR)
272 line = consoleline;
273
274 if (line == VIDEO_MINOR) {
275 return(NULL);
276 } else if ((line - CONS_MINOR) < NR_CONS) {
277 tp = tty_addr(line - CONS_MINOR);
278 } else if ((line - RS232_MINOR) < NR_RS_LINES) {
279 tp = tty_addr(line - RS232_MINOR + NR_CONS);
280 } else {
281 tp = NULL;
282 }
283
284 if (tp != NULL && !tty_active(tp))
285 tp = NULL;
286
287 return(tp);
288 }
289
290 /*===========================================================================*
291 * sef_local_startup *
292 *===========================================================================*/
sef_local_startup()293 static void sef_local_startup()
294 {
295 /* Register init callbacks. */
296 sef_setcb_init_fresh(sef_cb_init_fresh);
297 sef_setcb_init_restart(SEF_CB_INIT_RESTART_STATEFUL);
298
299 /* Register signal callbacks. */
300 sef_setcb_signal_handler(sef_cb_signal_handler);
301
302 /* Let SEF perform startup. */
303 sef_startup();
304 }
305
306 /*===========================================================================*
307 * sef_cb_init_fresh *
308 *===========================================================================*/
sef_cb_init_fresh(int UNUSED (type),sef_init_info_t * UNUSED (info))309 static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
310 {
311 /* Initialize the tty driver. */
312 int r;
313 char val[CONS_ARG];
314
315 /* Get kernel environment (protected_mode, pc_at and ega are needed). */
316 if (OK != (r=sys_getmachine(&machine))) {
317 panic("Couldn't obtain kernel environment: %d", r);
318 }
319
320 if (env_get_param("console", val, sizeof(val)) == OK) {
321 set_console_line(val);
322 }
323
324 if ((r = env_get_param("kernelclr", val, sizeof(val))) == OK) {
325 set_kernel_color(val);
326 }
327
328 /* Initialize the TTY driver. */
329 tty_init();
330
331 /* Final one-time keyboard initialization. */
332 kb_init_once();
333
334 /* Register for diagnostics notifications. */
335 sys_diagctl_register();
336
337 return(OK);
338 }
339
340 static void
set_console_line(char term[CONS_ARG])341 set_console_line(char term[CONS_ARG])
342 {
343 /* Parse 'term' and redirect console output there. */
344 int i;
345
346 /* Console */
347 if (!strncmp(term, "console", CONS_ARG - 1)) {
348 consoleline = CONS_MINOR+0;
349 }
350
351 /* The other console terminals */
352 for (i = 1; i < NR_CONS; i++) {
353 char cons[6];
354 strlcpy(cons, "ttyc0", sizeof(cons));
355 cons[4] += i;
356 if (!strncmp(term, cons,
357 CONS_ARG < sizeof(cons) ? CONS_ARG-1 : sizeof(cons) - 1))
358 consoleline = CONS_MINOR + i;
359 }
360
361 /* Serial lines */
362 assert(NR_RS_LINES <= 9);/* below assumes this is the case */
363 for (i = 0; i < NR_RS_LINES; i++) {
364 char sercons[6];
365 strlcpy(sercons, "tty00", sizeof(sercons));
366 sercons[4] += i;
367 if (!strncmp(term, sercons,
368 CONS_ARG < sizeof(sercons) ? CONS_ARG-1:sizeof(sercons)-1))
369 consoleline = RS232_MINOR + i;
370 }
371 }
372
373 static void
set_kernel_color(char color[CONS_ARG])374 set_kernel_color(char color[CONS_ARG])
375 {
376 int def_color;
377
378 #define SGR_COLOR_START 30
379 #define SGR_COLOR_END 37
380
381 def_color = atoi(color);
382 if ((SGR_COLOR_START + def_color) >= SGR_COLOR_START &&
383 (SGR_COLOR_START + def_color) <= SGR_COLOR_END) {
384 kernel_msg_color = def_color + SGR_COLOR_START;
385 }
386 }
387
388 static void
do_new_kmess(void)389 do_new_kmess(void)
390 {
391 /* Kernel wants to print a new message */
392 struct kmessages *kmess_ptr; /* kmessages structure */
393 char kernel_buf_copy[_KMESS_BUF_SIZE];
394 static int prev_next = 0;
395 int next, bytes, copy, restore = 0;
396 tty_t *tp, rtp;
397
398 kmess_ptr = get_minix_kerninfo()->kmessages;
399
400 /* The kernel buffer is circular; print only the new part. Determine
401 * how many new bytes there are with the help of current and
402 * previous 'next' index. This works fine if less than _KMESS_BUF_SIZE
403 * bytes is new data; else we miss % _KMESS_BUF_SIZE here. Obtain
404 * 'next' only once, since we are operating on shared memory here.
405 * Check for size being positive; the buffer might as well be emptied!
406 */
407 next = kmess_ptr->km_next;
408 bytes = ((next + _KMESS_BUF_SIZE) - prev_next) % _KMESS_BUF_SIZE;
409 if (bytes > 0) {
410 /* Copy from current position toward end of buffer */
411 copy = MIN(_KMESS_BUF_SIZE - prev_next, bytes);
412 memcpy(kernel_buf_copy, &kmess_ptr->km_buf[prev_next], copy);
413
414 /* Copy remainder from start of buffer */
415 if (copy < bytes) {
416 memcpy(&kernel_buf_copy[copy], &kmess_ptr->km_buf[0],
417 bytes - copy);
418 }
419
420 tp = line2tty(consoleline);
421 if (tp == NULL)
422 panic("Don't know where to send kernel messages");
423 if (tp->tty_outleft > 0) {
424 /* Terminal is already printing */
425 rtp = *tp; /* Make backup */
426 tp->tty_outleft = 0; /* So do_write is happy */
427 restore = 1;
428 }
429
430 if (kernel_msg_color != 0)
431 set_color(tp, kernel_msg_color);
432 do_write(tp->tty_minor, 0, KERNEL,
433 (cp_grant_id_t) kernel_buf_copy, bytes,
434 CDEV_NONBLOCK, 0);
435 if (kernel_msg_color != 0)
436 reset_color(tp);
437 if (restore) {
438 *tp = rtp;
439 }
440 }
441
442 /* Store 'next' pointer so that we can determine what part of the
443 * kernel messages buffer to print next time a notification arrives.
444 */
445 prev_next = next;
446 }
447
448 /*===========================================================================*
449 * sef_cb_signal_handler *
450 *===========================================================================*/
sef_cb_signal_handler(int signo)451 static void sef_cb_signal_handler(int signo)
452 {
453 /* Check for known signals, ignore anything else. */
454 switch(signo) {
455 /* There is a pending message from the kernel. */
456 case SIGKMESS:
457 do_new_kmess();
458 break;
459 /* Switch to primary console on termination. */
460 case SIGTERM:
461 cons_stop();
462 break;
463 }
464 }
465
466 /*===========================================================================*
467 * do_read *
468 *===========================================================================*/
do_read(devminor_t minor,u64_t UNUSED (position),endpoint_t endpt,cp_grant_id_t grant,size_t size,int flags,cdev_id_t id)469 static ssize_t do_read(devminor_t minor, u64_t UNUSED(position),
470 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
471 cdev_id_t id)
472 {
473 /* A process wants to read from a terminal. */
474 tty_t *tp;
475 int r;
476
477 if ((tp = line2tty(minor)) == NULL)
478 return ENXIO;
479
480 /* Check if there is already a process hanging in a read, check if the
481 * parameters are correct, do I/O.
482 */
483 if (tp->tty_incaller != NONE || tp->tty_inleft > 0)
484 return EIO;
485 if (size <= 0)
486 return EINVAL;
487
488 /* Copy information from the message to the tty struct. */
489 tp->tty_incaller = endpt;
490 tp->tty_inid = id;
491 tp->tty_ingrant = grant;
492 assert(tp->tty_incum == 0);
493 tp->tty_inleft = size;
494
495 if (!(tp->tty_termios.c_lflag & ICANON) && tp->tty_termios.c_cc[VTIME] > 0) {
496 if (tp->tty_termios.c_cc[VMIN] == 0) {
497 /* MIN & TIME specify a read timer that finishes the
498 * read in TIME/10 seconds if no bytes are available.
499 */
500 settimer(tp, TRUE);
501 tp->tty_min = 1;
502 } else {
503 /* MIN & TIME specify an inter-byte timer that may
504 * have to be cancelled if there are no bytes yet.
505 */
506 if (tp->tty_eotct == 0) {
507 settimer(tp, FALSE);
508 tp->tty_min = tp->tty_termios.c_cc[VMIN];
509 }
510 }
511 }
512
513 /* Anything waiting in the input buffer? Clear it out... */
514 in_transfer(tp);
515 /* ...then go back for more. */
516 handle_events(tp);
517 if (tp->tty_inleft == 0)
518 return EDONTREPLY; /* already done */
519
520 /* There were no bytes in the input queue available. */
521 if (flags & CDEV_NONBLOCK) {
522 tty_icancel(tp);
523 r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN;
524 tp->tty_inleft = tp->tty_incum = 0;
525 tp->tty_incaller = NONE;
526 return r;
527 }
528
529 if (tp->tty_select_ops)
530 select_retry(tp);
531
532 return EDONTREPLY; /* suspend the caller */
533 }
534
535 /*===========================================================================*
536 * do_write *
537 *===========================================================================*/
do_write(devminor_t minor,u64_t UNUSED (position),endpoint_t endpt,cp_grant_id_t grant,size_t size,int flags,cdev_id_t id)538 static ssize_t do_write(devminor_t minor, u64_t UNUSED(position),
539 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
540 cdev_id_t id)
541 {
542 /* A process wants to write on a terminal. */
543 tty_t *tp;
544 int r;
545
546 if ((tp = line2tty(minor)) == NULL)
547 return ENXIO;
548
549 /* Check if there is already a process hanging in a write, check if the
550 * parameters are correct, do I/O.
551 */
552 if (tp->tty_outcaller != NONE || tp->tty_outleft > 0)
553 return EIO;
554 if (size <= 0)
555 return EINVAL;
556
557 /* Copy message parameters to the tty structure. */
558 tp->tty_outcaller = endpt;
559 tp->tty_outid = id;
560 tp->tty_outgrant = grant;
561 assert(tp->tty_outcum == 0);
562 tp->tty_outleft = size;
563
564 /* Try to write. */
565 handle_events(tp);
566 if (tp->tty_outleft == 0)
567 return EDONTREPLY; /* already done */
568
569 /* None or not all the bytes could be written. */
570 if (flags & CDEV_NONBLOCK) {
571 r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN;
572 tp->tty_outleft = tp->tty_outcum = 0;
573 tp->tty_outcaller = NONE;
574 return r;
575 }
576
577 if (tp->tty_select_ops)
578 select_retry(tp);
579
580 return EDONTREPLY; /* suspend the caller */
581 }
582
583 /*===========================================================================*
584 * do_ioctl *
585 *===========================================================================*/
do_ioctl(devminor_t minor,unsigned long request,endpoint_t endpt,cp_grant_id_t grant,int flags,endpoint_t user_endpt,cdev_id_t id)586 static int do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
587 cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id)
588 {
589 /* Perform an IOCTL on this terminal. POSIX termios calls are handled
590 * by the IOCTL system call.
591 */
592 kio_bell_t bell;
593 clock_t ticks;
594 tty_t *tp;
595 int i, r;
596
597 if ((tp = line2tty(minor)) == NULL)
598 return ENXIO;
599
600 r = OK;
601 switch (request) {
602 case TIOCGETA:
603 /* Get the termios attributes. */
604 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_termios,
605 sizeof(struct termios));
606 break;
607
608 case TIOCSETAW:
609 case TIOCSETAF:
610 case TIOCDRAIN:
611 if (tp->tty_outleft > 0) {
612 if (flags & CDEV_NONBLOCK)
613 return EAGAIN;
614 /* Wait for all ongoing output processing to finish. */
615 tp->tty_iocaller = endpt;
616 tp->tty_ioid = id;
617 tp->tty_ioreq = request;
618 tp->tty_iogrant = grant;
619 return EDONTREPLY; /* suspend the caller */
620 }
621 if (request == TIOCDRAIN) break;
622 if (request == TIOCSETAF) tty_icancel(tp);
623 /*FALL THROUGH*/
624 case TIOCSETA:
625 /* Set the termios attributes. */
626 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_termios,
627 sizeof(struct termios));
628 if (r != OK) break;
629 setattr(tp);
630 break;
631
632 case TIOCFLUSH:
633 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &i, sizeof(i));
634 if (r != OK) break;
635 if(i & FREAD) { tty_icancel(tp); }
636 if(i & FWRITE) { (*tp->tty_ocancel)(tp, 0); }
637 break;
638 case TIOCSTART:
639 tp->tty_inhibited = 0;
640 tp->tty_events = 1;
641 break;
642 case TIOCSTOP:
643 tp->tty_inhibited = 1;
644 tp->tty_events = 1;
645 break;
646 case TIOCSBRK: /* tcsendbreak - turn break on */
647 if (tp->tty_break_on != NULL) (*tp->tty_break_on)(tp,0);
648 break;
649 case TIOCCBRK: /* tcsendbreak - turn break off */
650 if (tp->tty_break_off != NULL) (*tp->tty_break_off)(tp,0);
651 break;
652
653 case TIOCGWINSZ:
654 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_winsize,
655 sizeof(struct winsize));
656 break;
657
658 case TIOCSWINSZ:
659 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_winsize,
660 sizeof(struct winsize));
661 sigchar(tp, SIGWINCH, 0);
662 break;
663 case KIOCBELL:
664 /* Sound bell (only /dev/console). */
665 if (!isconsole(tp))
666 break;
667 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &bell, sizeof(bell));
668 if (r != OK)
669 break;
670 ticks = bell.kb_duration.tv_usec * system_hz / 1000000;
671 ticks += bell.kb_duration.tv_sec * system_hz;
672 if (!ticks)
673 ticks++;
674 beep_x(bell.kb_pitch, ticks);
675 break;
676 case TIOCGETD: /* get line discipline */
677 i = TTYDISC;
678 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &i, sizeof(i));
679 break;
680 case TIOCSETD: /* set line discipline */
681 printf("TTY: TIOCSETD: can't set any other line discipline.\n");
682 r = ENOTTY;
683 break;
684 case TIOCGLINED: /* get line discipline as string */
685 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) lined, sizeof(lined));
686 break;
687 case TIOCGQSIZE: /* get input/output queue sizes */
688 i = TTY_IN_BYTES; /* best we can do.. */
689 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &i, sizeof(i));
690 break;
691 case KIOCSMAP:
692 /* Load a new keymap (only /dev/console). */
693 if (isconsole(tp)) r = kbd_loadmap(endpt, grant);
694 break;
695
696 case TIOCSFON:
697 /* Load a font into an EGA or VGA card (hs@hck.hr) */
698 if (isconsole(tp)) r = con_loadfont(endpt, grant);
699 break;
700
701 case TIOCSCTTY:
702 /* Process sets this tty as its controlling tty */
703 tp->tty_pgrp = user_endpt;
704 break;
705
706 /* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is
707 * not defined.
708 */
709 case TIOCGPGRP:
710 case TIOCSPGRP:
711 default:
712 r = ENOTTY;
713 }
714
715 return r;
716 }
717
718 /*===========================================================================*
719 * do_open *
720 *===========================================================================*/
do_open(devminor_t minor,int access,endpoint_t user_endpt)721 static int do_open(devminor_t minor, int access, endpoint_t user_endpt)
722 {
723 /* A tty line has been opened. Make it the callers controlling tty if
724 * CDEV_NOCTTY is *not* set and it is not the log device. CDEV_CTTY is returned
725 * if the tty is made the controlling tty, otherwise OK or an error code.
726 */
727 tty_t *tp;
728 int r = OK;
729
730 if ((tp = line2tty(minor)) == NULL)
731 return ENXIO;
732
733 if (minor == LOG_MINOR && isconsole(tp)) {
734 /* The log device is a write-only diagnostics device. */
735 if (access & CDEV_R_BIT) return EACCES;
736 } else {
737 if (!(access & CDEV_NOCTTY)) {
738 tp->tty_pgrp = user_endpt;
739 r = CDEV_CTTY;
740 }
741 tp->tty_openct++;
742 if (tp->tty_openct == 1) {
743 /* Tell the device that the tty is opened */
744 (*tp->tty_open)(tp, 0);
745 }
746 }
747
748 return r;
749 }
750
751 /*===========================================================================*
752 * do_close *
753 *===========================================================================*/
do_close(devminor_t minor)754 static int do_close(devminor_t minor)
755 {
756 /* A tty line has been closed. Clean up the line if it is the last close. */
757 tty_t *tp;
758
759 if ((tp = line2tty(minor)) == NULL)
760 return ENXIO;
761
762 if ((minor != LOG_MINOR || !isconsole(tp)) && --tp->tty_openct == 0) {
763 tp->tty_pgrp = 0;
764 tty_icancel(tp);
765 (*tp->tty_ocancel)(tp, 0);
766 (*tp->tty_close)(tp, 0);
767 tp->tty_termios = termios_defaults;
768 tp->tty_winsize = winsize_defaults;
769 setattr(tp);
770 }
771
772 return OK;
773 }
774
775 /*===========================================================================*
776 * do_cancel *
777 *===========================================================================*/
do_cancel(devminor_t minor,endpoint_t endpt,cdev_id_t id)778 static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
779 {
780 /* A signal has been sent to a process that is hanging trying to read or write.
781 * The pending read or write must be finished off immediately.
782 */
783 tty_t *tp;
784 int r;
785
786 if ((tp = line2tty(minor)) == NULL)
787 return ENXIO;
788
789 /* Check the parameters carefully, to avoid cancelling twice. */
790 r = EDONTREPLY;
791 if (tp->tty_inleft != 0 && endpt == tp->tty_incaller && id == tp->tty_inid) {
792 /* Process was reading when killed. Clean up input. */
793 tty_icancel(tp);
794 r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN;
795 tp->tty_inleft = tp->tty_incum = 0;
796 tp->tty_incaller = NONE;
797 } else if (tp->tty_outleft != 0 && endpt == tp->tty_outcaller &&
798 id == tp->tty_outid) {
799 /* Process was writing when killed. Clean up output. */
800 r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN;
801 tp->tty_outleft = tp->tty_outcum = 0;
802 tp->tty_outcaller = NONE;
803 } else if (tp->tty_ioreq != 0 && endpt == tp->tty_iocaller &&
804 id == tp->tty_ioid) {
805 /* Process was waiting for output to drain. */
806 r = EINTR;
807 tp->tty_ioreq = 0;
808 tp->tty_iocaller = NONE;
809 }
810 if (r != EDONTREPLY)
811 tp->tty_events = 1;
812 /* Only reply if we found a matching request. */
813 return r;
814 }
815
select_try(struct tty * tp,int ops)816 int select_try(struct tty *tp, int ops)
817 {
818 int ready_ops = 0;
819
820 /* Special case. If line is hung up, no operations will block.
821 * (and it can be seen as an exceptional condition.)
822 */
823 if (tp->tty_termios.c_ospeed == B0) {
824 ready_ops |= ops;
825 }
826
827 if (ops & CDEV_OP_RD) {
828 /* will i/o not block on read? */
829 if (tp->tty_inleft > 0) {
830 ready_ops |= CDEV_OP_RD; /* EIO - no blocking */
831 } else if (tp->tty_incount > 0) {
832 /* Is a regular read possible? tty_incount
833 * says there is data. But a read will only succeed
834 * in canonical mode if a newline has been seen.
835 */
836 if (!(tp->tty_termios.c_lflag & ICANON) ||
837 tp->tty_eotct > 0) {
838 ready_ops |= CDEV_OP_RD;
839 }
840 }
841 }
842
843 if (ops & CDEV_OP_WR) {
844 if (tp->tty_outleft > 0) ready_ops |= CDEV_OP_WR;
845 else if ((*tp->tty_devwrite)(tp, 1)) ready_ops |= CDEV_OP_WR;
846 }
847 return ready_ops;
848 }
849
select_retry(struct tty * tp)850 int select_retry(struct tty *tp)
851 {
852 int ops;
853
854 if (tp->tty_select_ops && (ops = select_try(tp, tp->tty_select_ops))) {
855 chardriver_reply_select(tp->tty_select_proc,
856 tp->tty_select_minor, ops);
857 tp->tty_select_ops &= ~ops;
858 }
859 return OK;
860 }
861
862 /*===========================================================================*
863 * do_select *
864 *===========================================================================*/
do_select(devminor_t minor,unsigned int ops,endpoint_t endpt)865 static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt)
866 {
867 tty_t *tp;
868 int ready_ops, watch;
869
870 if ((tp = line2tty(minor)) == NULL)
871 return ENXIO;
872
873 watch = (ops & CDEV_NOTIFY);
874 ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR);
875
876 ready_ops = select_try(tp, ops);
877
878 ops &= ~ready_ops;
879 if (ops && watch) {
880 /* Translated minor numbers are a problem with late select replies. We
881 * have to save the minor number used to do the select, since otherwise
882 * VFS won't be able to make sense of those late replies. We do not
883 * support selecting on two different minors for the same object.
884 */
885 if (tp->tty_select_ops != 0 && tp->tty_select_minor != minor) {
886 printf("TTY: select on one object with two minors (%d, %d)\n",
887 tp->tty_select_minor, minor);
888 return EBADF;
889 }
890 tp->tty_select_ops |= ops;
891 tp->tty_select_proc = endpt;
892 tp->tty_select_minor = minor;
893 }
894
895 return ready_ops;
896 }
897
898 /*===========================================================================*
899 * handle_events *
900 *===========================================================================*/
handle_events(tp)901 void handle_events(tp)
902 tty_t *tp; /* TTY to check for events. */
903 {
904 /* Handle any events pending on a TTY. These events are usually device
905 * interrupts.
906 *
907 * Two kinds of events are prominent:
908 * - a character has been received from the console or an RS232 line.
909 * - an RS232 line has completed a write request (on behalf of a user).
910 * The interrupt handler may delay the interrupt message at its discretion
911 * to avoid swamping the TTY task. Messages may be overwritten when the
912 * lines are fast or when there are races between different lines, input
913 * and output, because MINIX only provides single buffering for interrupt
914 * messages. This is handled by explicitly checking each line for fresh input
915 * and completed output on each interrupt.
916 */
917
918 do {
919 tp->tty_events = 0;
920
921 /* Read input and perform input processing. */
922 (*tp->tty_devread)(tp, 0);
923
924 /* Perform output processing and write output. */
925 (*tp->tty_devwrite)(tp, 0);
926
927 /* Ioctl waiting for some event? */
928 if (tp->tty_ioreq != 0) dev_ioctl(tp);
929 } while (tp->tty_events);
930
931 /* Transfer characters from the input queue to a waiting process. */
932 in_transfer(tp);
933
934 /* Reply if enough bytes are available. */
935 if (tp->tty_incum >= tp->tty_min && tp->tty_inleft > 0) {
936 chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum);
937 tp->tty_inleft = tp->tty_incum = 0;
938 tp->tty_incaller = NONE;
939 }
940 if (tp->tty_select_ops)
941 {
942 select_retry(tp);
943 }
944 }
945
946 /*===========================================================================*
947 * in_transfer *
948 *===========================================================================*/
in_transfer(tp)949 static void in_transfer(tp)
950 register tty_t *tp; /* pointer to terminal to read from */
951 {
952 /* Transfer bytes from the input queue to a process reading from a terminal. */
953
954 int ch;
955 int count;
956 char buf[64], *bp;
957
958 /* Force read to succeed if the line is hung up, looks like EOF to reader. */
959 if (tp->tty_termios.c_ospeed == B0) tp->tty_min = 0;
960
961 /* Anything to do? */
962 if (tp->tty_inleft == 0 || tp->tty_eotct < tp->tty_min) return;
963
964 bp = buf;
965 while (tp->tty_inleft > 0 && tp->tty_eotct > 0) {
966 ch = *tp->tty_intail;
967
968 if (!(ch & IN_EOF)) {
969 /* One character to be delivered to the user. */
970 *bp = ch & IN_CHAR;
971 tp->tty_inleft--;
972 if (++bp == bufend(buf)) {
973 /* Temp buffer full, copy to user space. */
974 sys_safecopyto(tp->tty_incaller,
975 tp->tty_ingrant, tp->tty_incum,
976 (vir_bytes) buf, (vir_bytes) buflen(buf));
977 tp->tty_incum += buflen(buf);
978 bp = buf;
979 }
980 }
981
982 /* Remove the character from the input queue. */
983 if (++tp->tty_intail == bufend(tp->tty_inbuf))
984 tp->tty_intail = tp->tty_inbuf;
985 tp->tty_incount--;
986 if (ch & IN_EOT) {
987 tp->tty_eotct--;
988 /* Don't read past a line break in canonical mode. */
989 if (tp->tty_termios.c_lflag & ICANON) tp->tty_inleft = 0;
990 }
991 }
992
993 if (bp > buf) {
994 /* Leftover characters in the buffer. */
995 count = bp - buf;
996 sys_safecopyto(tp->tty_incaller, tp->tty_ingrant, tp->tty_incum,
997 (vir_bytes) buf, (vir_bytes) count);
998 tp->tty_incum += count;
999 }
1000
1001 /* Usually reply to the reader, possibly even if incum == 0 (EOF). */
1002 if (tp->tty_inleft == 0) {
1003 chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum);
1004 tp->tty_inleft = tp->tty_incum = 0;
1005 tp->tty_incaller = NONE;
1006 }
1007 }
1008
1009 /*===========================================================================*
1010 * in_process *
1011 *===========================================================================*/
in_process(tp,buf,count)1012 int in_process(tp, buf, count)
1013 register tty_t *tp; /* terminal on which character has arrived */
1014 char *buf; /* buffer with input characters */
1015 int count; /* number of input characters */
1016 {
1017 /* Characters have just been typed in. Process, save, and echo them. Return
1018 * the number of characters processed.
1019 */
1020
1021 int ch, sig, ct;
1022 int timeset = FALSE;
1023
1024 for (ct = 0; ct < count; ct++) {
1025 /* Take one character. */
1026 ch = *buf++ & BYTE;
1027
1028 /* Strip to seven bits? */
1029 if (tp->tty_termios.c_iflag & ISTRIP) ch &= 0x7F;
1030
1031 /* Input extensions? */
1032 if (tp->tty_termios.c_lflag & IEXTEN) {
1033
1034 /* Previous character was a character escape? */
1035 if (tp->tty_escaped) {
1036 tp->tty_escaped = NOT_ESCAPED;
1037 ch |= IN_ESC; /* protect character */
1038 }
1039
1040 /* LNEXT (^V) to escape the next character? */
1041 if (ch == tp->tty_termios.c_cc[VLNEXT]) {
1042 tp->tty_escaped = ESCAPED;
1043 rawecho(tp, '^');
1044 rawecho(tp, '\b');
1045 continue; /* do not store the escape */
1046 }
1047
1048 /* REPRINT (^R) to reprint echoed characters? */
1049 if (ch == tp->tty_termios.c_cc[VREPRINT]) {
1050 reprint(tp);
1051 continue;
1052 }
1053 }
1054
1055 /* _POSIX_VDISABLE is a normal character value, so better escape it. */
1056 if (ch == _POSIX_VDISABLE) ch |= IN_ESC;
1057
1058 /* Map CR to LF, ignore CR, or map LF to CR. */
1059 if (ch == '\r') {
1060 if (tp->tty_termios.c_iflag & IGNCR) continue;
1061 if (tp->tty_termios.c_iflag & ICRNL) ch = '\n';
1062 } else
1063 if (ch == '\n') {
1064 if (tp->tty_termios.c_iflag & INLCR) ch = '\r';
1065 }
1066
1067 /* Canonical mode? */
1068 if (tp->tty_termios.c_lflag & ICANON) {
1069
1070 /* Erase processing (rub out of last character). */
1071 if (ch == tp->tty_termios.c_cc[VERASE]) {
1072 (void) back_over(tp);
1073 if (!(tp->tty_termios.c_lflag & ECHOE)) {
1074 (void) tty_echo(tp, ch);
1075 }
1076 continue;
1077 }
1078
1079 /* Kill processing (remove current line). */
1080 if (ch == tp->tty_termios.c_cc[VKILL]) {
1081 while (back_over(tp)) {}
1082 if (!(tp->tty_termios.c_lflag & ECHOE)) {
1083 (void) tty_echo(tp, ch);
1084 if (tp->tty_termios.c_lflag & ECHOK)
1085 rawecho(tp, '\n');
1086 }
1087 continue;
1088 }
1089
1090 /* EOF (^D) means end-of-file, an invisible "line break". */
1091 if (ch == tp->tty_termios.c_cc[VEOF]) ch |= IN_EOT | IN_EOF;
1092
1093 /* The line may be returned to the user after an LF. */
1094 if (ch == '\n') ch |= IN_EOT;
1095
1096 /* Same thing with EOL, whatever it may be. */
1097 if (ch == tp->tty_termios.c_cc[VEOL]) ch |= IN_EOT;
1098 }
1099
1100 /* Start/stop input control? */
1101 if (tp->tty_termios.c_iflag & IXON) {
1102
1103 /* Output stops on STOP (^S). */
1104 if (ch == tp->tty_termios.c_cc[VSTOP]) {
1105 tp->tty_inhibited = STOPPED;
1106 tp->tty_events = 1;
1107 continue;
1108 }
1109
1110 /* Output restarts on START (^Q) or any character if IXANY. */
1111 if (tp->tty_inhibited) {
1112 if (ch == tp->tty_termios.c_cc[VSTART]
1113 || (tp->tty_termios.c_iflag & IXANY)) {
1114 tp->tty_inhibited = RUNNING;
1115 tp->tty_events = 1;
1116 if (ch == tp->tty_termios.c_cc[VSTART])
1117 continue;
1118 }
1119 }
1120 }
1121
1122 if (tp->tty_termios.c_lflag & ISIG) {
1123 /* Check for INTR, QUIT and STATUS characters. */
1124 int sig = -1;
1125 if (ch == tp->tty_termios.c_cc[VINTR])
1126 sig = SIGINT;
1127 else if(ch == tp->tty_termios.c_cc[VQUIT])
1128 sig = SIGQUIT;
1129 else if(ch == tp->tty_termios.c_cc[VSTATUS])
1130 sig = SIGINFO;
1131
1132 if(sig >= 0) {
1133 sigchar(tp, sig, 1);
1134 (void) tty_echo(tp, ch);
1135 continue;
1136 }
1137 }
1138
1139 /* Is there space in the input buffer? */
1140 if (tp->tty_incount == buflen(tp->tty_inbuf)) {
1141 /* No space; discard in canonical mode, keep in raw mode. */
1142 if (tp->tty_termios.c_lflag & ICANON) continue;
1143 break;
1144 }
1145
1146 if (!(tp->tty_termios.c_lflag & ICANON)) {
1147 /* In raw mode all characters are "line breaks". */
1148 ch |= IN_EOT;
1149
1150 /* Start an inter-byte timer? */
1151 if (!timeset && tp->tty_termios.c_cc[VMIN] > 0
1152 && tp->tty_termios.c_cc[VTIME] > 0) {
1153 settimer(tp, TRUE);
1154 timeset = TRUE;
1155 }
1156 }
1157
1158 /* Perform the intricate function of echoing. */
1159 if (tp->tty_termios.c_lflag & (ECHO|ECHONL)) ch = tty_echo(tp, ch);
1160
1161 /* Save the character in the input queue. */
1162 *tp->tty_inhead++ = ch;
1163 if (tp->tty_inhead == bufend(tp->tty_inbuf))
1164 tp->tty_inhead = tp->tty_inbuf;
1165 tp->tty_incount++;
1166 if (ch & IN_EOT) tp->tty_eotct++;
1167
1168 /* Try to finish input if the queue threatens to overflow. */
1169 if (tp->tty_incount == buflen(tp->tty_inbuf)) in_transfer(tp);
1170 }
1171 return ct;
1172 }
1173
1174 /*===========================================================================*
1175 * echo *
1176 *===========================================================================*/
tty_echo(tp,ch)1177 static int tty_echo(tp, ch)
1178 register tty_t *tp; /* terminal on which to echo */
1179 register int ch; /* pointer to character to echo */
1180 {
1181 /* Echo the character if echoing is on. Some control characters are echoed
1182 * with their normal effect, other control characters are echoed as "^X",
1183 * normal characters are echoed normally. EOF (^D) is echoed, but immediately
1184 * backspaced over. Return the character with the echoed length added to its
1185 * attributes.
1186 */
1187 int len, rp;
1188
1189 ch &= ~IN_LEN;
1190 if (!(tp->tty_termios.c_lflag & ECHO)) {
1191 if (ch == ('\n' | IN_EOT) && (tp->tty_termios.c_lflag
1192 & (ICANON|ECHONL)) == (ICANON|ECHONL))
1193 (*tp->tty_echo)(tp, '\n');
1194 return(ch);
1195 }
1196
1197 /* "Reprint" tells if the echo output has been messed up by other output. */
1198 rp = tp->tty_incount == 0 ? FALSE : tp->tty_reprint;
1199
1200 if ((ch & IN_CHAR) < ' ') {
1201 switch (ch & (IN_ESC|IN_EOF|IN_EOT|IN_CHAR)) {
1202 case '\t':
1203 len = 0;
1204 do {
1205 (*tp->tty_echo)(tp, ' ');
1206 len++;
1207 } while (len < TAB_SIZE && (tp->tty_position & TAB_MASK) != 0);
1208 break;
1209 case '\r' | IN_EOT:
1210 case '\n' | IN_EOT:
1211 (*tp->tty_echo)(tp, ch & IN_CHAR);
1212 len = 0;
1213 break;
1214 default:
1215 (*tp->tty_echo)(tp, '^');
1216 (*tp->tty_echo)(tp, '@' + (ch & IN_CHAR));
1217 len = 2;
1218 }
1219 } else
1220 if ((ch & IN_CHAR) == '\177') {
1221 /* A DEL prints as "^?". */
1222 (*tp->tty_echo)(tp, '^');
1223 (*tp->tty_echo)(tp, '?');
1224 len = 2;
1225 } else {
1226 (*tp->tty_echo)(tp, ch & IN_CHAR);
1227 len = 1;
1228 }
1229 if (ch & IN_EOF) while (len > 0) { (*tp->tty_echo)(tp, '\b'); len--; }
1230
1231 tp->tty_reprint = rp;
1232 return(ch | (len << IN_LSHIFT));
1233 }
1234
1235 /*===========================================================================*
1236 * rawecho *
1237 *===========================================================================*/
rawecho(tp,ch)1238 static void rawecho(tp, ch)
1239 register tty_t *tp;
1240 int ch;
1241 {
1242 /* Echo without interpretation if ECHO is set. */
1243 int rp = tp->tty_reprint;
1244 if (tp->tty_termios.c_lflag & ECHO) (*tp->tty_echo)(tp, ch);
1245 tp->tty_reprint = rp;
1246 }
1247
1248 /*===========================================================================*
1249 * back_over *
1250 *===========================================================================*/
back_over(tp)1251 static int back_over(tp)
1252 register tty_t *tp;
1253 {
1254 /* Backspace to previous character on screen and erase it. */
1255 u16_t *head;
1256 int len;
1257
1258 if (tp->tty_incount == 0) return(0); /* queue empty */
1259 head = tp->tty_inhead;
1260 if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf);
1261 if (*--head & IN_EOT) return(0); /* can't erase "line breaks" */
1262 if (tp->tty_reprint) reprint(tp); /* reprint if messed up */
1263 tp->tty_inhead = head;
1264 tp->tty_incount--;
1265 if (tp->tty_termios.c_lflag & ECHOE) {
1266 len = (*head & IN_LEN) >> IN_LSHIFT;
1267 while (len > 0) {
1268 rawecho(tp, '\b');
1269 rawecho(tp, ' ');
1270 rawecho(tp, '\b');
1271 len--;
1272 }
1273 }
1274 return(1); /* one character erased */
1275 }
1276
1277 /*===========================================================================*
1278 * reprint *
1279 *===========================================================================*/
reprint(tp)1280 static void reprint(tp)
1281 register tty_t *tp; /* pointer to tty struct */
1282 {
1283 /* Restore what has been echoed to screen before if the user input has been
1284 * messed up by output, or if REPRINT (^R) is typed.
1285 */
1286 int count;
1287 u16_t *head;
1288
1289 tp->tty_reprint = FALSE;
1290
1291 /* Find the last line break in the input. */
1292 head = tp->tty_inhead;
1293 count = tp->tty_incount;
1294 while (count > 0) {
1295 if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf);
1296 if (head[-1] & IN_EOT) break;
1297 head--;
1298 count--;
1299 }
1300 if (count == tp->tty_incount) return; /* no reason to reprint */
1301
1302 /* Show REPRINT (^R) and move to a new line. */
1303 (void) tty_echo(tp, tp->tty_termios.c_cc[VREPRINT] | IN_ESC);
1304 rawecho(tp, '\r');
1305 rawecho(tp, '\n');
1306
1307 /* Reprint from the last break onwards. */
1308 do {
1309 if (head == bufend(tp->tty_inbuf)) head = tp->tty_inbuf;
1310 *head = tty_echo(tp, *head);
1311 head++;
1312 count++;
1313 } while (count < tp->tty_incount);
1314 }
1315
1316 /*===========================================================================*
1317 * out_process *
1318 *===========================================================================*/
out_process(tp,bstart,bpos,bend,icount,ocount)1319 void out_process(tp, bstart, bpos, bend, icount, ocount)
1320 tty_t *tp;
1321 char *bstart, *bpos, *bend; /* start/pos/end of circular buffer */
1322 int *icount; /* # input chars / input chars used */
1323 int *ocount; /* max output chars / output chars used */
1324 {
1325 /* Perform output processing on a circular buffer. *icount is the number of
1326 * bytes to process, and the number of bytes actually processed on return.
1327 * *ocount is the space available on input and the space used on output.
1328 * (Naturally *icount < *ocount.) The column position is updated modulo
1329 * the TAB size, because we really only need it for tabs.
1330 */
1331
1332 int tablen;
1333 int ict = *icount;
1334 int oct = *ocount;
1335 int pos = tp->tty_position;
1336
1337 while (ict > 0) {
1338 switch (*bpos) {
1339 case '\7':
1340 break;
1341 case '\b':
1342 pos--;
1343 break;
1344 case '\r':
1345 pos = 0;
1346 break;
1347 case '\n':
1348 if ((tp->tty_termios.c_oflag & (OPOST|ONLCR))
1349 == (OPOST|ONLCR)) {
1350 /* Map LF to CR+LF if there is space. Note that the
1351 * next character in the buffer is overwritten, so
1352 * we stop at this point.
1353 */
1354 if (oct >= 2) {
1355 *bpos = '\r';
1356 if (++bpos == bend) bpos = bstart;
1357 *bpos = '\n';
1358 pos = 0;
1359 ict--;
1360 oct -= 2;
1361 }
1362 goto out_done; /* no space or buffer got changed */
1363 }
1364 break;
1365 case '\t':
1366 /* Best guess for the tab length. */
1367 tablen = TAB_SIZE - (pos & TAB_MASK);
1368
1369 if ((tp->tty_termios.c_oflag & (OPOST|OXTABS))
1370 == (OPOST|OXTABS)) {
1371 /* Tabs must be expanded. */
1372 if (oct >= tablen) {
1373 pos += tablen;
1374 ict--;
1375 oct -= tablen;
1376 do {
1377 *bpos = ' ';
1378 if (++bpos == bend) bpos = bstart;
1379 } while (--tablen != 0);
1380 }
1381 goto out_done;
1382 }
1383 /* Tabs are output directly. */
1384 pos += tablen;
1385 break;
1386 default:
1387 /* Assume any other character prints as one character. */
1388 pos++;
1389 }
1390 if (++bpos == bend) bpos = bstart;
1391 ict--;
1392 oct--;
1393 }
1394 out_done:
1395 tp->tty_position = pos & TAB_MASK;
1396
1397 *icount -= ict; /* [io]ct are the number of chars not used */
1398 *ocount -= oct; /* *[io]count are the number of chars that are used */
1399 }
1400
1401 /*===========================================================================*
1402 * dev_ioctl *
1403 *===========================================================================*/
dev_ioctl(tp)1404 static void dev_ioctl(tp)
1405 tty_t *tp;
1406 {
1407 /* The ioctl's TCSETSW, TCSETSF and TIOCDRAIN wait for output to finish to make
1408 * sure that an attribute change doesn't affect the processing of current
1409 * output. Once output finishes the ioctl is executed as in do_ioctl().
1410 */
1411 int result = EINVAL;
1412
1413 if (tp->tty_outleft > 0) return; /* output not finished */
1414
1415 if (tp->tty_ioreq != TIOCDRAIN) {
1416 if (tp->tty_ioreq == TIOCSETAF) tty_icancel(tp);
1417 result = sys_safecopyfrom(tp->tty_iocaller, tp->tty_iogrant, 0,
1418 (vir_bytes) &tp->tty_termios,
1419 (vir_bytes) sizeof(tp->tty_termios));
1420 if (result == OK) setattr(tp);
1421 }
1422 tp->tty_ioreq = 0;
1423 chardriver_reply_task(tp->tty_iocaller, tp->tty_ioid, result);
1424 tp->tty_iocaller = NONE;
1425 }
1426
1427 /*===========================================================================*
1428 * setattr *
1429 *===========================================================================*/
setattr(tp)1430 static void setattr(tp)
1431 tty_t *tp;
1432 {
1433 /* Apply the new line attributes (raw/canonical, line speed, etc.) */
1434 u16_t *inp;
1435 int count;
1436
1437 if (!(tp->tty_termios.c_lflag & ICANON)) {
1438 /* Raw mode; put a "line break" on all characters in the input queue.
1439 * It is undefined what happens to the input queue when ICANON is
1440 * switched off, a process should use TCSAFLUSH to flush the queue.
1441 * Keeping the queue to preserve typeahead is the Right Thing, however
1442 * when a process does use TCSANOW to switch to raw mode.
1443 */
1444 count = tp->tty_eotct = tp->tty_incount;
1445 inp = tp->tty_intail;
1446 while (count > 0) {
1447 *inp |= IN_EOT;
1448 if (++inp == bufend(tp->tty_inbuf)) inp = tp->tty_inbuf;
1449 --count;
1450 }
1451 }
1452
1453 /* Inspect MIN and TIME. */
1454 settimer(tp, FALSE);
1455 if (tp->tty_termios.c_lflag & ICANON) {
1456 /* No MIN & TIME in canonical mode. */
1457 tp->tty_min = 1;
1458 } else {
1459 /* In raw mode MIN is the number of chars wanted, and TIME how long
1460 * to wait for them. With interesting exceptions if either is zero.
1461 */
1462 tp->tty_min = tp->tty_termios.c_cc[VMIN];
1463 if (tp->tty_min == 0 && tp->tty_termios.c_cc[VTIME] > 0)
1464 tp->tty_min = 1;
1465 }
1466
1467 if (!(tp->tty_termios.c_iflag & IXON)) {
1468 /* No start/stop output control, so don't leave output inhibited. */
1469 tp->tty_inhibited = RUNNING;
1470 tp->tty_events = 1;
1471 }
1472
1473 /* Setting the output speed to zero hangs up the phone. */
1474 if (tp->tty_termios.c_ospeed == B0) sigchar(tp, SIGHUP, 1);
1475
1476 /* Set new line speed, character size, etc at the device level. */
1477 (*tp->tty_ioctl)(tp, 0);
1478 }
1479
1480 /*===========================================================================*
1481 * sigchar *
1482 *===========================================================================*/
sigchar(tp,sig,mayflush)1483 void sigchar(tp, sig, mayflush)
1484 register tty_t *tp;
1485 int sig; /* SIGINT, SIGQUIT, SIGKILL or SIGHUP */
1486 int mayflush;
1487 {
1488 /* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard or SIGHUP from
1489 * a tty close, "stty 0", or a real RS-232 hangup. PM will send the signal to
1490 * the process group (INT, QUIT), all processes (KILL), or the session leader
1491 * (HUP).
1492 */
1493 int status;
1494
1495 if (tp->tty_pgrp != 0) {
1496 if (OK != (status = sys_kill(tp->tty_pgrp, sig))) {
1497 panic("Error; call to sys_kill failed: %d", status);
1498 }
1499 }
1500
1501 if (mayflush && !(tp->tty_termios.c_lflag & NOFLSH)) {
1502 tp->tty_incount = tp->tty_eotct = 0; /* kill earlier input */
1503 tp->tty_intail = tp->tty_inhead;
1504 (*tp->tty_ocancel)(tp, 0); /* kill all output */
1505 tp->tty_inhibited = RUNNING;
1506 tp->tty_events = 1;
1507 }
1508 }
1509
1510 /*===========================================================================*
1511 * tty_icancel *
1512 *===========================================================================*/
tty_icancel(tp)1513 static void tty_icancel(tp)
1514 register tty_t *tp;
1515 {
1516 /* Discard all pending input, tty buffer or device. */
1517
1518 tp->tty_incount = tp->tty_eotct = 0;
1519 tp->tty_intail = tp->tty_inhead;
1520 (*tp->tty_icancel)(tp, 0);
1521 }
1522
1523 /*===========================================================================*
1524 * tty_devnop *
1525 *===========================================================================*/
tty_devnop(tty_t * UNUSED (tp),int UNUSED (try))1526 static int tty_devnop(tty_t *UNUSED(tp), int UNUSED(try))
1527 {
1528 /* Some functions need not be implemented at the device level. */
1529 return 0;
1530 }
1531
1532 /*===========================================================================*
1533 * tty_init *
1534 *===========================================================================*/
tty_init()1535 static void tty_init()
1536 {
1537 /* Initialize tty structure and call device initialization routines. */
1538
1539 register tty_t *tp;
1540 int s;
1541
1542 system_hz = sys_hz();
1543
1544 /* Initialize the terminal lines. */
1545 memset(tty_table, '\0' , sizeof(tty_table));
1546
1547 for (tp = FIRST_TTY,s=0; tp < END_TTY; tp++,s++) {
1548
1549 tp->tty_index = s;
1550 init_timer(&tp->tty_tmr);
1551
1552 tp->tty_intail = tp->tty_inhead = tp->tty_inbuf;
1553 tp->tty_min = 1;
1554 tp->tty_incaller = tp->tty_outcaller = tp->tty_iocaller = NONE;
1555 tp->tty_termios = termios_defaults;
1556 tp->tty_icancel = tp->tty_ocancel = tp->tty_ioctl = tp->tty_close =
1557 tp->tty_open = tty_devnop;
1558 if (tp < tty_addr(NR_CONS)) {
1559 scr_init(tp);
1560
1561 /* Initialize the keyboard driver. */
1562 kb_init(tp);
1563
1564 tp->tty_minor = CONS_MINOR + s;
1565 } else {
1566 rs_init(tp);
1567 tp->tty_minor = RS232_MINOR + s-NR_CONS;
1568 }
1569 }
1570
1571 }
1572
1573 /*===========================================================================*
1574 * tty_timed_out *
1575 *===========================================================================*/
tty_timed_out(int arg)1576 static void tty_timed_out(int arg)
1577 {
1578 /* This timer has expired. Set the events flag, to force processing. */
1579 tty_t *tty_ptr;
1580 tty_ptr = &tty_table[arg];
1581 tty_ptr->tty_min = 0; /* force read to succeed */
1582 tty_ptr->tty_events = 1;
1583 }
1584
1585 /*===========================================================================*
1586 * settimer *
1587 *===========================================================================*/
settimer(tty_ptr,enable)1588 static void settimer(tty_ptr, enable)
1589 tty_t *tty_ptr; /* line to set or unset a timer on */
1590 int enable; /* set timer if true, otherwise unset */
1591 {
1592 clock_t ticks;
1593
1594 if (enable) {
1595 ticks = tty_ptr->tty_termios.c_cc[VTIME] * (system_hz/10);
1596
1597 /* Set a new timer for enabling the TTY events flags. */
1598 set_timer(&tty_ptr->tty_tmr, ticks, tty_timed_out, tty_ptr->tty_index);
1599 } else {
1600 /* Remove the timer from the active and expired lists. */
1601 cancel_timer(&tty_ptr->tty_tmr);
1602 }
1603 }
1604