verbose printing and sanity checking functions.
[minix.git] / drivers / tty / tty.c
blobc87301fa3f5638fa818ba276fe5d4a9b0a6132c4
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.
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.
19 * The valid messages and their parameters are:
21 * HARD_INT: output has been completed or input has arrived
22 * SYS_SIG: e.g., MINIX wants to shutdown; run code to cleanly stop
23 * DEV_READ: a process wants to read from a terminal
24 * DEV_WRITE: a process wants to write on a terminal
25 * DEV_IOCTL: a process wants to change a terminal's parameters
26 * DEV_OPEN: a tty line has been opened
27 * DEV_CLOSE: a tty line has been closed
28 * DEV_SELECT: start select notification request
29 * DEV_STATUS: FS wants to know status for SELECT or REVIVE
30 * CANCEL: terminate a previous incomplete system call immediately
32 * m_type TTY_LINE IO_ENDPT COUNT TTY_SPEKS ADDRESS
33 * -----------------------------------------------------------------
34 * | HARD_INT | | | | | |
35 * |-------------+---------+---------+---------+---------+---------|
36 * | SYS_SIG | sig set | | | | |
37 * |-------------+---------+---------+---------+---------+---------|
38 * | DEV_READ |minor dev| proc nr | count | | buf ptr |
39 * |-------------+---------+---------+---------+---------+---------|
40 * | DEV_WRITE |minor dev| proc nr | count | | buf ptr |
41 * |-------------+---------+---------+---------+---------+---------|
42 * | DEV_IOCTL |minor dev| proc nr |func code|erase etc| |
43 * |-------------+---------+---------+---------+---------+---------|
44 * | DEV_OPEN |minor dev| proc nr | O_NOCTTY| | |
45 * |-------------+---------+---------+---------+---------+---------|
46 * | DEV_CLOSE |minor dev| proc nr | | | |
47 * |-------------+---------+---------+---------+---------+---------|
48 * | DEV_STATUS | | | | | |
49 * |-------------+---------+---------+---------+---------+---------|
50 * | CANCEL |minor dev| proc nr | | | |
51 * -----------------------------------------------------------------
53 * Changes:
54 * Jan 20, 2004 moved TTY driver to user-space (Jorrit N. Herder)
55 * Sep 20, 2004 local timer management/ sync alarms (Jorrit N. Herder)
56 * Jul 13, 2004 support for function key observers (Jorrit N. Herder)
59 #include "../drivers.h"
60 #include <termios.h>
61 #include <sys/ioc_tty.h>
62 #include <signal.h>
63 #include <minix/callnr.h>
64 #include <minix/sys_config.h>
65 #include <minix/tty.h>
66 #include <minix/keymap.h>
67 #include "tty.h"
69 #include <sys/time.h>
70 #include <sys/select.h>
72 extern int irq_hook_id;
74 unsigned long kbd_irq_set = 0;
75 unsigned long rs_irq_set = 0;
77 /* Address of a tty structure. */
78 #define tty_addr(line) (&tty_table[line])
80 /* Macros for magic tty types. */
81 #define isconsole(tp) ((tp) < tty_addr(NR_CONS))
82 #define ispty(tp) ((tp) >= tty_addr(NR_CONS+NR_RS_LINES))
84 /* Macros for magic tty structure pointers. */
85 #define FIRST_TTY tty_addr(0)
86 #define END_TTY tty_addr(sizeof(tty_table) / sizeof(tty_table[0]))
88 /* A device exists if at least its 'devread' function is defined. */
89 #define tty_active(tp) ((tp)->tty_devread != NULL)
91 /* RS232 lines or pseudo terminals can be completely configured out. */
92 #if NR_RS_LINES == 0
93 #define rs_init(tp) ((void) 0)
94 #endif
96 #if NR_PTYS == 0
97 #define pty_init(tp) ((void) 0)
98 #define do_pty(tp, mp) ((void) 0)
99 #endif
101 struct kmessages kmess;
103 FORWARD _PROTOTYPE( void tty_timed_out, (timer_t *tp) );
104 FORWARD _PROTOTYPE( void expire_timers, (void) );
105 FORWARD _PROTOTYPE( void settimer, (tty_t *tty_ptr, int enable) );
106 FORWARD _PROTOTYPE( void do_cancel, (tty_t *tp, message *m_ptr) );
107 FORWARD _PROTOTYPE( void do_ioctl, (tty_t *tp, message *m_ptr, int s) );
108 FORWARD _PROTOTYPE( void do_open, (tty_t *tp, message *m_ptr) );
109 FORWARD _PROTOTYPE( void do_close, (tty_t *tp, message *m_ptr) );
110 FORWARD _PROTOTYPE( void do_read, (tty_t *tp, message *m_ptr, int s) );
111 FORWARD _PROTOTYPE( void do_write, (tty_t *tp, message *m_ptr, int s) );
112 FORWARD _PROTOTYPE( void do_select, (tty_t *tp, message *m_ptr) );
113 FORWARD _PROTOTYPE( void do_status, (message *m_ptr) );
114 FORWARD _PROTOTYPE( void in_transfer, (tty_t *tp) );
115 FORWARD _PROTOTYPE( int tty_echo, (tty_t *tp, int ch) );
116 FORWARD _PROTOTYPE( void rawecho, (tty_t *tp, int ch) );
117 FORWARD _PROTOTYPE( int back_over, (tty_t *tp) );
118 FORWARD _PROTOTYPE( void reprint, (tty_t *tp) );
119 FORWARD _PROTOTYPE( void dev_ioctl, (tty_t *tp) );
120 FORWARD _PROTOTYPE( void setattr, (tty_t *tp) );
121 FORWARD _PROTOTYPE( void tty_icancel, (tty_t *tp) );
122 FORWARD _PROTOTYPE( void tty_init, (void) );
124 /* Default attributes. */
125 PRIVATE struct termios termios_defaults = {
126 TINPUT_DEF, TOUTPUT_DEF, TCTRL_DEF, TLOCAL_DEF, TSPEED_DEF, TSPEED_DEF,
128 TEOF_DEF, TEOL_DEF, TERASE_DEF, TINTR_DEF, TKILL_DEF, TMIN_DEF,
129 TQUIT_DEF, TTIME_DEF, TSUSP_DEF, TSTART_DEF, TSTOP_DEF,
130 TREPRINT_DEF, TLNEXT_DEF, TDISCARD_DEF,
133 PRIVATE struct winsize winsize_defaults; /* = all zeroes */
135 /* Global variables for the TTY task (declared extern in tty.h). */
136 PUBLIC tty_t tty_table[NR_CONS+NR_RS_LINES+NR_PTYS];
137 PUBLIC int ccurrent; /* currently active console */
138 PUBLIC timer_t *tty_timers; /* queue of TTY timers */
139 PUBLIC clock_t tty_next_timeout; /* time that the next alarm is due */
140 PUBLIC struct machine machine; /* kernel environment variables */
141 PUBLIC u32_t system_hz;
143 extern PUBLIC unsigned info_location;
144 extern PUBLIC phys_bytes vid_size; /* 0x2000 for color or 0x0800 for mono */
145 extern PUBLIC phys_bytes vid_base;
148 /*===========================================================================*
149 * tty_task *
150 *===========================================================================*/
151 PUBLIC int main(void)
153 /* Main routine of the terminal task. */
155 message tty_mess; /* buffer for all incoming messages */
156 unsigned line;
157 int r, s;
158 register tty_t *tp;
160 /* Get kernel environment (protected_mode, pc_at and ega are needed). */
161 if (OK != (s=sys_getmachine(&machine))) {
162 panic("TTY","Couldn't obtain kernel environment.", s);
165 /* Initialize the TTY driver. */
166 tty_init();
168 /* Final one-time keyboard initialization. */
169 kb_init_once();
171 while (TRUE) {
172 int adflag = 0;
174 /* Check for and handle any events on any of the ttys. */
175 for (tp = FIRST_TTY; tp < END_TTY; tp++) {
176 if (tp->tty_events) handle_events(tp);
179 /* Get a request message. */
180 r= receive(ANY, &tty_mess);
181 if (r != 0)
182 panic("TTY", "receive failed with %d", r);
184 /* First handle all kernel notification types that the TTY supports.
185 * - An alarm went off, expire all timers and handle the events.
186 * - A hardware interrupt also is an invitation to check for events.
187 * - A new kernel message is available for printing.
188 * - Reset the console on system shutdown.
189 * Then see if this message is different from a normal device driver
190 * request and should be handled separately. These extra functions
191 * do not operate on a device, in constrast to the driver requests.
193 switch (tty_mess.m_type) {
194 case SYN_ALARM: /* fall through */
195 expire_timers(); /* run watchdogs of expired timers */
196 continue; /* contine to check for events */
197 case DEV_PING:
198 notify(tty_mess.m_source);
199 continue;
200 case HARD_INT: { /* hardware interrupt notification */
201 if (tty_mess.NOTIFY_ARG & kbd_irq_set)
202 kbd_interrupt(&tty_mess);/* fetch chars from keyboard */
203 #if NR_RS_LINES > 0
204 if (tty_mess.NOTIFY_ARG & rs_irq_set)
205 rs_interrupt(&tty_mess);/* serial I/O */
206 #endif
207 expire_timers(); /* run watchdogs of expired timers */
208 continue; /* contine to check for events */
210 case PROC_EVENT: {
211 cons_stop(); /* switch to primary console */
212 continue;
214 case SYS_SIG: { /* system signal */
215 sigset_t sigset = (sigset_t) tty_mess.NOTIFY_ARG;
216 if (sigismember(&sigset, SIGKMESS)) do_new_kmess(&tty_mess);
217 continue;
219 case DIAGNOSTICS_OLD: /* a server wants to print some */
220 #if 0
221 if (tty_mess.m_source != LOG_PROC_NR)
223 printf("[%d ", tty_mess.m_source);
225 #endif
226 do_diagnostics(&tty_mess, 0);
227 continue;
228 case DIAGNOSTICS_S_OLD:
229 case ASYN_DIAGNOSTICS_OLD:
230 do_diagnostics(&tty_mess, 1);
231 continue;
232 case GET_KMESS:
233 do_get_kmess(&tty_mess);
234 continue;
235 case GET_KMESS_S:
236 do_get_kmess_s(&tty_mess);
237 continue;
238 case FKEY_CONTROL: /* (un)register a fkey observer */
239 do_fkey_ctl(&tty_mess);
240 continue;
241 default: /* should be a driver request */
242 ; /* do nothing; end switch */
245 /* Only device requests should get to this point. All requests,
246 * except DEV_STATUS, have a minor device number. Check this
247 * exception and get the minor device number otherwise.
249 if (tty_mess.m_type == DEV_STATUS) {
250 do_status(&tty_mess);
251 continue;
253 line = tty_mess.TTY_LINE;
254 if (line == KBD_MINOR) {
255 do_kbd(&tty_mess);
256 continue;
257 } else if (line == KBDAUX_MINOR) {
258 do_kbdaux(&tty_mess);
259 continue;
260 } else if (line == VIDEO_MINOR) {
261 do_video(&tty_mess);
262 continue;
263 } else if ((line - CONS_MINOR) < NR_CONS) {
264 tp = tty_addr(line - CONS_MINOR);
265 } else if (line == LOG_MINOR) {
266 tp = tty_addr(0);
267 } else if ((line - RS232_MINOR) < NR_RS_LINES) {
268 tp = tty_addr(line - RS232_MINOR + NR_CONS);
269 } else if ((line - TTYPX_MINOR) < NR_PTYS) {
270 tp = tty_addr(line - TTYPX_MINOR + NR_CONS + NR_RS_LINES);
271 } else if ((line - PTYPX_MINOR) < NR_PTYS) {
272 tp = tty_addr(line - PTYPX_MINOR + NR_CONS + NR_RS_LINES);
273 if (tty_mess.m_type != DEV_IOCTL_S) {
274 do_pty(tp, &tty_mess);
275 continue;
277 } else {
278 tp = NULL;
281 /* If the device doesn't exist or is not configured return ENXIO. */
282 if (tp == NULL || ! tty_active(tp)) {
283 printf("Warning, TTY got illegal request %d from %d\n",
284 tty_mess.m_type, tty_mess.m_source);
285 if (tty_mess.m_source != LOG_PROC_NR)
287 tty_reply(TASK_REPLY, tty_mess.m_source,
288 tty_mess.IO_ENDPT, ENXIO);
290 continue;
293 /* Execute the requested device driver function. */
294 switch (tty_mess.m_type) {
295 case DEV_READ_S: do_read(tp, &tty_mess, 1); break;
296 case DEV_WRITE_S: do_write(tp, &tty_mess, 1); break;
297 case DEV_IOCTL_S: do_ioctl(tp, &tty_mess, 1); break;
298 case DEV_OPEN: do_open(tp, &tty_mess); break;
299 case DEV_CLOSE: do_close(tp, &tty_mess); break;
300 case DEV_SELECT: do_select(tp, &tty_mess); break;
301 case CANCEL: do_cancel(tp, &tty_mess); break;
302 default:
303 printf("Warning, TTY got unexpected request %d from %d\n",
304 tty_mess.m_type, tty_mess.m_source);
305 tty_reply(TASK_REPLY, tty_mess.m_source,
306 tty_mess.IO_ENDPT, EINVAL);
310 return 0;
313 /*===========================================================================*
314 * do_status *
315 *===========================================================================*/
316 PRIVATE void do_status(m_ptr)
317 message *m_ptr;
319 register struct tty *tp;
320 int event_found;
321 int status;
322 int ops;
324 /* Check for select or revive events on any of the ttys. If we found an,
325 * event return a single status message for it. The FS will make another
326 * call to see if there is more.
328 event_found = 0;
329 for (tp = FIRST_TTY; tp < END_TTY; tp++) {
330 if ((ops = select_try(tp, tp->tty_select_ops)) &&
331 tp->tty_select_proc == m_ptr->m_source) {
333 /* I/O for a selected minor device is ready. */
334 m_ptr->m_type = DEV_IO_READY;
335 m_ptr->DEV_MINOR = tp->tty_minor;
336 m_ptr->DEV_SEL_OPS = ops;
338 tp->tty_select_ops &= ~ops; /* unmark select event */
339 event_found = 1;
340 break;
342 else if (tp->tty_inrevived && tp->tty_incaller == m_ptr->m_source) {
344 /* Suspended request finished. Send a REVIVE. */
345 m_ptr->m_type = DEV_REVIVE;
346 m_ptr->REP_ENDPT = tp->tty_inproc;
347 m_ptr->REP_IO_GRANT = tp->tty_in_vir_g;
348 m_ptr->REP_STATUS = tp->tty_incum;
350 tp->tty_inleft = tp->tty_incum = 0;
351 tp->tty_inrevived = 0; /* unmark revive event */
352 event_found = 1;
353 break;
355 else if (tp->tty_outrevived && tp->tty_outcaller == m_ptr->m_source) {
357 /* Suspended request finished. Send a REVIVE. */
358 m_ptr->m_type = DEV_REVIVE;
359 m_ptr->REP_ENDPT = tp->tty_outproc;
360 m_ptr->REP_IO_GRANT = tp->tty_out_vir_g;
361 m_ptr->REP_STATUS = tp->tty_outcum;
363 tp->tty_outcum = 0;
364 tp->tty_outrevived = 0; /* unmark revive event */
365 event_found = 1;
366 break;
368 else if (tp->tty_iorevived && tp->tty_iocaller == m_ptr->m_source) {
369 /* Suspended request finished. Send a REVIVE. */
370 m_ptr->m_type = DEV_REVIVE;
371 m_ptr->REP_ENDPT = tp->tty_ioproc;
372 m_ptr->REP_IO_GRANT = tp->tty_iovir_g;
373 m_ptr->REP_STATUS = tp->tty_iostatus;
374 tp->tty_iorevived = 0; /* unmark revive event */
375 event_found = 1;
376 break;
380 #if NR_PTYS > 0
381 if (!event_found)
382 event_found = pty_status(m_ptr);
383 #endif
384 if (!event_found)
385 event_found= kbd_status(m_ptr);
387 if (! event_found) {
388 /* No events of interest were found. Return an empty message. */
389 m_ptr->m_type = DEV_NO_STATUS;
392 /* Almost done. Send back the reply message to the caller. */
393 status = sendnb(m_ptr->m_source, m_ptr);
394 if (status != OK) {
395 printf("tty`do_status: send to %d failed: %d\n",
396 m_ptr->m_source, status);
400 /*===========================================================================*
401 * do_read *
402 *===========================================================================*/
403 PRIVATE void do_read(tp, m_ptr, safe)
404 register tty_t *tp; /* pointer to tty struct */
405 register message *m_ptr; /* pointer to message sent to the task */
406 int safe; /* use safecopies? */
408 /* A process wants to read from a terminal. */
409 int r;
411 /* Check if there is already a process hanging in a read, check if the
412 * parameters are correct, do I/O.
414 if (tp->tty_inleft > 0) {
415 r = EIO;
416 } else
417 if (m_ptr->COUNT <= 0) {
418 r = EINVAL;
419 } else {
420 /* Copy information from the message to the tty struct. */
421 tp->tty_inrepcode = TASK_REPLY;
422 tp->tty_incaller = m_ptr->m_source;
423 tp->tty_inproc = m_ptr->IO_ENDPT;
424 tp->tty_in_vir_g = (vir_bytes) m_ptr->ADDRESS;
425 tp->tty_in_vir_offset = 0;
426 tp->tty_in_safe = safe;
427 tp->tty_inleft = m_ptr->COUNT;
429 if (!(tp->tty_termios.c_lflag & ICANON)
430 && tp->tty_termios.c_cc[VTIME] > 0) {
431 if (tp->tty_termios.c_cc[VMIN] == 0) {
432 /* MIN & TIME specify a read timer that finishes the
433 * read in TIME/10 seconds if no bytes are available.
435 settimer(tp, TRUE);
436 tp->tty_min = 1;
437 } else {
438 /* MIN & TIME specify an inter-byte timer that may
439 * have to be cancelled if there are no bytes yet.
441 if (tp->tty_eotct == 0) {
442 settimer(tp, FALSE);
443 tp->tty_min = tp->tty_termios.c_cc[VMIN];
448 /* Anything waiting in the input buffer? Clear it out... */
449 in_transfer(tp);
450 /* ...then go back for more. */
451 handle_events(tp);
452 if (tp->tty_inleft == 0) {
453 if (tp->tty_select_ops)
454 select_retry(tp);
455 return; /* already done */
458 /* There were no bytes in the input queue available, so suspend
459 * the caller.
461 r = SUSPEND; /* suspend the caller */
462 tp->tty_inrepcode = TTY_REVIVE;
464 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, r);
465 if (tp->tty_select_ops)
466 select_retry(tp);
469 /*===========================================================================*
470 * do_write *
471 *===========================================================================*/
472 PRIVATE void do_write(tp, m_ptr, safe)
473 register tty_t *tp;
474 register message *m_ptr; /* pointer to message sent to the task */
475 int safe;
477 /* A process wants to write on a terminal. */
478 int r;
480 /* Check if there is already a process hanging in a write, check if the
481 * parameters are correct, do I/O.
483 if (tp->tty_outleft > 0) {
484 r = EIO;
485 } else
486 if (m_ptr->COUNT <= 0) {
487 r = EINVAL;
488 } else {
489 /* Copy message parameters to the tty structure. */
490 tp->tty_outrepcode = TASK_REPLY;
491 tp->tty_outcaller = m_ptr->m_source;
492 tp->tty_outproc = m_ptr->IO_ENDPT;
493 tp->tty_out_vir_g = (vir_bytes) m_ptr->ADDRESS;
494 tp->tty_out_vir_offset = 0;
495 tp->tty_out_safe = safe;
496 tp->tty_outleft = m_ptr->COUNT;
498 /* Try to write. */
499 handle_events(tp);
500 if (tp->tty_outleft == 0)
501 return; /* already done */
503 /* None or not all the bytes could be written, so suspend the
504 * caller.
506 r = SUSPEND; /* suspend the caller */
507 tp->tty_outrepcode = TTY_REVIVE;
509 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, r);
512 /*===========================================================================*
513 * do_ioctl *
514 *===========================================================================*/
515 PRIVATE void do_ioctl(tp, m_ptr, safe)
516 register tty_t *tp;
517 message *m_ptr; /* pointer to message sent to task */
518 int safe;
520 /* Perform an IOCTL on this terminal. Posix termios calls are handled
521 * by the IOCTL system call
524 int r;
525 union {
526 int i;
527 } param;
528 size_t size;
530 /* Size of the ioctl parameter. */
531 switch (m_ptr->TTY_REQUEST) {
532 case TCGETS: /* Posix tcgetattr function */
533 case TCSETS: /* Posix tcsetattr function, TCSANOW option */
534 case TCSETSW: /* Posix tcsetattr function, TCSADRAIN option */
535 case TCSETSF: /* Posix tcsetattr function, TCSAFLUSH option */
536 size = sizeof(struct termios);
537 break;
539 case TCSBRK: /* Posix tcsendbreak function */
540 case TCFLOW: /* Posix tcflow function */
541 case TCFLSH: /* Posix tcflush function */
542 case TIOCGPGRP: /* Posix tcgetpgrp function */
543 case TIOCSPGRP: /* Posix tcsetpgrp function */
544 size = sizeof(int);
545 break;
547 case TIOCGWINSZ: /* get window size (not Posix) */
548 case TIOCSWINSZ: /* set window size (not Posix) */
549 size = sizeof(struct winsize);
550 break;
552 #if (MACHINE == IBM_PC)
553 case KIOCSMAP: /* load keymap (Minix extension) */
554 size = sizeof(keymap_t);
555 break;
557 case TIOCSFON: /* load font (Minix extension) */
558 size = sizeof(u8_t [8192]);
559 break;
561 #endif
562 case TCDRAIN: /* Posix tcdrain function -- no parameter */
563 default: size = 0;
566 r = OK;
567 switch (m_ptr->TTY_REQUEST) {
568 case TCGETS:
569 /* Get the termios attributes. */
570 if(safe) {
571 r = sys_safecopyto(m_ptr->IO_ENDPT, (vir_bytes) m_ptr->ADDRESS, 0,
572 (vir_bytes) &tp->tty_termios, (vir_bytes) size, D);
573 } else {
574 r = sys_vircopy(SELF, D, (vir_bytes) &tp->tty_termios,
575 m_ptr->IO_ENDPT, D, (vir_bytes) m_ptr->ADDRESS,
576 (vir_bytes) size);
578 break;
580 case TCSETSW:
581 case TCSETSF:
582 case TCDRAIN:
583 if (tp->tty_outleft > 0) {
584 /* Wait for all ongoing output processing to finish. */
585 tp->tty_iocaller = m_ptr->m_source;
586 tp->tty_ioproc = m_ptr->IO_ENDPT;
587 tp->tty_ioreq = m_ptr->REQUEST;
588 tp->tty_iovir_g = (vir_bytes) m_ptr->ADDRESS;
589 tp->tty_io_safe = safe;
590 r = SUSPEND;
591 break;
593 if (m_ptr->TTY_REQUEST == TCDRAIN) break;
594 if (m_ptr->TTY_REQUEST == TCSETSF) tty_icancel(tp);
595 /*FALL THROUGH*/
596 case TCSETS:
597 /* Set the termios attributes. */
598 if(safe) {
599 r = sys_safecopyfrom(m_ptr->IO_ENDPT, (vir_bytes) m_ptr->ADDRESS, 0,
600 (vir_bytes) &tp->tty_termios, (vir_bytes) size, D);
601 } else {
602 r = sys_vircopy( m_ptr->IO_ENDPT, D, (vir_bytes) m_ptr->ADDRESS,
603 SELF, D, (vir_bytes) &tp->tty_termios, (vir_bytes) size);
605 if (r != OK) break;
606 setattr(tp);
607 break;
609 case TCFLSH:
610 if(safe) {
611 r = sys_safecopyfrom(m_ptr->IO_ENDPT, (vir_bytes) m_ptr->ADDRESS, 0,
612 (vir_bytes) &param.i, (vir_bytes) size, D);
613 } else {
614 r = sys_vircopy(m_ptr->IO_ENDPT, D, (vir_bytes) m_ptr->ADDRESS,
615 SELF, D, (vir_bytes) &param.i, (vir_bytes) size);
617 if (r != OK) break;
618 switch (param.i) {
619 case TCIFLUSH: tty_icancel(tp); break;
620 case TCOFLUSH: (*tp->tty_ocancel)(tp, 0); break;
621 case TCIOFLUSH: tty_icancel(tp); (*tp->tty_ocancel)(tp, 0); break;
622 default: r = EINVAL;
624 break;
626 case TCFLOW:
627 if(safe) {
628 r = sys_safecopyfrom(m_ptr->IO_ENDPT, (vir_bytes) m_ptr->ADDRESS, 0,
629 (vir_bytes) &param.i, (vir_bytes) size, D);
630 } else {
631 r = sys_vircopy( m_ptr->IO_ENDPT, D, (vir_bytes) m_ptr->ADDRESS,
632 SELF, D, (vir_bytes) &param.i, (vir_bytes) size);
634 if (r != OK) break;
635 switch (param.i) {
636 case TCOOFF:
637 case TCOON:
638 tp->tty_inhibited = (param.i == TCOOFF);
639 tp->tty_events = 1;
640 break;
641 case TCIOFF:
642 (*tp->tty_echo)(tp, tp->tty_termios.c_cc[VSTOP]);
643 break;
644 case TCION:
645 (*tp->tty_echo)(tp, tp->tty_termios.c_cc[VSTART]);
646 break;
647 default:
648 r = EINVAL;
650 break;
652 case TCSBRK:
653 if (tp->tty_break != NULL) (*tp->tty_break)(tp,0);
654 break;
656 case TIOCGWINSZ:
657 if(safe) {
658 r = sys_safecopyto(m_ptr->IO_ENDPT, (vir_bytes) m_ptr->ADDRESS, 0,
659 (vir_bytes) &tp->tty_winsize, (vir_bytes) size, D);
660 } else {
661 r = sys_vircopy(SELF, D, (vir_bytes) &tp->tty_winsize,
662 m_ptr->IO_ENDPT, D, (vir_bytes) m_ptr->ADDRESS,
663 (vir_bytes) size);
665 break;
667 case TIOCSWINSZ:
668 if(safe) {
669 r = sys_safecopyfrom(m_ptr->IO_ENDPT, (vir_bytes) m_ptr->ADDRESS, 0,
670 (vir_bytes) &tp->tty_winsize, (vir_bytes) size, D);
671 } else {
672 r = sys_vircopy( m_ptr->IO_ENDPT, D, (vir_bytes) m_ptr->ADDRESS,
673 SELF, D, (vir_bytes) &tp->tty_winsize, (vir_bytes) size);
675 sigchar(tp, SIGWINCH, 0);
676 break;
678 #if (MACHINE == IBM_PC)
679 case KIOCSMAP:
680 /* Load a new keymap (only /dev/console). */
681 if (isconsole(tp)) r = kbd_loadmap(m_ptr, safe);
682 break;
684 case TIOCSFON_OLD:
685 printf("TTY: old TIOCSFON ignored.\n");
686 break;
687 case TIOCSFON:
688 /* Load a font into an EGA or VGA card (hs@hck.hr) */
689 if (isconsole(tp)) r = con_loadfont(m_ptr);
690 break;
691 #endif
693 #if (MACHINE == ATARI)
694 case VDU_LOADFONT:
695 r = vdu_loadfont(m_ptr);
696 break;
697 #endif
699 /* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is
700 * not defined.
702 case TIOCGPGRP:
703 case TIOCSPGRP:
704 default:
705 r = ENOTTY;
708 /* Send the reply. */
709 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, r);
712 /*===========================================================================*
713 * do_open *
714 *===========================================================================*/
715 PRIVATE void do_open(tp, m_ptr)
716 register tty_t *tp;
717 message *m_ptr; /* pointer to message sent to task */
719 /* A tty line has been opened. Make it the callers controlling tty if
720 * O_NOCTTY is *not* set and it is not the log device. 1 is returned if
721 * the tty is made the controlling tty, otherwise OK or an error code.
723 int r = OK;
725 if (m_ptr->TTY_LINE == LOG_MINOR) {
726 /* The log device is a write-only diagnostics device. */
727 if (m_ptr->COUNT & R_BIT) r = EACCES;
728 } else {
729 if (!(m_ptr->COUNT & O_NOCTTY)) {
730 tp->tty_pgrp = m_ptr->IO_ENDPT;
731 r = 1;
733 tp->tty_openct++;
735 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, r);
738 /*===========================================================================*
739 * do_close *
740 *===========================================================================*/
741 PRIVATE void do_close(tp, m_ptr)
742 register tty_t *tp;
743 message *m_ptr; /* pointer to message sent to task */
745 /* A tty line has been closed. Clean up the line if it is the last close. */
747 if (m_ptr->TTY_LINE != LOG_MINOR && --tp->tty_openct == 0) {
748 tp->tty_pgrp = 0;
749 tty_icancel(tp);
750 (*tp->tty_ocancel)(tp, 0);
751 (*tp->tty_close)(tp, 0);
752 tp->tty_termios = termios_defaults;
753 tp->tty_winsize = winsize_defaults;
754 setattr(tp);
756 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, OK);
759 /*===========================================================================*
760 * do_cancel *
761 *===========================================================================*/
762 PRIVATE void do_cancel(tp, m_ptr)
763 register tty_t *tp;
764 message *m_ptr; /* pointer to message sent to task */
766 /* A signal has been sent to a process that is hanging trying to read or write.
767 * The pending read or write must be finished off immediately.
770 int proc_nr;
771 int mode;
772 int r = EINTR;
774 /* Check the parameters carefully, to avoid cancelling twice. */
775 proc_nr = m_ptr->IO_ENDPT;
776 mode = m_ptr->COUNT;
777 if ((mode & R_BIT) && tp->tty_inleft != 0 && proc_nr == tp->tty_inproc &&
778 (!tp->tty_in_safe || tp->tty_in_vir_g==(vir_bytes)m_ptr->IO_GRANT)) {
779 /* Process was reading when killed. Clean up input. */
780 tty_icancel(tp);
781 r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN;
782 tp->tty_inleft = tp->tty_incum = tp->tty_inrevived = 0;
784 if ((mode & W_BIT) && tp->tty_outleft != 0 && proc_nr == tp->tty_outproc &&
785 (!tp->tty_out_safe || tp->tty_out_vir_g==(vir_bytes)m_ptr->IO_GRANT)) {
786 /* Process was writing when killed. Clean up output. */
787 r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN;
788 tp->tty_outleft = tp->tty_outcum = tp->tty_outrevived = 0;
790 if (tp->tty_ioreq != 0 && proc_nr == tp->tty_ioproc) {
791 /* Process was waiting for output to drain. */
792 tp->tty_ioreq = 0;
794 tp->tty_events = 1;
795 tty_reply(TASK_REPLY, m_ptr->m_source, proc_nr, r);
798 PUBLIC int select_try(struct tty *tp, int ops)
800 int ready_ops = 0;
802 /* Special case. If line is hung up, no operations will block.
803 * (and it can be seen as an exceptional condition.)
805 if (tp->tty_termios.c_ospeed == B0) {
806 ready_ops |= ops;
809 if (ops & SEL_RD) {
810 /* will i/o not block on read? */
811 if (tp->tty_inleft > 0) {
812 ready_ops |= SEL_RD; /* EIO - no blocking */
813 } else if (tp->tty_incount > 0) {
814 /* Is a regular read possible? tty_incount
815 * says there is data. But a read will only succeed
816 * in canonical mode if a newline has been seen.
818 if (!(tp->tty_termios.c_lflag & ICANON) ||
819 tp->tty_eotct > 0) {
820 ready_ops |= SEL_RD;
825 if (ops & SEL_WR) {
826 if (tp->tty_outleft > 0) ready_ops |= SEL_WR;
827 else if ((*tp->tty_devwrite)(tp, 1)) ready_ops |= SEL_WR;
829 return ready_ops;
832 PUBLIC int select_retry(struct tty *tp)
834 if (tp->tty_select_ops && select_try(tp, tp->tty_select_ops))
835 notify(tp->tty_select_proc);
836 return OK;
839 /*===========================================================================*
840 * handle_events *
841 *===========================================================================*/
842 PUBLIC void handle_events(tp)
843 tty_t *tp; /* TTY to check for events. */
845 /* Handle any events pending on a TTY. These events are usually device
846 * interrupts.
848 * Two kinds of events are prominent:
849 * - a character has been received from the console or an RS232 line.
850 * - an RS232 line has completed a write request (on behalf of a user).
851 * The interrupt handler may delay the interrupt message at its discretion
852 * to avoid swamping the TTY task. Messages may be overwritten when the
853 * lines are fast or when there are races between different lines, input
854 * and output, because MINIX only provides single buffering for interrupt
855 * messages (in proc.c). This is handled by explicitly checking each line
856 * for fresh input and completed output on each interrupt.
859 do {
860 tp->tty_events = 0;
862 /* Read input and perform input processing. */
863 (*tp->tty_devread)(tp, 0);
865 /* Perform output processing and write output. */
866 (*tp->tty_devwrite)(tp, 0);
868 /* Ioctl waiting for some event? */
869 if (tp->tty_ioreq != 0) dev_ioctl(tp);
870 } while (tp->tty_events);
872 /* Transfer characters from the input queue to a waiting process. */
873 in_transfer(tp);
875 /* Reply if enough bytes are available. */
876 if (tp->tty_incum >= tp->tty_min && tp->tty_inleft > 0) {
877 if (tp->tty_inrepcode == TTY_REVIVE) {
878 notify(tp->tty_incaller);
879 tp->tty_inrevived = 1;
880 } else {
881 tty_reply(tp->tty_inrepcode, tp->tty_incaller,
882 tp->tty_inproc, tp->tty_incum);
883 tp->tty_inleft = tp->tty_incum = 0;
886 if (tp->tty_select_ops)
888 select_retry(tp);
890 #if NR_PTYS > 0
891 if (ispty(tp))
892 select_retry_pty(tp);
893 #endif
896 /*===========================================================================*
897 * in_transfer *
898 *===========================================================================*/
899 PRIVATE void in_transfer(tp)
900 register tty_t *tp; /* pointer to terminal to read from */
902 /* Transfer bytes from the input queue to a process reading from a terminal. */
904 int ch;
905 int count;
906 char buf[64], *bp;
908 /* Force read to succeed if the line is hung up, looks like EOF to reader. */
909 if (tp->tty_termios.c_ospeed == B0) tp->tty_min = 0;
911 /* Anything to do? */
912 if (tp->tty_inleft == 0 || tp->tty_eotct < tp->tty_min) return;
914 bp = buf;
915 while (tp->tty_inleft > 0 && tp->tty_eotct > 0) {
916 ch = *tp->tty_intail;
918 if (!(ch & IN_EOF)) {
919 /* One character to be delivered to the user. */
920 *bp = ch & IN_CHAR;
921 tp->tty_inleft--;
922 if (++bp == bufend(buf)) {
923 /* Temp buffer full, copy to user space. */
924 if(tp->tty_in_safe) {
925 sys_safecopyto(tp->tty_inproc,
926 tp->tty_in_vir_g, tp->tty_in_vir_offset,
927 (vir_bytes) buf,
928 (vir_bytes) buflen(buf), D);
929 tp->tty_in_vir_offset += buflen(buf);
930 } else {
931 sys_vircopy(SELF, D, (vir_bytes) buf,
932 tp->tty_inproc, D, tp->tty_in_vir_g,
933 (vir_bytes) buflen(buf));
934 tp->tty_in_vir_g += buflen(buf);
936 tp->tty_incum += buflen(buf);
937 bp = buf;
941 /* Remove the character from the input queue. */
942 if (++tp->tty_intail == bufend(tp->tty_inbuf))
943 tp->tty_intail = tp->tty_inbuf;
944 tp->tty_incount--;
945 if (ch & IN_EOT) {
946 tp->tty_eotct--;
947 /* Don't read past a line break in canonical mode. */
948 if (tp->tty_termios.c_lflag & ICANON) tp->tty_inleft = 0;
952 if (bp > buf) {
953 /* Leftover characters in the buffer. */
954 count = bp - buf;
955 if(tp->tty_in_safe) {
956 sys_safecopyto(tp->tty_inproc,
957 tp->tty_in_vir_g, tp->tty_in_vir_offset,
958 (vir_bytes) buf, (vir_bytes) count, D);
959 tp->tty_in_vir_offset += count;
960 } else {
961 sys_vircopy(SELF, D, (vir_bytes) buf,
962 tp->tty_inproc, D, tp->tty_in_vir_g, (vir_bytes) count);
963 tp->tty_in_vir_g += count;
965 tp->tty_incum += count;
968 /* Usually reply to the reader, possibly even if incum == 0 (EOF). */
969 if (tp->tty_inleft == 0) {
970 if (tp->tty_inrepcode == TTY_REVIVE) {
971 notify(tp->tty_incaller);
972 tp->tty_inrevived = 1;
973 } else {
974 tty_reply(tp->tty_inrepcode, tp->tty_incaller,
975 tp->tty_inproc, tp->tty_incum);
976 tp->tty_inleft = tp->tty_incum = 0;
981 /*===========================================================================*
982 * in_process *
983 *===========================================================================*/
984 PUBLIC int in_process(tp, buf, count)
985 register tty_t *tp; /* terminal on which character has arrived */
986 char *buf; /* buffer with input characters */
987 int count; /* number of input characters */
989 /* Characters have just been typed in. Process, save, and echo them. Return
990 * the number of characters processed.
993 int ch, sig, ct;
994 int timeset = FALSE;
996 for (ct = 0; ct < count; ct++) {
997 /* Take one character. */
998 ch = *buf++ & BYTE;
1000 /* Strip to seven bits? */
1001 if (tp->tty_termios.c_iflag & ISTRIP) ch &= 0x7F;
1003 /* Input extensions? */
1004 if (tp->tty_termios.c_lflag & IEXTEN) {
1006 /* Previous character was a character escape? */
1007 if (tp->tty_escaped) {
1008 tp->tty_escaped = NOT_ESCAPED;
1009 ch |= IN_ESC; /* protect character */
1012 /* LNEXT (^V) to escape the next character? */
1013 if (ch == tp->tty_termios.c_cc[VLNEXT]) {
1014 tp->tty_escaped = ESCAPED;
1015 rawecho(tp, '^');
1016 rawecho(tp, '\b');
1017 continue; /* do not store the escape */
1020 /* REPRINT (^R) to reprint echoed characters? */
1021 if (ch == tp->tty_termios.c_cc[VREPRINT]) {
1022 reprint(tp);
1023 continue;
1027 /* _POSIX_VDISABLE is a normal character value, so better escape it. */
1028 if (ch == _POSIX_VDISABLE) ch |= IN_ESC;
1030 /* Map CR to LF, ignore CR, or map LF to CR. */
1031 if (ch == '\r') {
1032 if (tp->tty_termios.c_iflag & IGNCR) continue;
1033 if (tp->tty_termios.c_iflag & ICRNL) ch = '\n';
1034 } else
1035 if (ch == '\n') {
1036 if (tp->tty_termios.c_iflag & INLCR) ch = '\r';
1039 /* Canonical mode? */
1040 if (tp->tty_termios.c_lflag & ICANON) {
1042 /* Erase processing (rub out of last character). */
1043 if (ch == tp->tty_termios.c_cc[VERASE]) {
1044 (void) back_over(tp);
1045 if (!(tp->tty_termios.c_lflag & ECHOE)) {
1046 (void) tty_echo(tp, ch);
1048 continue;
1051 /* Kill processing (remove current line). */
1052 if (ch == tp->tty_termios.c_cc[VKILL]) {
1053 while (back_over(tp)) {}
1054 if (!(tp->tty_termios.c_lflag & ECHOE)) {
1055 (void) tty_echo(tp, ch);
1056 if (tp->tty_termios.c_lflag & ECHOK)
1057 rawecho(tp, '\n');
1059 continue;
1062 /* EOF (^D) means end-of-file, an invisible "line break". */
1063 if (ch == tp->tty_termios.c_cc[VEOF]) ch |= IN_EOT | IN_EOF;
1065 /* The line may be returned to the user after an LF. */
1066 if (ch == '\n') ch |= IN_EOT;
1068 /* Same thing with EOL, whatever it may be. */
1069 if (ch == tp->tty_termios.c_cc[VEOL]) ch |= IN_EOT;
1072 /* Start/stop input control? */
1073 if (tp->tty_termios.c_iflag & IXON) {
1075 /* Output stops on STOP (^S). */
1076 if (ch == tp->tty_termios.c_cc[VSTOP]) {
1077 tp->tty_inhibited = STOPPED;
1078 tp->tty_events = 1;
1079 continue;
1082 /* Output restarts on START (^Q) or any character if IXANY. */
1083 if (tp->tty_inhibited) {
1084 if (ch == tp->tty_termios.c_cc[VSTART]
1085 || (tp->tty_termios.c_iflag & IXANY)) {
1086 tp->tty_inhibited = RUNNING;
1087 tp->tty_events = 1;
1088 if (ch == tp->tty_termios.c_cc[VSTART])
1089 continue;
1094 if (tp->tty_termios.c_lflag & ISIG) {
1095 /* Check for INTR (^?) and QUIT (^\) characters. */
1096 if (ch == tp->tty_termios.c_cc[VINTR]
1097 || ch == tp->tty_termios.c_cc[VQUIT]) {
1098 sig = SIGINT;
1099 if (ch == tp->tty_termios.c_cc[VQUIT]) sig = SIGQUIT;
1100 sigchar(tp, sig, 1);
1101 (void) tty_echo(tp, ch);
1102 continue;
1106 /* Is there space in the input buffer? */
1107 if (tp->tty_incount == buflen(tp->tty_inbuf)) {
1108 /* No space; discard in canonical mode, keep in raw mode. */
1109 if (tp->tty_termios.c_lflag & ICANON) continue;
1110 break;
1113 if (!(tp->tty_termios.c_lflag & ICANON)) {
1114 /* In raw mode all characters are "line breaks". */
1115 ch |= IN_EOT;
1117 /* Start an inter-byte timer? */
1118 if (!timeset && tp->tty_termios.c_cc[VMIN] > 0
1119 && tp->tty_termios.c_cc[VTIME] > 0) {
1120 settimer(tp, TRUE);
1121 timeset = TRUE;
1125 /* Perform the intricate function of echoing. */
1126 if (tp->tty_termios.c_lflag & (ECHO|ECHONL)) ch = tty_echo(tp, ch);
1128 /* Save the character in the input queue. */
1129 *tp->tty_inhead++ = ch;
1130 if (tp->tty_inhead == bufend(tp->tty_inbuf))
1131 tp->tty_inhead = tp->tty_inbuf;
1132 tp->tty_incount++;
1133 if (ch & IN_EOT) tp->tty_eotct++;
1135 /* Try to finish input if the queue threatens to overflow. */
1136 if (tp->tty_incount == buflen(tp->tty_inbuf)) in_transfer(tp);
1138 return ct;
1141 /*===========================================================================*
1142 * echo *
1143 *===========================================================================*/
1144 PRIVATE int tty_echo(tp, ch)
1145 register tty_t *tp; /* terminal on which to echo */
1146 register int ch; /* pointer to character to echo */
1148 /* Echo the character if echoing is on. Some control characters are echoed
1149 * with their normal effect, other control characters are echoed as "^X",
1150 * normal characters are echoed normally. EOF (^D) is echoed, but immediately
1151 * backspaced over. Return the character with the echoed length added to its
1152 * attributes.
1154 int len, rp;
1156 ch &= ~IN_LEN;
1157 if (!(tp->tty_termios.c_lflag & ECHO)) {
1158 if (ch == ('\n' | IN_EOT) && (tp->tty_termios.c_lflag
1159 & (ICANON|ECHONL)) == (ICANON|ECHONL))
1160 (*tp->tty_echo)(tp, '\n');
1161 return(ch);
1164 /* "Reprint" tells if the echo output has been messed up by other output. */
1165 rp = tp->tty_incount == 0 ? FALSE : tp->tty_reprint;
1167 if ((ch & IN_CHAR) < ' ') {
1168 switch (ch & (IN_ESC|IN_EOF|IN_EOT|IN_CHAR)) {
1169 case '\t':
1170 len = 0;
1171 do {
1172 (*tp->tty_echo)(tp, ' ');
1173 len++;
1174 } while (len < TAB_SIZE && (tp->tty_position & TAB_MASK) != 0);
1175 break;
1176 case '\r' | IN_EOT:
1177 case '\n' | IN_EOT:
1178 (*tp->tty_echo)(tp, ch & IN_CHAR);
1179 len = 0;
1180 break;
1181 default:
1182 (*tp->tty_echo)(tp, '^');
1183 (*tp->tty_echo)(tp, '@' + (ch & IN_CHAR));
1184 len = 2;
1186 } else
1187 if ((ch & IN_CHAR) == '\177') {
1188 /* A DEL prints as "^?". */
1189 (*tp->tty_echo)(tp, '^');
1190 (*tp->tty_echo)(tp, '?');
1191 len = 2;
1192 } else {
1193 (*tp->tty_echo)(tp, ch & IN_CHAR);
1194 len = 1;
1196 if (ch & IN_EOF) while (len > 0) { (*tp->tty_echo)(tp, '\b'); len--; }
1198 tp->tty_reprint = rp;
1199 return(ch | (len << IN_LSHIFT));
1202 /*===========================================================================*
1203 * rawecho *
1204 *===========================================================================*/
1205 PRIVATE void rawecho(tp, ch)
1206 register tty_t *tp;
1207 int ch;
1209 /* Echo without interpretation if ECHO is set. */
1210 int rp = tp->tty_reprint;
1211 if (tp->tty_termios.c_lflag & ECHO) (*tp->tty_echo)(tp, ch);
1212 tp->tty_reprint = rp;
1215 /*===========================================================================*
1216 * back_over *
1217 *===========================================================================*/
1218 PRIVATE int back_over(tp)
1219 register tty_t *tp;
1221 /* Backspace to previous character on screen and erase it. */
1222 u16_t *head;
1223 int len;
1225 if (tp->tty_incount == 0) return(0); /* queue empty */
1226 head = tp->tty_inhead;
1227 if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf);
1228 if (*--head & IN_EOT) return(0); /* can't erase "line breaks" */
1229 if (tp->tty_reprint) reprint(tp); /* reprint if messed up */
1230 tp->tty_inhead = head;
1231 tp->tty_incount--;
1232 if (tp->tty_termios.c_lflag & ECHOE) {
1233 len = (*head & IN_LEN) >> IN_LSHIFT;
1234 while (len > 0) {
1235 rawecho(tp, '\b');
1236 rawecho(tp, ' ');
1237 rawecho(tp, '\b');
1238 len--;
1241 return(1); /* one character erased */
1244 /*===========================================================================*
1245 * reprint *
1246 *===========================================================================*/
1247 PRIVATE void reprint(tp)
1248 register tty_t *tp; /* pointer to tty struct */
1250 /* Restore what has been echoed to screen before if the user input has been
1251 * messed up by output, or if REPRINT (^R) is typed.
1253 int count;
1254 u16_t *head;
1256 tp->tty_reprint = FALSE;
1258 /* Find the last line break in the input. */
1259 head = tp->tty_inhead;
1260 count = tp->tty_incount;
1261 while (count > 0) {
1262 if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf);
1263 if (head[-1] & IN_EOT) break;
1264 head--;
1265 count--;
1267 if (count == tp->tty_incount) return; /* no reason to reprint */
1269 /* Show REPRINT (^R) and move to a new line. */
1270 (void) tty_echo(tp, tp->tty_termios.c_cc[VREPRINT] | IN_ESC);
1271 rawecho(tp, '\r');
1272 rawecho(tp, '\n');
1274 /* Reprint from the last break onwards. */
1275 do {
1276 if (head == bufend(tp->tty_inbuf)) head = tp->tty_inbuf;
1277 *head = tty_echo(tp, *head);
1278 head++;
1279 count++;
1280 } while (count < tp->tty_incount);
1283 /*===========================================================================*
1284 * out_process *
1285 *===========================================================================*/
1286 PUBLIC void out_process(tp, bstart, bpos, bend, icount, ocount)
1287 tty_t *tp;
1288 char *bstart, *bpos, *bend; /* start/pos/end of circular buffer */
1289 int *icount; /* # input chars / input chars used */
1290 int *ocount; /* max output chars / output chars used */
1292 /* Perform output processing on a circular buffer. *icount is the number of
1293 * bytes to process, and the number of bytes actually processed on return.
1294 * *ocount is the space available on input and the space used on output.
1295 * (Naturally *icount < *ocount.) The column position is updated modulo
1296 * the TAB size, because we really only need it for tabs.
1299 int tablen;
1300 int ict = *icount;
1301 int oct = *ocount;
1302 int pos = tp->tty_position;
1304 while (ict > 0) {
1305 switch (*bpos) {
1306 case '\7':
1307 break;
1308 case '\b':
1309 pos--;
1310 break;
1311 case '\r':
1312 pos = 0;
1313 break;
1314 case '\n':
1315 if ((tp->tty_termios.c_oflag & (OPOST|ONLCR))
1316 == (OPOST|ONLCR)) {
1317 /* Map LF to CR+LF if there is space. Note that the
1318 * next character in the buffer is overwritten, so
1319 * we stop at this point.
1321 if (oct >= 2) {
1322 *bpos = '\r';
1323 if (++bpos == bend) bpos = bstart;
1324 *bpos = '\n';
1325 pos = 0;
1326 ict--;
1327 oct -= 2;
1329 goto out_done; /* no space or buffer got changed */
1331 break;
1332 case '\t':
1333 /* Best guess for the tab length. */
1334 tablen = TAB_SIZE - (pos & TAB_MASK);
1336 if ((tp->tty_termios.c_oflag & (OPOST|XTABS))
1337 == (OPOST|XTABS)) {
1338 /* Tabs must be expanded. */
1339 if (oct >= tablen) {
1340 pos += tablen;
1341 ict--;
1342 oct -= tablen;
1343 do {
1344 *bpos = ' ';
1345 if (++bpos == bend) bpos = bstart;
1346 } while (--tablen != 0);
1348 goto out_done;
1350 /* Tabs are output directly. */
1351 pos += tablen;
1352 break;
1353 default:
1354 /* Assume any other character prints as one character. */
1355 pos++;
1357 if (++bpos == bend) bpos = bstart;
1358 ict--;
1359 oct--;
1361 out_done:
1362 tp->tty_position = pos & TAB_MASK;
1364 *icount -= ict; /* [io]ct are the number of chars not used */
1365 *ocount -= oct; /* *[io]count are the number of chars that are used */
1368 /*===========================================================================*
1369 * dev_ioctl *
1370 *===========================================================================*/
1371 PRIVATE void dev_ioctl(tp)
1372 tty_t *tp;
1374 /* The ioctl's TCSETSW, TCSETSF and TCDRAIN wait for output to finish to make
1375 * sure that an attribute change doesn't affect the processing of current
1376 * output. Once output finishes the ioctl is executed as in do_ioctl().
1378 int result = EINVAL;
1380 if (tp->tty_outleft > 0) return; /* output not finished */
1382 if (tp->tty_ioreq != TCDRAIN) {
1383 if (tp->tty_ioreq == TCSETSF) tty_icancel(tp);
1384 if(tp->tty_io_safe) {
1385 result = sys_safecopyfrom(tp->tty_ioproc, tp->tty_iovir_g, 0,
1386 (vir_bytes) &tp->tty_termios,
1387 (vir_bytes) sizeof(tp->tty_termios), D);
1388 } else {
1389 result = sys_vircopy(tp->tty_ioproc, D, tp->tty_iovir_g,
1390 SELF, D, (vir_bytes) &tp->tty_termios,
1391 (vir_bytes) sizeof(tp->tty_termios));
1393 setattr(tp);
1395 tp->tty_ioreq = 0;
1396 notify(tp->tty_iocaller);
1397 tp->tty_iorevived = 1;
1398 tp->tty_iostatus = result;
1401 /*===========================================================================*
1402 * setattr *
1403 *===========================================================================*/
1404 PRIVATE void setattr(tp)
1405 tty_t *tp;
1407 /* Apply the new line attributes (raw/canonical, line speed, etc.) */
1408 u16_t *inp;
1409 int count;
1411 if (!(tp->tty_termios.c_lflag & ICANON)) {
1412 /* Raw mode; put a "line break" on all characters in the input queue.
1413 * It is undefined what happens to the input queue when ICANON is
1414 * switched off, a process should use TCSAFLUSH to flush the queue.
1415 * Keeping the queue to preserve typeahead is the Right Thing, however
1416 * when a process does use TCSANOW to switch to raw mode.
1418 count = tp->tty_eotct = tp->tty_incount;
1419 inp = tp->tty_intail;
1420 while (count > 0) {
1421 *inp |= IN_EOT;
1422 if (++inp == bufend(tp->tty_inbuf)) inp = tp->tty_inbuf;
1423 --count;
1427 /* Inspect MIN and TIME. */
1428 settimer(tp, FALSE);
1429 if (tp->tty_termios.c_lflag & ICANON) {
1430 /* No MIN & TIME in canonical mode. */
1431 tp->tty_min = 1;
1432 } else {
1433 /* In raw mode MIN is the number of chars wanted, and TIME how long
1434 * to wait for them. With interesting exceptions if either is zero.
1436 tp->tty_min = tp->tty_termios.c_cc[VMIN];
1437 if (tp->tty_min == 0 && tp->tty_termios.c_cc[VTIME] > 0)
1438 tp->tty_min = 1;
1441 if (!(tp->tty_termios.c_iflag & IXON)) {
1442 /* No start/stop output control, so don't leave output inhibited. */
1443 tp->tty_inhibited = RUNNING;
1444 tp->tty_events = 1;
1447 /* Setting the output speed to zero hangs up the phone. */
1448 if (tp->tty_termios.c_ospeed == B0) sigchar(tp, SIGHUP, 1);
1450 /* Set new line speed, character size, etc at the device level. */
1451 (*tp->tty_ioctl)(tp, 0);
1454 /*===========================================================================*
1455 * tty_reply *
1456 *===========================================================================*/
1457 PUBLIC void
1458 tty_reply_f(
1459 file, line, code, replyee, proc_nr, status)
1460 char *file;
1461 int line;
1462 int code; /* TASK_REPLY or REVIVE */
1463 int replyee; /* destination address for the reply */
1464 int proc_nr; /* to whom should the reply go? */
1465 int status; /* reply code */
1467 /* Send a reply to a process that wanted to read or write data. */
1468 message tty_mess;
1470 tty_mess.m_type = code;
1471 tty_mess.REP_ENDPT = proc_nr;
1472 tty_mess.REP_STATUS = status;
1474 /* TTY is not supposed to send a TTY_REVIVE message. The
1475 * REVIVE message is gone, TTY_REVIVE is only used as an internal
1476 * placeholder for something that is not supposed to be a message.
1478 if(code == TTY_REVIVE) {
1479 panicing = 1;
1480 printf("%s:%d: ", file, line);
1481 panic("TTY","tty_reply sending TTY_REVIVE", NO_NUM);
1484 status = sendnb(replyee, &tty_mess);
1485 if (status != OK)
1486 printf("tty`tty_reply: send to %d failed: %d\n", replyee, status);
1489 /*===========================================================================*
1490 * sigchar *
1491 *===========================================================================*/
1492 PUBLIC void sigchar(tp, sig, mayflush)
1493 register tty_t *tp;
1494 int sig; /* SIGINT, SIGQUIT, SIGKILL or SIGHUP */
1495 int mayflush;
1497 /* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard or SIGHUP from
1498 * a tty close, "stty 0", or a real RS-232 hangup. MM will send the signal to
1499 * the process group (INT, QUIT), all processes (KILL), or the session leader
1500 * (HUP).
1502 int status;
1504 if (tp->tty_pgrp != 0) {
1505 if (OK != (status = sys_kill(tp->tty_pgrp, sig))) {
1506 panic("TTY","Error, call to sys_kill failed", status);
1510 if (mayflush && !(tp->tty_termios.c_lflag & NOFLSH)) {
1511 tp->tty_incount = tp->tty_eotct = 0; /* kill earlier input */
1512 tp->tty_intail = tp->tty_inhead;
1513 (*tp->tty_ocancel)(tp, 0); /* kill all output */
1514 tp->tty_inhibited = RUNNING;
1515 tp->tty_events = 1;
1519 /*===========================================================================*
1520 * tty_icancel *
1521 *===========================================================================*/
1522 PRIVATE void tty_icancel(tp)
1523 register tty_t *tp;
1525 /* Discard all pending input, tty buffer or device. */
1527 tp->tty_incount = tp->tty_eotct = 0;
1528 tp->tty_intail = tp->tty_inhead;
1529 (*tp->tty_icancel)(tp, 0);
1532 /*===========================================================================*
1533 * tty_init *
1534 *===========================================================================*/
1535 PRIVATE void tty_init()
1537 /* Initialize tty structure and call device initialization routines. */
1539 register tty_t *tp;
1540 int s;
1542 system_hz = sys_hz();
1544 /* Initialize the terminal lines. */
1545 for (tp = FIRST_TTY,s=0; tp < END_TTY; tp++,s++) {
1547 tp->tty_index = s;
1549 tmr_inittimer(&tp->tty_tmr);
1551 tp->tty_intail = tp->tty_inhead = tp->tty_inbuf;
1552 tp->tty_min = 1;
1553 tp->tty_termios = termios_defaults;
1554 tp->tty_icancel = tp->tty_ocancel = tp->tty_ioctl = tp->tty_close =
1555 tty_devnop;
1556 if (tp < tty_addr(NR_CONS)) {
1557 scr_init(tp);
1559 /* Initialize the keyboard driver. */
1560 kb_init(tp);
1562 tp->tty_minor = CONS_MINOR + s;
1563 } else
1564 if (tp < tty_addr(NR_CONS+NR_RS_LINES)) {
1565 rs_init(tp);
1566 tp->tty_minor = RS232_MINOR + s-NR_CONS;
1567 } else {
1568 pty_init(tp);
1569 tp->tty_minor = s - (NR_CONS+NR_RS_LINES) + TTYPX_MINOR;
1575 /*===========================================================================*
1576 * tty_timed_out *
1577 *===========================================================================*/
1578 PRIVATE void tty_timed_out(timer_t *tp)
1580 /* This timer has expired. Set the events flag, to force processing. */
1581 tty_t *tty_ptr;
1582 tty_ptr = &tty_table[tmr_arg(tp)->ta_int];
1583 tty_ptr->tty_min = 0; /* force read to succeed */
1584 tty_ptr->tty_events = 1;
1587 /*===========================================================================*
1588 * expire_timers *
1589 *===========================================================================*/
1590 PRIVATE void expire_timers(void)
1592 /* A synchronous alarm message was received. Check if there are any expired
1593 * timers. Possibly set the event flag and reschedule another alarm.
1595 clock_t now; /* current time */
1596 int s;
1598 /* Get the current time to compare the timers against. */
1599 if ((s=getuptime(&now)) != OK)
1600 panic("TTY","Couldn't get uptime from clock.", s);
1602 /* Scan the queue of timers for expired timers. This dispatch the watchdog
1603 * functions of expired timers. Possibly a new alarm call must be scheduled.
1605 tmrs_exptimers(&tty_timers, now, NULL);
1606 if (tty_timers == NULL) tty_next_timeout = TMR_NEVER;
1607 else { /* set new sync alarm */
1608 tty_next_timeout = tty_timers->tmr_exp_time;
1609 if ((s=sys_setalarm(tty_next_timeout, 1)) != OK)
1610 panic("TTY","Couldn't set synchronous alarm.", s);
1614 /*===========================================================================*
1615 * settimer *
1616 *===========================================================================*/
1617 PRIVATE void settimer(tty_ptr, enable)
1618 tty_t *tty_ptr; /* line to set or unset a timer on */
1619 int enable; /* set timer if true, otherwise unset */
1621 clock_t now; /* current time */
1622 clock_t exp_time;
1623 int s;
1625 /* Get the current time to calculate the timeout time. */
1626 if ((s=getuptime(&now)) != OK)
1627 panic("TTY","Couldn't get uptime from clock.", s);
1628 if (enable) {
1629 exp_time = now + tty_ptr->tty_termios.c_cc[VTIME] * (system_hz/10);
1630 /* Set a new timer for enabling the TTY events flags. */
1631 tmrs_settimer(&tty_timers, &tty_ptr->tty_tmr,
1632 exp_time, tty_timed_out, NULL);
1633 } else {
1634 /* Remove the timer from the active and expired lists. */
1635 tmrs_clrtimer(&tty_timers, &tty_ptr->tty_tmr, NULL);
1638 /* Now check if a new alarm must be scheduled. This happens when the front
1639 * of the timers queue was disabled or reinserted at another position, or
1640 * when a new timer was added to the front.
1642 if (tty_timers == NULL) tty_next_timeout = TMR_NEVER;
1643 else if (tty_timers->tmr_exp_time != tty_next_timeout) {
1644 tty_next_timeout = tty_timers->tmr_exp_time;
1645 if ((s=sys_setalarm(tty_next_timeout, 1)) != OK)
1646 panic("TTY","Couldn't set synchronous alarm.", s);
1650 /*===========================================================================*
1651 * tty_devnop *
1652 *===========================================================================*/
1653 PUBLIC int tty_devnop(tp, try)
1654 tty_t *tp;
1655 int try;
1657 /* Some functions need not be implemented at the device level. */
1658 return 0;
1661 /*===========================================================================*
1662 * do_select *
1663 *===========================================================================*/
1664 PRIVATE void do_select(tp, m_ptr)
1665 register tty_t *tp; /* pointer to tty struct */
1666 register message *m_ptr; /* pointer to message sent to the task */
1668 int ops, ready_ops = 0, watch;
1670 ops = m_ptr->IO_ENDPT & (SEL_RD|SEL_WR|SEL_ERR);
1671 watch = (m_ptr->IO_ENDPT & SEL_NOTIFY) ? 1 : 0;
1673 ready_ops = select_try(tp, ops);
1675 if (!ready_ops && ops && watch) {
1676 tp->tty_select_ops |= ops;
1677 tp->tty_select_proc = m_ptr->m_source;
1680 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, ready_ops);
1682 return;