| Commit | Line | Data |
|---|---|---|
| 6f7b98e0 MD |
1 | /* |
| 2 | * Copyright (c) 2006 The DragonFly Project. All rights reserved. | |
| 3 | * | |
| 4 | * This code is derived from software contributed to The DragonFly Project | |
| 5 | * by Matthew Dillon <dillon@backplane.com> | |
| 6 | * | |
| 7 | * Redistribution and use in source and binary forms, with or without | |
| 8 | * modification, are permitted provided that the following conditions | |
| 9 | * are met: | |
| 10 | * | |
| 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 | |
| 15 | * the documentation and/or other materials provided with the | |
| 16 | * distribution. | |
| 17 | * 3. Neither the name of The DragonFly Project nor the names of its | |
| 18 | * contributors may be used to endorse or promote products derived | |
| 19 | * from this software without specific, prior written permission. | |
| 20 | * | |
| 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
| 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
| 25 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 26 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
| 27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
| 29 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| 30 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
| 31 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 32 | * SUCH DAMAGE. | |
| 33 | * | |
| 0df380ef | 34 | * $DragonFly: src/sys/platform/vkernel/platform/console.c,v 1.17 2007/07/02 04:19:14 dillon Exp $ |
| 6f7b98e0 MD |
35 | */ |
| 36 | ||
| 37 | #include <sys/systm.h> | |
| 38 | #include <sys/kernel.h> | |
| 39 | #include <sys/conf.h> | |
| 40 | #include <sys/cons.h> | |
| 41 | #include <sys/tty.h> | |
| 42 | #include <sys/termios.h> | |
| 43 | #include <sys/fcntl.h> | |
| 30809ad1 | 44 | #include <sys/signalvar.h> |
| db74ac99 | 45 | #include <sys/eventhandler.h> |
| 0df380ef | 46 | #include <sys/interrupt.h> |
| cb118784 | 47 | #include <machine/md_var.h> |
| 6f7b98e0 | 48 | #include <unistd.h> |
| e7f2d7de | 49 | #include <termios.h> |
| 3c5a0ece | 50 | #include <stdlib.h> |
| 6f7b98e0 | 51 | |
| aaf8b91f | 52 | static int console_stolen_by_kernel; |
| 9aeb4297 | 53 | static struct kqueue_info *kqueue_console_info; |
| aaf8b91f | 54 | |
| 6f7b98e0 MD |
55 | /* |
| 56 | * Global console locking functions | |
| 57 | */ | |
| 58 | void | |
| 59 | cons_lock(void) | |
| 60 | { | |
| 61 | } | |
| 62 | ||
| 63 | void | |
| 64 | cons_unlock(void) | |
| 65 | { | |
| 66 | } | |
| 67 | ||
| 68 | /************************************************************************ | |
| 69 | * CONSOLE DEVICE * | |
| 70 | ************************************************************************ | |
| 71 | * | |
| 72 | */ | |
| 73 | ||
| a72d8a9f | 74 | #define CDEV_MAJOR 12 /* steal the same major as /dev/ttyv* */ |
| 6f7b98e0 MD |
75 | |
| 76 | static int vcons_tty_param(struct tty *tp, struct termios *tio); | |
| 77 | static void vcons_tty_start(struct tty *tp); | |
| 9aeb4297 | 78 | static void vcons_intr(void *tpx, struct intrframe *frame __unused); |
| 6f7b98e0 MD |
79 | |
| 80 | static d_open_t vcons_open; | |
| 81 | static d_close_t vcons_close; | |
| 82 | static d_ioctl_t vcons_ioctl; | |
| 83 | ||
| 84 | static struct dev_ops vcons_ops = { | |
| 85 | { "vcons", CDEV_MAJOR, D_TTY }, | |
| 86 | .d_open = vcons_open, | |
| 87 | .d_close = vcons_close, | |
| 88 | .d_read = ttyread, | |
| 89 | .d_write = ttywrite, | |
| 90 | .d_ioctl = vcons_ioctl, | |
| 91 | .d_poll = ttypoll, | |
| 92 | }; | |
| 93 | ||
| 94 | static int | |
| 95 | vcons_open(struct dev_open_args *ap) | |
| 96 | { | |
| 97 | cdev_t dev = ap->a_head.a_dev; | |
| 98 | struct tty *tp; | |
| 99 | int error; | |
| 100 | ||
| 6f7b98e0 | 101 | tp = dev->si_tty = ttymalloc(dev->si_tty); |
| 1fdc022f MD |
102 | if ((tp->t_state & TS_ISOPEN) == 0) { |
| 103 | tp->t_oproc = vcons_tty_start; | |
| 104 | tp->t_param = vcons_tty_param; | |
| 105 | tp->t_stop = nottystop; | |
| 106 | tp->t_dev = dev; | |
| 107 | ||
| 108 | tp->t_state |= TS_CARR_ON | TS_CONNECTED; | |
| 109 | ttychars(tp); | |
| 110 | tp->t_iflag = TTYDEF_IFLAG; | |
| 111 | tp->t_oflag = TTYDEF_OFLAG; | |
| 112 | tp->t_cflag = TTYDEF_CFLAG; | |
| 113 | tp->t_lflag = TTYDEF_LFLAG; | |
| 114 | tp->t_ispeed = TTYDEF_SPEED; | |
| 115 | tp->t_ospeed = TTYDEF_SPEED; | |
| 116 | ttsetwater(tp); | |
| aaf8b91f | 117 | } |
| a72d8a9f MD |
118 | if (minor(dev) == 0) { |
| 119 | error = (*linesw[tp->t_line].l_open)(dev, tp); | |
| 120 | ioctl(0, TIOCGWINSZ, &tp->t_winsize); | |
| 121 | ||
| 122 | if (kqueue_console_info == NULL) | |
| 123 | kqueue_console_info = kqueue_add(0, vcons_intr, tp); | |
| 124 | } else { | |
| 125 | /* dummy up other minors so the installer will run */ | |
| 126 | error = 0; | |
| 127 | } | |
| 6f7b98e0 MD |
128 | return(error); |
| 129 | } | |
| 130 | ||
| 131 | static int | |
| 132 | vcons_close(struct dev_close_args *ap) | |
| 133 | { | |
| 134 | cdev_t dev = ap->a_head.a_dev; | |
| 135 | struct tty *tp; | |
| 136 | ||
| 6f7b98e0 | 137 | tp = dev->si_tty; |
| 1fdc022f MD |
138 | (*linesw[tp->t_line].l_close)(tp, ap->a_fflag); |
| 139 | ttyclose(tp); | |
| 6f7b98e0 MD |
140 | return(0); |
| 141 | } | |
| 142 | ||
| 143 | static int | |
| 144 | vcons_ioctl(struct dev_ioctl_args *ap) | |
| 145 | { | |
| 146 | cdev_t dev = ap->a_head.a_dev; | |
| 147 | struct tty *tp; | |
| 148 | int error; | |
| 149 | ||
| 6f7b98e0 MD |
150 | tp = dev->si_tty; |
| 151 | error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data, | |
| 152 | ap->a_fflag, ap->a_cred); | |
| 153 | if (error != ENOIOCTL) | |
| 154 | return (error); | |
| 155 | error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag); | |
| 156 | if (error != ENOIOCTL) | |
| 157 | return (error); | |
| 158 | return (ENOTTY); | |
| 159 | } | |
| 160 | ||
| 161 | static int | |
| 162 | vcons_tty_param(struct tty *tp, struct termios *tio) | |
| 163 | { | |
| 164 | tp->t_ispeed = tio->c_ispeed; | |
| 165 | tp->t_ospeed = tio->c_ospeed; | |
| 166 | tp->t_cflag = tio->c_cflag; | |
| 167 | return(0); | |
| 168 | } | |
| 169 | ||
| 170 | static void | |
| 171 | vcons_tty_start(struct tty *tp) | |
| 172 | { | |
| 173 | int n; | |
| 174 | char buf[64]; | |
| 175 | ||
| 176 | if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { | |
| 177 | ttwwakeup(tp); | |
| 178 | return; | |
| 179 | } | |
| 180 | tp->t_state |= TS_BUSY; | |
| a72d8a9f MD |
181 | while ((n = q_to_b(&tp->t_outq, buf, sizeof(buf))) > 0) { |
| 182 | /* | |
| 183 | * Dummy up ttyv1, etc. | |
| 184 | */ | |
| 03bcb4ab | 185 | if (minor(tp->t_dev) == 0) { |
| 94131955 | 186 | pwrite(1, buf, n, -1); |
| 03bcb4ab | 187 | } |
| a72d8a9f | 188 | } |
| 6f7b98e0 MD |
189 | tp->t_state &= ~TS_BUSY; |
| 190 | ttwwakeup(tp); | |
| 191 | } | |
| 192 | ||
| aaf8b91f MD |
193 | static |
| 194 | void | |
| 9aeb4297 | 195 | vcons_intr(void *tpx, struct intrframe *frame __unused) |
| aaf8b91f MD |
196 | { |
| 197 | struct tty *tp = tpx; | |
| 198 | unsigned char buf[32]; | |
| 199 | int i; | |
| 200 | int n; | |
| 201 | ||
| 1fdc022f MD |
202 | /* |
| 203 | * If we aren't open we only have synchronous traffic via the | |
| 204 | * debugger and do not need to poll. | |
| 205 | */ | |
| 206 | if ((tp->t_state & TS_ISOPEN) == 0) | |
| 207 | return; | |
| 208 | ||
| 209 | /* | |
| 210 | * Only poll if we are open and haven't been stolen by the debugger. | |
| 211 | */ | |
| aaf8b91f MD |
212 | if (console_stolen_by_kernel == 0 && (tp->t_state & TS_ISOPEN)) { |
| 213 | do { | |
| b09fd398 | 214 | n = extpread(0, buf, sizeof(buf), O_FNONBLOCKING, -1LL); |
| aaf8b91f MD |
215 | for (i = 0; i < n; ++i) |
| 216 | (*linesw[tp->t_line].l_rint)(buf[i], tp); | |
| 217 | } while (n > 0); | |
| 218 | } | |
| aaf8b91f MD |
219 | } |
| 220 | ||
| 6f7b98e0 MD |
221 | /************************************************************************ |
| 222 | * KERNEL CONSOLE INTERFACE * | |
| 223 | ************************************************************************ | |
| 224 | * | |
| 225 | * Kernel direct-call interface console driver | |
| 226 | */ | |
| 227 | static cn_probe_t vconsprobe; | |
| 228 | static cn_init_t vconsinit; | |
| ce81f184 | 229 | static cn_init_fini_t vconsinit_fini; |
| 6f7b98e0 MD |
230 | static cn_term_t vconsterm; |
| 231 | static cn_getc_t vconsgetc; | |
| 232 | static cn_checkc_t vconscheckc; | |
| 233 | static cn_putc_t vconsputc; | |
| 234 | ||
| ce81f184 | 235 | CONS_DRIVER(vcons, vconsprobe, vconsinit, vconsinit_fini, vconsterm, vconsgetc, |
| 6f7b98e0 MD |
236 | vconscheckc, vconsputc, NULL); |
| 237 | ||
| 3c5a0ece | 238 | static struct termios init_tio; |
| 30809ad1 | 239 | static struct consdev *vconsole; |
| 3c5a0ece | 240 | |
| 6f7b98e0 MD |
241 | static void |
| 242 | vconsprobe(struct consdev *cp) | |
| 243 | { | |
| e7f2d7de | 244 | cp->cn_pri = CN_NORMAL; |
| ce81f184 | 245 | cp->cn_probegood = 1; |
| 3c5a0ece | 246 | } |
| e7f2d7de | 247 | |
| 3c5a0ece SS |
248 | /* |
| 249 | * This is a little bulky handler to set proper terminal | |
| 250 | * settings in the case of a signal which might lead to | |
| 251 | * termination or suspension. | |
| 252 | */ | |
| 253 | static void | |
| 254 | vconssignal(int sig) | |
| 255 | { | |
| 256 | struct termios curtio; | |
| 257 | struct sigaction sa, osa; | |
| 258 | sigset_t ss, oss; | |
| 259 | ||
| 260 | tcgetattr(0, &curtio); | |
| 261 | tcsetattr(0, TCSAFLUSH, &init_tio); | |
| 262 | bzero(&sa, sizeof(sa)); | |
| 263 | sigemptyset(&sa.sa_mask); | |
| 264 | sa.sa_handler = SIG_DFL; | |
| 265 | sigaction(sig, &sa, &osa); | |
| 266 | sigemptyset(&ss); | |
| 267 | sigaddset(&ss, sig); | |
| 268 | sigprocmask(SIG_UNBLOCK, &ss, &oss); | |
| 269 | raise(sig); /* now hand down the sig */ | |
| 270 | sigprocmask(SIG_SETMASK, &oss, NULL); | |
| 271 | sigaction(sig, &osa, NULL); | |
| 272 | tcsetattr(0, TCSAFLUSH, &curtio); | |
| 3c5a0ece SS |
273 | } |
| 274 | ||
| 30809ad1 | 275 | static void |
| 0df380ef MD |
276 | vconswinchsig(int __unused sig) |
| 277 | { | |
| 278 | signalintr(3); | |
| 279 | } | |
| 280 | ||
| 281 | static void | |
| 282 | vconswinch_intr(void *arg __unused, void *frame __unused) | |
| 30809ad1 SS |
283 | { |
| 284 | struct winsize newsize; | |
| 285 | ||
| 286 | if (vconsole != NULL && vconsole->cn_dev->si_tty != NULL) { | |
| 287 | ioctl(0, TIOCGWINSZ, &newsize); | |
| 288 | /* | |
| 289 | * ttioctl(vconsole->cn_dev->si_tty, TIOCSWINSZ, &newsize, 0); | |
| 290 | * I wished. Unfortunately this needs a curproc, so do it | |
| 291 | * manually. | |
| 292 | */ | |
| 293 | if (bcmp(&newsize, &vconsole->cn_dev->si_tty->t_winsize, | |
| 294 | sizeof(newsize)) != 0) { | |
| 295 | vconsole->cn_dev->si_tty->t_winsize = newsize; | |
| 296 | pgsignal(vconsole->cn_dev->si_tty->t_pgrp, SIGWINCH, 1); | |
| 297 | } | |
| 298 | } | |
| 299 | } | |
| 300 | ||
| 301 | static void | |
| 302 | vconscleanup(void) | |
| 3c5a0ece | 303 | { |
| db74ac99 SS |
304 | /* |
| 305 | * We might catch stray SIGIOs, so try hard. | |
| 306 | */ | |
| 307 | while (tcsetattr(0, TCSAFLUSH, &init_tio) != 0 && errno == EINTR) | |
| 308 | /* NOTHING */; | |
| 6f7b98e0 MD |
309 | } |
| 310 | ||
| 311 | static void | |
| 312 | vconsinit(struct consdev *cp) | |
| 313 | { | |
| 3c5a0ece SS |
314 | struct sigaction sa; |
| 315 | ||
| 30809ad1 SS |
316 | vconsole = cp; |
| 317 | ||
| 3c5a0ece SS |
318 | tcgetattr(0, &init_tio); |
| 319 | bzero(&sa, sizeof(sa)); | |
| 320 | sigemptyset(&sa.sa_mask); | |
| 321 | sa.sa_handler = vconssignal; | |
| 322 | sigaction(SIGTSTP, &sa, NULL); | |
| 323 | sigaction(SIGINT, &sa, NULL); | |
| 324 | sigaction(SIGTERM, &sa, NULL); | |
| 325 | atexit(vconscleanup); | |
| 3c5a0ece | 326 | vcons_set_mode(0); |
| 6f7b98e0 MD |
327 | } |
| 328 | ||
| 329 | static void | |
| ce81f184 MD |
330 | vconsinit_fini(struct consdev *cp) |
| 331 | { | |
| 0df380ef | 332 | struct sigaction sa; |
| a72d8a9f MD |
333 | cdev_t dev; |
| 334 | int i; | |
| 335 | ||
| 336 | /* | |
| 0df380ef MD |
337 | * We have to do this here rather then in early boot to be able |
| 338 | * to use the interrupt subsystem. | |
| 339 | */ | |
| 340 | register_int(3, vconswinch_intr, NULL, "swinch", NULL, 0); | |
| 341 | bzero(&sa, sizeof(sa)); | |
| 342 | sigemptyset(&sa.sa_mask); | |
| 343 | sa.sa_handler = vconswinchsig; | |
| 344 | sigaction(SIGWINCH, &sa, NULL); | |
| 345 | ||
| 346 | /* | |
| a72d8a9f MD |
347 | * Implement ttyv0-ttyv7. At the moment ttyv1-7 are sink nulls. |
| 348 | */ | |
| 349 | dev_ops_add(&vcons_ops, -1 & ~7, 0); | |
| 350 | for (i = 0; i < 8; ++i) { | |
| 351 | dev = make_dev(&vcons_ops, i, | |
| 352 | UID_ROOT, GID_WHEEL, 0600, "ttyv%d", i); | |
| 353 | if (i == 0) | |
| 354 | cp->cn_dev = dev; | |
| 355 | } | |
| db74ac99 | 356 | EVENTHANDLER_REGISTER(shutdown_final, vconscleanup, NULL, SHUTDOWN_PRI_LAST); |
| ce81f184 MD |
357 | } |
| 358 | ||
| 359 | static void | |
| 6f7b98e0 MD |
360 | vconsterm(struct consdev *vp) |
| 361 | { | |
| 30809ad1 | 362 | vconsole = NULL; |
| 3c5a0ece | 363 | vconscleanup(); |
| 6f7b98e0 MD |
364 | } |
| 365 | ||
| 366 | static int | |
| ce81f184 | 367 | vconsgetc(void *private) |
| 6f7b98e0 MD |
368 | { |
| 369 | unsigned char c; | |
| 6092278a MD |
370 | ssize_t n; |
| 371 | ||
| aaf8b91f | 372 | console_stolen_by_kernel = 1; |
| 6092278a | 373 | for (;;) { |
| 94131955 | 374 | n = pread(0, &c, 1, -1); |
| 03bcb4ab | 375 | if (n == 1) |
| aaf8b91f | 376 | break; |
| 6092278a MD |
377 | if (n < 0 && errno == EINTR) |
| 378 | continue; | |
| 379 | panic("vconsgetc: EOF on console %d %d", n ,errno); | |
| 380 | } | |
| aaf8b91f MD |
381 | console_stolen_by_kernel = 0; |
| 382 | return((int)c); | |
| 6f7b98e0 MD |
383 | } |
| 384 | ||
| 385 | static int | |
| ce81f184 | 386 | vconscheckc(void *private) |
| 6f7b98e0 MD |
387 | { |
| 388 | unsigned char c; | |
| 389 | ||
| b09fd398 | 390 | if (extpread(0, &c, 1, O_FNONBLOCKING, -1LL) == 1) |
| 6f7b98e0 MD |
391 | return((int)c); |
| 392 | return(-1); | |
| 393 | } | |
| 394 | ||
| 395 | static void | |
| ce81f184 | 396 | vconsputc(void *private, int c) |
| 6f7b98e0 MD |
397 | { |
| 398 | char cc = c; | |
| 399 | ||
| 94131955 | 400 | pwrite(1, &cc, 1, -1); |
| 6f7b98e0 MD |
401 | } |
| 402 | ||
| cb118784 MD |
403 | void |
| 404 | vcons_set_mode(int in_debugger) | |
| 405 | { | |
| 406 | struct termios tio; | |
| 407 | ||
| 408 | if (tcgetattr(0, &tio) < 0) | |
| 409 | return; | |
| 410 | cfmakeraw(&tio); | |
| 1b40cca7 | 411 | tio.c_oflag |= OPOST | ONLCR; |
| cb118784 MD |
412 | tio.c_lflag |= ISIG; |
| 413 | if (in_debugger) { | |
| 3c5a0ece SS |
414 | tio.c_cc[VINTR] = init_tio.c_cc[VINTR]; |
| 415 | tio.c_cc[VSUSP] = init_tio.c_cc[VSUSP]; | |
| 416 | tio.c_cc[VSTATUS] = init_tio.c_cc[VSTATUS]; | |
| cb118784 | 417 | } else { |
| 5f99c60c YT |
418 | tio.c_cc[VINTR] = _POSIX_VDISABLE; |
| 419 | tio.c_cc[VSUSP] = _POSIX_VDISABLE; | |
| 420 | tio.c_cc[VSTATUS] = _POSIX_VDISABLE; | |
| cb118784 MD |
421 | } |
| 422 | tcsetattr(0, TCSAFLUSH, &tio); | |
| 423 | } |