xref: /freebsd/sys/kern/subr_terminal.c (revision 4b9d6057)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2009 The FreeBSD Foundation
5  *
6  * This software was developed by Ed Schouten under sponsorship from the
7  * FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/cons.h>
33 #include <sys/consio.h>
34 #include <sys/kernel.h>
35 #include <sys/lock.h>
36 #include <sys/malloc.h>
37 #include <sys/mutex.h>
38 #include <sys/systm.h>
39 #include <sys/terminal.h>
40 #include <sys/tty.h>
41 
42 #include <machine/stdarg.h>
43 
44 static MALLOC_DEFINE(M_TERMINAL, "terminal", "terminal device");
45 
46 /*
47  * Locking.
48  *
49  * Normally we don't need to lock down the terminal emulator, because
50  * the TTY lock is already held when calling teken_input().
51  * Unfortunately this is not the case when the terminal acts as a
52  * console device, because cnputc() can be called at the same time.
53  * This means terminals may need to be locked down using a spin lock.
54  */
55 #define	TERMINAL_LOCK(tm)	do {					\
56 	if ((tm)->tm_flags & TF_CONS)					\
57 		mtx_lock_spin(&(tm)->tm_mtx);				\
58 	else if ((tm)->tm_tty != NULL)					\
59 		tty_lock((tm)->tm_tty);					\
60 } while (0)
61 #define	TERMINAL_UNLOCK(tm)	do {					\
62 	if ((tm)->tm_flags & TF_CONS)					\
63 		mtx_unlock_spin(&(tm)->tm_mtx);				\
64 	else if ((tm)->tm_tty != NULL)					\
65 		tty_unlock((tm)->tm_tty);				\
66 } while (0)
67 #define	TERMINAL_LOCK_TTY(tm)	do {					\
68 	if ((tm)->tm_flags & TF_CONS)					\
69 		mtx_lock_spin(&(tm)->tm_mtx);				\
70 } while (0)
71 #define	TERMINAL_UNLOCK_TTY(tm)	do {					\
72 	if ((tm)->tm_flags & TF_CONS)					\
73 		mtx_unlock_spin(&(tm)->tm_mtx);				\
74 } while (0)
75 #define	TERMINAL_LOCK_CONS(tm)		mtx_lock_spin(&(tm)->tm_mtx)
76 #define	TERMINAL_UNLOCK_CONS(tm)	mtx_unlock_spin(&(tm)->tm_mtx)
77 
78 /*
79  * TTY routines.
80  */
81 
82 static tsw_open_t	termtty_open;
83 static tsw_close_t	termtty_close;
84 static tsw_outwakeup_t	termtty_outwakeup;
85 static tsw_ioctl_t	termtty_ioctl;
86 static tsw_mmap_t	termtty_mmap;
87 
88 static struct ttydevsw terminal_tty_class = {
89 	.tsw_open	= termtty_open,
90 	.tsw_close	= termtty_close,
91 	.tsw_outwakeup	= termtty_outwakeup,
92 	.tsw_ioctl	= termtty_ioctl,
93 	.tsw_mmap	= termtty_mmap,
94 };
95 
96 /*
97  * Terminal emulator routines.
98  */
99 
100 static tf_bell_t	termteken_bell;
101 static tf_cursor_t	termteken_cursor;
102 static tf_putchar_t	termteken_putchar;
103 static tf_fill_t	termteken_fill;
104 static tf_copy_t	termteken_copy;
105 static tf_pre_input_t	termteken_pre_input;
106 static tf_post_input_t	termteken_post_input;
107 static tf_param_t	termteken_param;
108 static tf_respond_t	termteken_respond;
109 
110 static teken_funcs_t terminal_drawmethods = {
111 	.tf_bell	= termteken_bell,
112 	.tf_cursor	= termteken_cursor,
113 	.tf_putchar	= termteken_putchar,
114 	.tf_fill	= termteken_fill,
115 	.tf_copy	= termteken_copy,
116 	.tf_pre_input	= termteken_pre_input,
117 	.tf_post_input	= termteken_post_input,
118 	.tf_param	= termteken_param,
119 	.tf_respond	= termteken_respond,
120 };
121 
122 /* Kernel message formatting. */
123 static teken_attr_t kernel_message = {
124 	.ta_fgcolor	= TCHAR_FGCOLOR(TERMINAL_KERN_ATTR),
125 	.ta_bgcolor	= TCHAR_BGCOLOR(TERMINAL_KERN_ATTR),
126 	.ta_format	= TCHAR_FORMAT(TERMINAL_KERN_ATTR)
127 };
128 
129 static teken_attr_t default_message = {
130 	.ta_fgcolor	= TCHAR_FGCOLOR(TERMINAL_NORM_ATTR),
131 	.ta_bgcolor	= TCHAR_BGCOLOR(TERMINAL_NORM_ATTR),
132 	.ta_format	= TCHAR_FORMAT(TERMINAL_NORM_ATTR)
133 };
134 
135 /* Fudge fg brightness as TF_BOLD (shifted). */
136 #define	TCOLOR_FG_FUDGED(color) __extension__ ({			\
137 	teken_color_t _c;						\
138 									\
139 	_c = (color);							\
140 	TCOLOR_FG(_c & 7) | ((_c & 8) << 18);				\
141 })
142 
143 /* Fudge bg brightness as TF_BLINK (shifted). */
144 #define	TCOLOR_BG_FUDGED(color) __extension__ ({			\
145 	teken_color_t _c;						\
146 									\
147 	_c = (color);							\
148 	TCOLOR_BG(_c & 7) | ((_c & 8) << 20);				\
149 })
150 
151 #define	TCOLOR_256TO16(color) __extension__ ({				\
152 	teken_color_t _c;						\
153 									\
154 	_c = (color);							\
155 	if (_c >= 16)							\
156 		_c = teken_256to16(_c);					\
157 	_c;								\
158 })
159 
160 #define	TCHAR_CREATE(c, a)	((c) | TFORMAT((a)->ta_format) |	\
161 	TCOLOR_FG_FUDGED(TCOLOR_256TO16((a)->ta_fgcolor)) |		\
162 	TCOLOR_BG_FUDGED(TCOLOR_256TO16((a)->ta_bgcolor)))
163 
164 static void
165 terminal_init(struct terminal *tm)
166 {
167 	int fg, bg;
168 
169 	if (tm->tm_flags & TF_CONS)
170 		mtx_init(&tm->tm_mtx, "trmlck", NULL, MTX_SPIN);
171 
172 	teken_init(&tm->tm_emulator, &terminal_drawmethods, tm);
173 
174 	fg = bg = -1;
175 	TUNABLE_INT_FETCH("teken.fg_color", &fg);
176 	TUNABLE_INT_FETCH("teken.bg_color", &bg);
177 
178 	if (fg != -1) {
179 		default_message.ta_fgcolor = fg;
180 		kernel_message.ta_fgcolor = fg;
181 	}
182 	if (bg != -1) {
183 		default_message.ta_bgcolor = bg;
184 		kernel_message.ta_bgcolor = bg;
185 	}
186 
187 	if (default_message.ta_bgcolor == TC_WHITE) {
188 		default_message.ta_bgcolor |= TC_LIGHT;
189 		kernel_message.ta_bgcolor |= TC_LIGHT;
190 	}
191 
192 	if (default_message.ta_bgcolor == TC_BLACK &&
193 	    default_message.ta_fgcolor < TC_NCOLORS)
194 		kernel_message.ta_fgcolor |= TC_LIGHT;
195 	teken_set_defattr(&tm->tm_emulator, &default_message);
196 }
197 
198 struct terminal *
199 terminal_alloc(const struct terminal_class *tc, void *softc)
200 {
201 	struct terminal *tm;
202 
203 	tm = malloc(sizeof(struct terminal), M_TERMINAL, M_WAITOK|M_ZERO);
204 	terminal_init(tm);
205 
206 	tm->tm_class = tc;
207 	tm->tm_softc = softc;
208 
209 	return (tm);
210 }
211 
212 static void
213 terminal_sync_ttysize(struct terminal *tm)
214 {
215 	struct tty *tp;
216 
217 	tp = tm->tm_tty;
218 	if (tp == NULL)
219 		return;
220 
221 	tty_lock(tp);
222 	tty_set_winsize(tp, &tm->tm_winsize);
223 	tty_unlock(tp);
224 }
225 
226 void
227 terminal_maketty(struct terminal *tm, const char *fmt, ...)
228 {
229 	struct tty *tp;
230 	char name[8];
231 	va_list ap;
232 
233 	va_start(ap, fmt);
234 	vsnrprintf(name, sizeof name, 32, fmt, ap);
235 	va_end(ap);
236 
237 	tp = tty_alloc(&terminal_tty_class, tm);
238 	tty_makedev(tp, NULL, "%s", name);
239 	tm->tm_tty = tp;
240 	terminal_sync_ttysize(tm);
241 }
242 
243 void
244 terminal_set_cursor(struct terminal *tm, const term_pos_t *pos)
245 {
246 
247 	teken_set_cursor(&tm->tm_emulator, pos);
248 }
249 
250 void
251 terminal_set_winsize_blank(struct terminal *tm, const struct winsize *size,
252     int blank, const term_attr_t *attr)
253 {
254 	term_rect_t r;
255 
256 	tm->tm_winsize = *size;
257 
258 	r.tr_begin.tp_row = r.tr_begin.tp_col = 0;
259 	r.tr_end.tp_row = size->ws_row;
260 	r.tr_end.tp_col = size->ws_col;
261 
262 	TERMINAL_LOCK(tm);
263 	if (blank == 0)
264 		teken_set_winsize_noreset(&tm->tm_emulator, &r.tr_end);
265 	else
266 		teken_set_winsize(&tm->tm_emulator, &r.tr_end);
267 	TERMINAL_UNLOCK(tm);
268 
269 	if ((blank != 0) && !(tm->tm_flags & TF_MUTE))
270 		tm->tm_class->tc_fill(tm, &r,
271 		    TCHAR_CREATE((teken_char_t)' ', attr));
272 
273 	terminal_sync_ttysize(tm);
274 }
275 
276 void
277 terminal_set_winsize(struct terminal *tm, const struct winsize *size)
278 {
279 
280 	terminal_set_winsize_blank(tm, size, 1,
281 	    (const term_attr_t *)&default_message);
282 }
283 
284 /*
285  * XXX: This function is a kludge.  Drivers like vt(4) need to
286  * temporarily stop input when resizing, etc.  This should ideally be
287  * handled within the driver.
288  */
289 
290 void
291 terminal_mute(struct terminal *tm, int yes)
292 {
293 
294 	TERMINAL_LOCK(tm);
295 	if (yes)
296 		tm->tm_flags |= TF_MUTE;
297 	else
298 		tm->tm_flags &= ~TF_MUTE;
299 	TERMINAL_UNLOCK(tm);
300 }
301 
302 void
303 terminal_input_char(struct terminal *tm, term_char_t c)
304 {
305 	struct tty *tp;
306 
307 	tp = tm->tm_tty;
308 	if (tp == NULL)
309 		return;
310 
311 	/*
312 	 * Strip off any attributes. Also ignore input of second part of
313 	 * CJK fullwidth characters, as we don't want to return these
314 	 * characters twice.
315 	 */
316 	if (TCHAR_FORMAT(c) & TF_CJK_RIGHT)
317 		return;
318 	c = TCHAR_CHARACTER(c);
319 
320 	tty_lock(tp);
321 	/*
322 	 * Conversion to UTF-8.
323 	 */
324 	if (c < 0x80) {
325 		ttydisc_rint(tp, c, 0);
326 	} else if (c < 0x800) {
327 		char str[2] = {
328 			0xc0 | (c >> 6),
329 			0x80 | (c & 0x3f)
330 		};
331 
332 		ttydisc_rint_simple(tp, str, sizeof str);
333 	} else if (c < 0x10000) {
334 		char str[3] = {
335 			0xe0 | (c >> 12),
336 			0x80 | ((c >> 6) & 0x3f),
337 			0x80 | (c & 0x3f)
338 		};
339 
340 		ttydisc_rint_simple(tp, str, sizeof str);
341 	} else {
342 		char str[4] = {
343 			0xf0 | (c >> 18),
344 			0x80 | ((c >> 12) & 0x3f),
345 			0x80 | ((c >> 6) & 0x3f),
346 			0x80 | (c & 0x3f)
347 		};
348 
349 		ttydisc_rint_simple(tp, str, sizeof str);
350 	}
351 	ttydisc_rint_done(tp);
352 	tty_unlock(tp);
353 }
354 
355 void
356 terminal_input_raw(struct terminal *tm, char c)
357 {
358 	struct tty *tp;
359 
360 	tp = tm->tm_tty;
361 	if (tp == NULL)
362 		return;
363 
364 	tty_lock(tp);
365 	ttydisc_rint(tp, c, 0);
366 	ttydisc_rint_done(tp);
367 	tty_unlock(tp);
368 }
369 
370 void
371 terminal_input_special(struct terminal *tm, unsigned int k)
372 {
373 	struct tty *tp;
374 	const char *str;
375 
376 	tp = tm->tm_tty;
377 	if (tp == NULL)
378 		return;
379 
380 	str = teken_get_sequence(&tm->tm_emulator, k);
381 	if (str == NULL)
382 		return;
383 
384 	tty_lock(tp);
385 	ttydisc_rint_simple(tp, str, strlen(str));
386 	ttydisc_rint_done(tp);
387 	tty_unlock(tp);
388 }
389 
390 /*
391  * Binding with the TTY layer.
392  */
393 
394 static int
395 termtty_open(struct tty *tp)
396 {
397 	struct terminal *tm = tty_softc(tp);
398 
399 	tm->tm_class->tc_opened(tm, 1);
400 	return (0);
401 }
402 
403 static void
404 termtty_close(struct tty *tp)
405 {
406 	struct terminal *tm = tty_softc(tp);
407 
408 	tm->tm_class->tc_opened(tm, 0);
409 }
410 
411 static void
412 termtty_outwakeup(struct tty *tp)
413 {
414 	struct terminal *tm = tty_softc(tp);
415 	char obuf[128];
416 	size_t olen;
417 	unsigned int flags = 0;
418 
419 	while ((olen = ttydisc_getc(tp, obuf, sizeof obuf)) > 0) {
420 		TERMINAL_LOCK_TTY(tm);
421 		if (!(tm->tm_flags & TF_MUTE)) {
422 			tm->tm_flags &= ~TF_BELL;
423 			teken_input(&tm->tm_emulator, obuf, olen);
424 			flags |= tm->tm_flags;
425 		}
426 		TERMINAL_UNLOCK_TTY(tm);
427 	}
428 
429 	TERMINAL_LOCK_TTY(tm);
430 	if (!(tm->tm_flags & TF_MUTE))
431 		tm->tm_class->tc_done(tm);
432 	TERMINAL_UNLOCK_TTY(tm);
433 	if (flags & TF_BELL)
434 		tm->tm_class->tc_bell(tm);
435 }
436 
437 static int
438 termtty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
439 {
440 	struct terminal *tm = tty_softc(tp);
441 	int error;
442 
443 	switch (cmd) {
444 	case CONS_GETINFO: {
445 		vid_info_t *vi = (vid_info_t *)data;
446 		const teken_pos_t *p;
447 		int fg, bg;
448 
449 		if (vi->size != sizeof(vid_info_t))
450 			return (EINVAL);
451 
452 		/* Already help the console driver by filling in some data. */
453 		p = teken_get_cursor(&tm->tm_emulator);
454 		vi->mv_row = p->tp_row;
455 		vi->mv_col = p->tp_col;
456 
457 		p = teken_get_winsize(&tm->tm_emulator);
458 		vi->mv_rsz = p->tp_row;
459 		vi->mv_csz = p->tp_col;
460 
461 		teken_get_defattr_cons25(&tm->tm_emulator, &fg, &bg);
462 		vi->mv_norm.fore = fg;
463 		vi->mv_norm.back = bg;
464 		/* XXX: keep vidcontrol happy; bold backgrounds. */
465 		vi->mv_rev.fore = bg;
466 		vi->mv_rev.back = fg & 0x7;
467 		break;
468 	}
469 	}
470 
471 	/*
472 	 * Unlike various other drivers, this driver will never
473 	 * deallocate TTYs.  This means it's safe to temporarily unlock
474 	 * the TTY when handling ioctls.
475 	 */
476 	tty_unlock(tp);
477 	error = tm->tm_class->tc_ioctl(tm, cmd, data, td);
478 	tty_lock(tp);
479 	if ((error == 0) && (cmd == CONS_CLRHIST)) {
480 		/*
481 		 * Scrollback history has been successfully cleared,
482 		 * so reset the cursor position to the top left of the screen.
483 		 */
484 		teken_pos_t p;
485 		p.tp_row = 0;
486 		p.tp_col = 0;
487 		teken_set_cursor(&tm->tm_emulator, &p);
488 	}
489 	return (error);
490 }
491 
492 static int
493 termtty_mmap(struct tty *tp, vm_ooffset_t offset, vm_paddr_t * paddr,
494     int nprot, vm_memattr_t *memattr)
495 {
496 	struct terminal *tm = tty_softc(tp);
497 
498 	return (tm->tm_class->tc_mmap(tm, offset, paddr, nprot, memattr));
499 }
500 
501 /*
502  * Binding with the kernel and debug console.
503  */
504 
505 static cn_probe_t	termcn_cnprobe;
506 static cn_init_t	termcn_cninit;
507 static cn_term_t	termcn_cnterm;
508 static cn_getc_t	termcn_cngetc;
509 static cn_putc_t	termcn_cnputc;
510 static cn_grab_t	termcn_cngrab;
511 static cn_ungrab_t	termcn_cnungrab;
512 
513 const struct consdev_ops termcn_cnops = {
514 	.cn_probe	= termcn_cnprobe,
515 	.cn_init	= termcn_cninit,
516 	.cn_term	= termcn_cnterm,
517 	.cn_getc	= termcn_cngetc,
518 	.cn_putc	= termcn_cnputc,
519 	.cn_grab	= termcn_cngrab,
520 	.cn_ungrab	= termcn_cnungrab,
521 };
522 
523 void
524 termcn_cnregister(struct terminal *tm)
525 {
526 	struct consdev *cp;
527 
528 	cp = tm->consdev;
529 	if (cp == NULL) {
530 		cp = malloc(sizeof(struct consdev), M_TERMINAL,
531 		    M_WAITOK|M_ZERO);
532 		cp->cn_ops = &termcn_cnops;
533 		cp->cn_arg = tm;
534 		cp->cn_pri = CN_INTERNAL;
535 		sprintf(cp->cn_name, "ttyv0");
536 
537 		tm->tm_flags = TF_CONS;
538 		tm->consdev = cp;
539 
540 		terminal_init(tm);
541 	}
542 
543 	/* Attach terminal as console. */
544 	cnadd(cp);
545 }
546 
547 static void
548 termcn_cngrab(struct consdev *cp)
549 {
550 	struct terminal *tm = cp->cn_arg;
551 
552 	tm->tm_class->tc_cngrab(tm);
553 }
554 
555 static void
556 termcn_cnungrab(struct consdev *cp)
557 {
558 	struct terminal *tm = cp->cn_arg;
559 
560 	tm->tm_class->tc_cnungrab(tm);
561 }
562 
563 static void
564 termcn_cnprobe(struct consdev *cp)
565 {
566 	struct terminal *tm = cp->cn_arg;
567 
568 	if (tm == NULL) {
569 		cp->cn_pri = CN_DEAD;
570 		return;
571 	}
572 
573 	tm->consdev = cp;
574 	terminal_init(tm);
575 
576 	tm->tm_class->tc_cnprobe(tm, cp);
577 }
578 
579 static void
580 termcn_cninit(struct consdev *cp)
581 {
582 
583 }
584 
585 static void
586 termcn_cnterm(struct consdev *cp)
587 {
588 
589 }
590 
591 static int
592 termcn_cngetc(struct consdev *cp)
593 {
594 	struct terminal *tm = cp->cn_arg;
595 
596 	return (tm->tm_class->tc_cngetc(tm));
597 }
598 
599 static void
600 termcn_cnputc(struct consdev *cp, int c)
601 {
602 	struct terminal *tm = cp->cn_arg;
603 	teken_attr_t backup;
604 	char cv = c;
605 
606 	TERMINAL_LOCK_CONS(tm);
607 	if (!(tm->tm_flags & TF_MUTE)) {
608 		backup = *teken_get_curattr(&tm->tm_emulator);
609 		teken_set_curattr(&tm->tm_emulator, &kernel_message);
610 		teken_input(&tm->tm_emulator, &cv, 1);
611 		teken_set_curattr(&tm->tm_emulator, &backup);
612 		tm->tm_class->tc_done(tm);
613 	}
614 	TERMINAL_UNLOCK_CONS(tm);
615 }
616 
617 /*
618  * Binding with the terminal emulator.
619  */
620 
621 static void
622 termteken_bell(void *softc)
623 {
624 	struct terminal *tm = softc;
625 
626 	tm->tm_flags |= TF_BELL;
627 }
628 
629 static void
630 termteken_cursor(void *softc, const teken_pos_t *p)
631 {
632 	struct terminal *tm = softc;
633 
634 	tm->tm_class->tc_cursor(tm, p);
635 }
636 
637 static void
638 termteken_putchar(void *softc, const teken_pos_t *p, teken_char_t c,
639     const teken_attr_t *a)
640 {
641 	struct terminal *tm = softc;
642 
643 	tm->tm_class->tc_putchar(tm, p, TCHAR_CREATE(c, a));
644 }
645 
646 static void
647 termteken_fill(void *softc, const teken_rect_t *r, teken_char_t c,
648     const teken_attr_t *a)
649 {
650 	struct terminal *tm = softc;
651 
652 	tm->tm_class->tc_fill(tm, r, TCHAR_CREATE(c, a));
653 }
654 
655 static void
656 termteken_copy(void *softc, const teken_rect_t *r, const teken_pos_t *p)
657 {
658 	struct terminal *tm = softc;
659 
660 	tm->tm_class->tc_copy(tm, r, p);
661 }
662 
663 static void
664 termteken_pre_input(void *softc)
665 {
666 	struct terminal *tm = softc;
667 
668 	tm->tm_class->tc_pre_input(tm);
669 }
670 
671 static void
672 termteken_post_input(void *softc)
673 {
674 	struct terminal *tm = softc;
675 
676 	tm->tm_class->tc_post_input(tm);
677 }
678 
679 static void
680 termteken_param(void *softc, int cmd, unsigned int arg)
681 {
682 	struct terminal *tm = softc;
683 
684 	tm->tm_class->tc_param(tm, cmd, arg);
685 }
686 
687 static void
688 termteken_respond(void *softc, const void *buf, size_t len)
689 {
690 #if 0
691 	struct terminal *tm = softc;
692 	struct tty *tp;
693 
694 	/*
695 	 * Only inject a response into the TTY if the data actually
696 	 * originated from the TTY.
697 	 *
698 	 * XXX: This cannot be done right now.  The TTY could pick up
699 	 * other locks.  It could also in theory cause loops, when the
700 	 * TTY performs echoing of a command that generates even more
701 	 * input.
702 	 */
703 	tp = tm->tm_tty;
704 	if (tp == NULL)
705 		return;
706 
707 	ttydisc_rint_simple(tp, buf, len);
708 	ttydisc_rint_done(tp);
709 #endif
710 }
711