2 * Copyright (c) 2009 The FreeBSD Foundation
5 * This software was developed by Ed Schouten under sponsorship from the
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
35 #include <sys/consio.h>
36 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/mutex.h>
40 #include <sys/systm.h>
41 #include <sys/terminal.h>
44 #include <machine/stdarg.h>
46 static MALLOC_DEFINE(M_TERMINAL
, "terminal", "terminal device");
51 * Normally we don't need to lock down the terminal emulator, because
52 * the TTY lock is already held when calling teken_input().
53 * Unfortunately this is not the case when the terminal acts as a
54 * console device, because cnputc() can be called at the same time.
55 * This means terminals may need to be locked down using a spin lock.
57 #define TERMINAL_LOCK(tm) do { \
58 if ((tm)->tm_flags & TF_CONS) \
59 mtx_lock_spin(&(tm)->tm_mtx); \
60 else if ((tm)->tm_tty != NULL) \
61 tty_lock((tm)->tm_tty); \
63 #define TERMINAL_UNLOCK(tm) do { \
64 if ((tm)->tm_flags & TF_CONS) \
65 mtx_unlock_spin(&(tm)->tm_mtx); \
66 else if ((tm)->tm_tty != NULL) \
67 tty_unlock((tm)->tm_tty); \
69 #define TERMINAL_LOCK_TTY(tm) do { \
70 if ((tm)->tm_flags & TF_CONS) \
71 mtx_lock_spin(&(tm)->tm_mtx); \
73 #define TERMINAL_UNLOCK_TTY(tm) do { \
74 if ((tm)->tm_flags & TF_CONS) \
75 mtx_unlock_spin(&(tm)->tm_mtx); \
77 #define TERMINAL_LOCK_CONS(tm) mtx_lock_spin(&(tm)->tm_mtx)
78 #define TERMINAL_UNLOCK_CONS(tm) mtx_unlock_spin(&(tm)->tm_mtx)
84 static tsw_open_t termtty_open
;
85 static tsw_close_t termtty_close
;
86 static tsw_outwakeup_t termtty_outwakeup
;
87 static tsw_ioctl_t termtty_ioctl
;
88 static tsw_mmap_t termtty_mmap
;
90 static struct ttydevsw terminal_tty_class
= {
91 .tsw_open
= termtty_open
,
92 .tsw_close
= termtty_close
,
93 .tsw_outwakeup
= termtty_outwakeup
,
94 .tsw_ioctl
= termtty_ioctl
,
95 .tsw_mmap
= termtty_mmap
,
99 * Terminal emulator routines.
102 static tf_bell_t termteken_bell
;
103 static tf_cursor_t termteken_cursor
;
104 static tf_putchar_t termteken_putchar
;
105 static tf_fill_t termteken_fill
;
106 static tf_copy_t termteken_copy
;
107 static tf_param_t termteken_param
;
108 static tf_respond_t termteken_respond
;
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_param
= termteken_param
,
117 .tf_respond
= termteken_respond
,
120 /* Kernel message formatting. */
121 static const teken_attr_t kernel_message
= {
122 .ta_fgcolor
= TCHAR_FGCOLOR(TERMINAL_KERN_ATTR
),
123 .ta_bgcolor
= TCHAR_BGCOLOR(TERMINAL_KERN_ATTR
),
124 .ta_format
= TCHAR_FORMAT(TERMINAL_KERN_ATTR
)
127 static const teken_attr_t default_message
= {
128 .ta_fgcolor
= TCHAR_FGCOLOR(TERMINAL_NORM_ATTR
),
129 .ta_bgcolor
= TCHAR_BGCOLOR(TERMINAL_NORM_ATTR
),
130 .ta_format
= TCHAR_FORMAT(TERMINAL_NORM_ATTR
)
133 #define TCHAR_CREATE(c, a) ((c) | TFORMAT((a)->ta_format) | \
134 TCOLOR_FG(teken_256to8((a)->ta_fgcolor)) | \
135 TCOLOR_BG(teken_256to8((a)->ta_bgcolor)))
138 terminal_init(struct terminal
*tm
)
141 if (tm
->tm_flags
& TF_CONS
)
142 mtx_init(&tm
->tm_mtx
, "trmlck", NULL
, MTX_SPIN
);
143 teken_init(&tm
->tm_emulator
, &terminal_drawmethods
, tm
);
144 teken_set_defattr(&tm
->tm_emulator
, &default_message
);
148 terminal_alloc(const struct terminal_class
*tc
, void *softc
)
152 tm
= malloc(sizeof(struct terminal
), M_TERMINAL
, M_WAITOK
|M_ZERO
);
156 tm
->tm_softc
= softc
;
162 terminal_sync_ttysize(struct terminal
*tm
)
171 tty_set_winsize(tp
, &tm
->tm_winsize
);
176 terminal_maketty(struct terminal
*tm
, const char *fmt
, ...)
183 vsnrprintf(name
, sizeof name
, 32, fmt
, ap
);
186 tp
= tty_alloc(&terminal_tty_class
, tm
);
187 tty_makedev(tp
, NULL
, "%s", name
);
189 terminal_sync_ttysize(tm
);
193 terminal_set_cursor(struct terminal
*tm
, const term_pos_t
*pos
)
196 teken_set_cursor(&tm
->tm_emulator
, pos
);
200 terminal_set_winsize_blank(struct terminal
*tm
, const struct winsize
*size
,
201 int blank
, const term_attr_t
*attr
)
205 tm
->tm_winsize
= *size
;
207 r
.tr_begin
.tp_row
= r
.tr_begin
.tp_col
= 0;
208 r
.tr_end
.tp_row
= size
->ws_row
;
209 r
.tr_end
.tp_col
= size
->ws_col
;
213 teken_set_winsize_noreset(&tm
->tm_emulator
, &r
.tr_end
);
215 teken_set_winsize(&tm
->tm_emulator
, &r
.tr_end
);
218 if ((blank
!= 0) && !(tm
->tm_flags
& TF_MUTE
))
219 tm
->tm_class
->tc_fill(tm
, &r
,
220 TCHAR_CREATE((teken_char_t
)' ', attr
));
222 terminal_sync_ttysize(tm
);
226 terminal_set_winsize(struct terminal
*tm
, const struct winsize
*size
)
229 terminal_set_winsize_blank(tm
, size
, 1,
230 (const term_attr_t
*)&default_message
);
234 * XXX: This function is a kludge. Drivers like vt(4) need to
235 * temporarily stop input when resizing, etc. This should ideally be
236 * handled within the driver.
240 terminal_mute(struct terminal
*tm
, int yes
)
245 tm
->tm_flags
|= TF_MUTE
;
247 tm
->tm_flags
&= ~TF_MUTE
;
252 terminal_input_char(struct terminal
*tm
, term_char_t c
)
261 * Strip off any attributes. Also ignore input of second part of
262 * CJK fullwidth characters, as we don't want to return these
265 if (TCHAR_FORMAT(c
) & TF_CJK_RIGHT
)
267 c
= TCHAR_CHARACTER(c
);
271 * Conversion to UTF-8.
274 ttydisc_rint(tp
, c
, 0);
275 } else if (c
< 0x800) {
281 ttydisc_rint_simple(tp
, str
, sizeof str
);
282 } else if (c
< 0x10000) {
285 0x80 | ((c
>> 6) & 0x3f),
289 ttydisc_rint_simple(tp
, str
, sizeof str
);
293 0x80 | ((c
>> 12) & 0x3f),
294 0x80 | ((c
>> 6) & 0x3f),
298 ttydisc_rint_simple(tp
, str
, sizeof str
);
300 ttydisc_rint_done(tp
);
305 terminal_input_raw(struct terminal
*tm
, char c
)
314 ttydisc_rint(tp
, c
, 0);
315 ttydisc_rint_done(tp
);
320 terminal_input_special(struct terminal
*tm
, unsigned int k
)
329 str
= teken_get_sequence(&tm
->tm_emulator
, k
);
334 ttydisc_rint_simple(tp
, str
, strlen(str
));
335 ttydisc_rint_done(tp
);
340 * Binding with the TTY layer.
344 termtty_open(struct tty
*tp
)
346 struct terminal
*tm
= tty_softc(tp
);
348 tm
->tm_class
->tc_opened(tm
, 1);
353 termtty_close(struct tty
*tp
)
355 struct terminal
*tm
= tty_softc(tp
);
357 tm
->tm_class
->tc_opened(tm
, 0);
361 termtty_outwakeup(struct tty
*tp
)
363 struct terminal
*tm
= tty_softc(tp
);
366 unsigned int flags
= 0;
368 while ((olen
= ttydisc_getc(tp
, obuf
, sizeof obuf
)) > 0) {
369 TERMINAL_LOCK_TTY(tm
);
370 if (!(tm
->tm_flags
& TF_MUTE
)) {
371 tm
->tm_flags
&= ~TF_BELL
;
372 teken_input(&tm
->tm_emulator
, obuf
, olen
);
373 flags
|= tm
->tm_flags
;
375 TERMINAL_UNLOCK_TTY(tm
);
378 tm
->tm_class
->tc_done(tm
);
380 tm
->tm_class
->tc_bell(tm
);
384 termtty_ioctl(struct tty
*tp
, u_long cmd
, caddr_t data
, struct thread
*td
)
386 struct terminal
*tm
= tty_softc(tp
);
391 vid_info_t
*vi
= (vid_info_t
*)data
;
392 const teken_pos_t
*p
;
395 if (vi
->size
!= sizeof(vid_info_t
))
398 /* Already help the console driver by filling in some data. */
399 p
= teken_get_cursor(&tm
->tm_emulator
);
400 vi
->mv_row
= p
->tp_row
;
401 vi
->mv_col
= p
->tp_col
;
403 p
= teken_get_winsize(&tm
->tm_emulator
);
404 vi
->mv_rsz
= p
->tp_row
;
405 vi
->mv_csz
= p
->tp_col
;
407 teken_get_defattr_cons25(&tm
->tm_emulator
, &fg
, &bg
);
408 vi
->mv_norm
.fore
= fg
;
409 vi
->mv_norm
.back
= bg
;
410 /* XXX: keep vidcontrol happy; bold backgrounds. */
411 vi
->mv_rev
.fore
= bg
;
412 vi
->mv_rev
.back
= fg
& 0x7;
418 * Unlike various other drivers, this driver will never
419 * deallocate TTYs. This means it's safe to temporarily unlock
420 * the TTY when handling ioctls.
423 error
= tm
->tm_class
->tc_ioctl(tm
, cmd
, data
, td
);
429 termtty_mmap(struct tty
*tp
, vm_ooffset_t offset
, vm_paddr_t
* paddr
,
430 int nprot
, vm_memattr_t
*memattr
)
432 struct terminal
*tm
= tty_softc(tp
);
434 return (tm
->tm_class
->tc_mmap(tm
, offset
, paddr
, nprot
, memattr
));
438 * Binding with the kernel and debug console.
441 static cn_probe_t termcn_cnprobe
;
442 static cn_init_t termcn_cninit
;
443 static cn_term_t termcn_cnterm
;
444 static cn_getc_t termcn_cngetc
;
445 static cn_putc_t termcn_cnputc
;
446 static cn_grab_t termcn_cngrab
;
447 static cn_ungrab_t termcn_cnungrab
;
449 const struct consdev_ops termcn_cnops
= {
450 .cn_probe
= termcn_cnprobe
,
451 .cn_init
= termcn_cninit
,
452 .cn_term
= termcn_cnterm
,
453 .cn_getc
= termcn_cngetc
,
454 .cn_putc
= termcn_cnputc
,
455 .cn_grab
= termcn_cngrab
,
456 .cn_ungrab
= termcn_cnungrab
,
460 termcn_cnregister(struct terminal
*tm
)
466 cp
= malloc(sizeof(struct consdev
), M_TERMINAL
,
468 cp
->cn_ops
= &termcn_cnops
;
470 cp
->cn_pri
= CN_INTERNAL
;
471 sprintf(cp
->cn_name
, "ttyv0");
473 tm
->tm_flags
= TF_CONS
;
479 /* Attach terminal as console. */
484 termcn_cngrab(struct consdev
*cp
)
486 struct terminal
*tm
= cp
->cn_arg
;
488 tm
->tm_class
->tc_cngrab(tm
);
492 termcn_cnungrab(struct consdev
*cp
)
494 struct terminal
*tm
= cp
->cn_arg
;
496 tm
->tm_class
->tc_cnungrab(tm
);
500 termcn_cnprobe(struct consdev
*cp
)
502 struct terminal
*tm
= cp
->cn_arg
;
505 cp
->cn_pri
= CN_DEAD
;
512 tm
->tm_class
->tc_cnprobe(tm
, cp
);
516 termcn_cninit(struct consdev
*cp
)
522 termcn_cnterm(struct consdev
*cp
)
528 termcn_cngetc(struct consdev
*cp
)
530 struct terminal
*tm
= cp
->cn_arg
;
532 return (tm
->tm_class
->tc_cngetc(tm
));
536 termcn_cnputc(struct consdev
*cp
, int c
)
538 struct terminal
*tm
= cp
->cn_arg
;
542 TERMINAL_LOCK_CONS(tm
);
543 if (!(tm
->tm_flags
& TF_MUTE
)) {
544 backup
= *teken_get_curattr(&tm
->tm_emulator
);
545 teken_set_curattr(&tm
->tm_emulator
, &kernel_message
);
546 teken_input(&tm
->tm_emulator
, &cv
, 1);
547 teken_set_curattr(&tm
->tm_emulator
, &backup
);
549 TERMINAL_UNLOCK_CONS(tm
);
551 tm
->tm_class
->tc_done(tm
);
555 * Binding with the terminal emulator.
559 termteken_bell(void *softc
)
561 struct terminal
*tm
= softc
;
563 tm
->tm_flags
|= TF_BELL
;
567 termteken_cursor(void *softc
, const teken_pos_t
*p
)
569 struct terminal
*tm
= softc
;
571 tm
->tm_class
->tc_cursor(tm
, p
);
575 termteken_putchar(void *softc
, const teken_pos_t
*p
, teken_char_t c
,
576 const teken_attr_t
*a
)
578 struct terminal
*tm
= softc
;
580 tm
->tm_class
->tc_putchar(tm
, p
, TCHAR_CREATE(c
, a
));
584 termteken_fill(void *softc
, const teken_rect_t
*r
, teken_char_t c
,
585 const teken_attr_t
*a
)
587 struct terminal
*tm
= softc
;
589 tm
->tm_class
->tc_fill(tm
, r
, TCHAR_CREATE(c
, a
));
593 termteken_copy(void *softc
, const teken_rect_t
*r
, const teken_pos_t
*p
)
595 struct terminal
*tm
= softc
;
597 tm
->tm_class
->tc_copy(tm
, r
, p
);
601 termteken_param(void *softc
, int cmd
, unsigned int arg
)
603 struct terminal
*tm
= softc
;
605 tm
->tm_class
->tc_param(tm
, cmd
, arg
);
609 termteken_respond(void *softc
, const void *buf
, size_t len
)
612 struct terminal
*tm
= softc
;
616 * Only inject a response into the TTY if the data actually
617 * originated from the TTY.
619 * XXX: This cannot be done right now. The TTY could pick up
620 * other locks. It could also in theory cause loops, when the
621 * TTY performs echoing of a command that generates even more
628 ttydisc_rint_simple(tp
, buf
, len
);
629 ttydisc_rint_done(tp
);