]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/subr_terminal.c
src.conf.5: Regen after removing MK_NVME
[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/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 }