]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/subr_terminal.c
libevent: Import libevent 2.1.12
[FreeBSD/FreeBSD.git] / sys / kern / subr_terminal.c
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 }