4 * Copyright (c) 2006 The DragonFly Project. All rights reserved.
6 * This code is derived from software contributed to The DragonFly Project
7 * by Matthew Dillon <dillon@backplane.com>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
19 * 3. Neither the name of The DragonFly Project nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific, prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
43 #include <sys/fcntl.h>
44 #include <sys/signalvar.h>
45 #include <sys/eventhandler.h>
46 #include <sys/interrupt.h>
48 #include <machine/md_var.h>
53 static int console_stolen_by_kernel
;
54 static struct kqueue_info
*kqueue_console_info
;
55 static struct tty
*kqueue_console_tty
;
57 /************************************************************************
59 ************************************************************************
63 static int vcons_tty_param(struct tty
*tp
, struct termios
*tio
);
64 static void vcons_tty_start(struct tty
*tp
);
65 static void vcons_hardintr(void *tpx
, struct intrframe
*frame __unused
);
67 static d_open_t vcons_open
;
68 static d_close_t vcons_close
;
69 static d_ioctl_t vcons_ioctl
;
71 static struct dev_ops vcons_ops
= {
72 { "vcons", 0, D_TTY
},
74 .d_close
= vcons_close
,
77 .d_ioctl
= vcons_ioctl
,
78 .d_kqfilter
= ttykqfilter
82 vcons_open(struct dev_open_args
*ap
)
84 cdev_t dev
= ap
->a_head
.a_dev
;
88 lwkt_gettoken(&tty_token
);
89 tp
= dev
->si_tty
= ttymalloc(dev
->si_tty
);
91 #define ISSET(t, f) ((t) & (f))
93 if ((tp
->t_state
& TS_ISOPEN
) == 0) {
94 tp
->t_oproc
= vcons_tty_start
;
95 tp
->t_param
= vcons_tty_param
;
96 tp
->t_stop
= nottystop
;
99 tp
->t_state
|= TS_CARR_ON
| TS_CONNECTED
;
101 tp
->t_iflag
= TTYDEF_IFLAG
;
102 tp
->t_oflag
= TTYDEF_OFLAG
;
103 tp
->t_cflag
= TTYDEF_CFLAG
;
104 tp
->t_lflag
= TTYDEF_LFLAG
;
105 tp
->t_ispeed
= TTYDEF_SPEED
;
106 tp
->t_ospeed
= TTYDEF_SPEED
;
109 if (minor(dev
) == 0) {
110 error
= (*linesw
[tp
->t_line
].l_open
)(dev
, tp
);
111 ioctl(0, TIOCGWINSZ
, &tp
->t_winsize
);
113 if (kqueue_console_info
== NULL
) {
114 kqueue_console_tty
= tp
;
115 kqueue_console_info
= kqueue_add(0, vcons_hardintr
, tp
);
118 /* dummy up other minors so the installer will run */
121 lwkt_reltoken(&tty_token
);
126 vcons_close(struct dev_close_args
*ap
)
128 cdev_t dev
= ap
->a_head
.a_dev
;
131 lwkt_gettoken(&tty_token
);
133 (*linesw
[tp
->t_line
].l_close
)(tp
, ap
->a_fflag
);
135 lwkt_reltoken(&tty_token
);
140 vcons_ioctl(struct dev_ioctl_args
*ap
)
142 cdev_t dev
= ap
->a_head
.a_dev
;
146 lwkt_gettoken(&tty_token
);
148 error
= (*linesw
[tp
->t_line
].l_ioctl
)(tp
, ap
->a_cmd
, ap
->a_data
,
149 ap
->a_fflag
, ap
->a_cred
);
150 if (error
!= ENOIOCTL
) {
151 lwkt_reltoken(&tty_token
);
154 error
= ttioctl(tp
, ap
->a_cmd
, ap
->a_data
, ap
->a_fflag
);
155 if (error
!= ENOIOCTL
) {
156 lwkt_reltoken(&tty_token
);
159 lwkt_reltoken(&tty_token
);
164 vcons_tty_param(struct tty
*tp
, struct termios
*tio
)
166 lwkt_gettoken(&tty_token
);
167 tp
->t_ispeed
= tio
->c_ispeed
;
168 tp
->t_ospeed
= tio
->c_ospeed
;
169 tp
->t_cflag
= tio
->c_cflag
;
170 lwkt_reltoken(&tty_token
);
175 vcons_tty_start(struct tty
*tp
)
180 lwkt_gettoken(&tty_token
);
181 if (tp
->t_state
& (TS_TIMEOUT
| TS_TTSTOP
)) {
183 lwkt_reltoken(&tty_token
);
186 tp
->t_state
|= TS_BUSY
;
187 while ((n
= q_to_b(&tp
->t_outq
, buf
, sizeof(buf
))) > 0) {
189 * Dummy up ttyv1, etc.
191 if (minor(tp
->t_dev
) == 0) {
192 pwrite(1, buf
, n
, -1);
195 tp
->t_state
&= ~TS_BUSY
;
196 lwkt_reltoken(&tty_token
);
202 vcons_hardintr(void *tpx
, struct intrframe
*frame __unused
)
204 if (console_stolen_by_kernel
== 0)
208 /************************************************************************
209 * KERNEL CONSOLE INTERFACE *
210 ************************************************************************
212 * Kernel direct-call interface console driver
214 static cn_probe_t vconsprobe
;
215 static cn_init_t vconsinit
;
216 static cn_init_fini_t vconsinit_fini
;
217 static cn_term_t vconsterm
;
218 static cn_getc_t vconsgetc
;
219 static cn_checkc_t vconscheckc
;
220 static cn_putc_t vconsputc
;
222 CONS_DRIVER(vcons
, vconsprobe
, vconsinit
, vconsinit_fini
, vconsterm
, vconsgetc
,
223 vconscheckc
, vconsputc
, NULL
, NULL
);
225 static struct termios init_tio
;
226 static struct consdev
*vconsole
;
229 vconsprobe(struct consdev
*cp
)
231 cp
->cn_pri
= CN_NORMAL
;
232 cp
->cn_probegood
= 1;
236 * This is a little bulky handler to set proper terminal
237 * settings in the case of a signal which might lead to
238 * termination or suspension.
243 struct termios curtio
;
244 struct sigaction sa
, osa
;
247 tcgetattr(0, &curtio
);
248 tcsetattr(0, TCSAFLUSH
, &init_tio
);
249 bzero(&sa
, sizeof(sa
));
250 sigemptyset(&sa
.sa_mask
);
251 sa
.sa_handler
= SIG_DFL
;
252 sigaction(sig
, &sa
, &osa
);
255 sigprocmask(SIG_UNBLOCK
, &ss
, &oss
);
256 raise(sig
); /* now hand down the sig */
257 sigprocmask(SIG_SETMASK
, &oss
, NULL
);
258 sigaction(sig
, &osa
, NULL
);
259 tcsetattr(0, TCSAFLUSH
, &curtio
);
263 vconswinchsig(int __unused sig
)
269 vconswinch_intr(void *arg __unused
, void *frame __unused
)
271 struct winsize newsize
;
273 if (vconsole
!= NULL
&& vconsole
->cn_dev
->si_tty
!= NULL
) {
274 ioctl(0, TIOCGWINSZ
, &newsize
);
276 * ttioctl(vconsole->cn_dev->si_tty, TIOCSWINSZ, &newsize, 0);
277 * I wished. Unfortunately this needs a curproc, so do it
280 if (bcmp(&newsize
, &vconsole
->cn_dev
->si_tty
->t_winsize
,
281 sizeof(newsize
)) != 0) {
282 vconsole
->cn_dev
->si_tty
->t_winsize
= newsize
;
283 pgsignal(vconsole
->cn_dev
->si_tty
->t_pgrp
, SIGWINCH
, 1);
289 * This has to be an interrupt thread and not a hard interrupt.
293 vconsvirt_intr(void *arg __unused
, void *frame __unused
)
296 unsigned char buf
[32];
300 if (kqueue_console_info
== NULL
)
302 tp
= kqueue_console_tty
;
304 lwkt_gettoken(&tty_token
);
306 * If we aren't open we only have synchronous traffic via the
307 * debugger and do not need to poll.
309 if ((tp
->t_state
& TS_ISOPEN
) == 0) {
310 lwkt_reltoken(&tty_token
);
315 * Only poll if we are open and haven't been stolen by the debugger.
317 if (console_stolen_by_kernel
== 0 && (tp
->t_state
& TS_ISOPEN
)) {
319 n
= extpread(0, buf
, sizeof(buf
), O_FNONBLOCKING
, -1LL);
320 for (i
= 0; i
< n
; ++i
)
321 (*linesw
[tp
->t_line
].l_rint
)(buf
[i
], tp
);
324 lwkt_reltoken(&tty_token
);
333 * We might catch stray SIGIOs, so try hard.
335 while (tcsetattr(0, TCSAFLUSH
, &init_tio
) != 0 && errno
== EINTR
)
340 vconsinit(struct consdev
*cp
)
346 tcgetattr(0, &init_tio
);
347 bzero(&sa
, sizeof(sa
));
348 sigemptyset(&sa
.sa_mask
);
349 sa
.sa_handler
= vconssignal
;
350 sigaction(SIGTSTP
, &sa
, NULL
);
351 sigaction(SIGINT
, &sa
, NULL
);
352 sigaction(SIGTERM
, &sa
, NULL
);
353 atexit(vconscleanup
);
358 vconsinit_fini(struct consdev
*cp
)
365 * We have to do this here rather then in early boot to be able
366 * to use the interrupt subsystem.
368 register_int_virtual(3, vconswinch_intr
, NULL
, "swinch", NULL
,
370 register_int_virtual(4, vconsvirt_intr
, NULL
, "vintr", NULL
,
372 bzero(&sa
, sizeof(sa
));
373 sigemptyset(&sa
.sa_mask
);
374 sa
.sa_handler
= vconswinchsig
;
375 sigaction(SIGWINCH
, &sa
, NULL
);
378 * Implement ttyv0-ttyv7. At the moment ttyv1-7 are sink nulls.
380 for (i
= 0; i
< 8; ++i
) {
381 dev
= make_dev(&vcons_ops
, i
,
382 UID_ROOT
, GID_WHEEL
, 0600, "ttyv%d", i
);
387 EVENTHANDLER_REGISTER(shutdown_final
, vconscleanup
, NULL
, SHUTDOWN_PRI_LAST
);
391 vconsterm(struct consdev
*vp
)
398 vconsgetc(void *private)
403 console_stolen_by_kernel
= 1;
405 n
= pread(0, &c
, 1, -1);
408 if (n
< 0 && errno
== EINTR
)
410 panic("vconsgetc: EOF on console %jd %d", (intmax_t)n
, errno
);
412 console_stolen_by_kernel
= 0;
417 vconscheckc(void *private)
421 if (extpread(0, &c
, 1, O_FNONBLOCKING
, -1LL) == 1)
427 vconsputc(void *private, int c
)
431 pwrite(1, &cc
, 1, -1);
435 vcons_set_mode(int in_debugger
)
439 if (tcgetattr(0, &tio
) < 0) {
443 tio
.c_oflag
|= OPOST
| ONLCR
;
446 tio
.c_cc
[VINTR
] = init_tio
.c_cc
[VINTR
];
447 tio
.c_cc
[VSUSP
] = init_tio
.c_cc
[VSUSP
];
448 tio
.c_cc
[VSTATUS
] = init_tio
.c_cc
[VSTATUS
];
450 tio
.c_cc
[VINTR
] = _POSIX_VDISABLE
;
451 tio
.c_cc
[VSUSP
] = _POSIX_VDISABLE
;
452 tio
.c_cc
[VSTATUS
] = _POSIX_VDISABLE
;
454 tcsetattr(0, TCSAFLUSH
, &tio
);