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