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