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