| 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); |
| cd29885a MD |
102 | kprintf("vcons_open(): called! (tty: %x, dev: %x)\n", tp, dev); |
| 103 | ||
| 104 | #define ISSET(t, f) ((t) & (f)) | |
| 105 | if (!ISSET(tp->t_state, TS_ISOPEN)) { | |
| 106 | kprintf("vcons_open(): Verified, tty is *NOT* open\n"); | |
| 107 | } else { | |
| 108 | kprintf("vcons_open(): Verified, tty is open\n"); | |
| 109 | } | |
| 1fdc022f MD |
110 | if ((tp->t_state & TS_ISOPEN) == 0) { |
| 111 | tp->t_oproc = vcons_tty_start; | |
| 112 | tp->t_param = vcons_tty_param; | |
| 113 | tp->t_stop = nottystop; | |
| 114 | tp->t_dev = dev; | |
| 115 | ||
| 116 | tp->t_state |= TS_CARR_ON | TS_CONNECTED; | |
| cd29885a | 117 | kprintf("vcons_open(): ttyv (%x) is TS_CONNECTED\n", tp); |
| 1fdc022f MD |
118 | ttychars(tp); |
| 119 | tp->t_iflag = TTYDEF_IFLAG; | |
| 120 | tp->t_oflag = TTYDEF_OFLAG; | |
| 121 | tp->t_cflag = TTYDEF_CFLAG; | |
| 122 | tp->t_lflag = TTYDEF_LFLAG; | |
| 123 | tp->t_ispeed = TTYDEF_SPEED; | |
| 124 | tp->t_ospeed = TTYDEF_SPEED; | |
| 125 | ttsetwater(tp); | |
| aaf8b91f | 126 | } |
| a72d8a9f MD |
127 | if (minor(dev) == 0) { |
| 128 | error = (*linesw[tp->t_line].l_open)(dev, tp); | |
| 129 | ioctl(0, TIOCGWINSZ, &tp->t_winsize); | |
| 130 | ||
| 131 | if (kqueue_console_info == NULL) | |
| 132 | kqueue_console_info = kqueue_add(0, vcons_intr, tp); | |
| 133 | } else { | |
| 134 | /* dummy up other minors so the installer will run */ | |
| 135 | error = 0; | |
| 136 | } | |
| 6f7b98e0 MD |
137 | return(error); |
| 138 | } | |
| 139 | ||
| 140 | static int | |
| 141 | vcons_close(struct dev_close_args *ap) | |
| 142 | { | |
| 143 | cdev_t dev = ap->a_head.a_dev; | |
| 144 | struct tty *tp; | |
| 145 | ||
| 6f7b98e0 | 146 | tp = dev->si_tty; |
| 1fdc022f MD |
147 | (*linesw[tp->t_line].l_close)(tp, ap->a_fflag); |
| 148 | ttyclose(tp); | |
| 6f7b98e0 MD |
149 | return(0); |
| 150 | } | |
| 151 | ||
| 152 | static int | |
| 153 | vcons_ioctl(struct dev_ioctl_args *ap) | |
| 154 | { | |
| 155 | cdev_t dev = ap->a_head.a_dev; | |
| 156 | struct tty *tp; | |
| 157 | int error; | |
| 158 | ||
| 6f7b98e0 MD |
159 | tp = dev->si_tty; |
| 160 | error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data, | |
| 161 | ap->a_fflag, ap->a_cred); | |
| 162 | if (error != ENOIOCTL) | |
| 163 | return (error); | |
| 164 | error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag); | |
| 165 | if (error != ENOIOCTL) | |
| 166 | return (error); | |
| 167 | return (ENOTTY); | |
| 168 | } | |
| 169 | ||
| 170 | static int | |
| 171 | vcons_tty_param(struct tty *tp, struct termios *tio) | |
| 172 | { | |
| 173 | tp->t_ispeed = tio->c_ispeed; | |
| 174 | tp->t_ospeed = tio->c_ospeed; | |
| 175 | tp->t_cflag = tio->c_cflag; | |
| 176 | return(0); | |
| 177 | } | |
| 178 | ||
| 179 | static void | |
| 180 | vcons_tty_start(struct tty *tp) | |
| 181 | { | |
| 182 | int n; | |
| 183 | char buf[64]; | |
| 184 | ||
| 185 | if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { | |
| 186 | ttwwakeup(tp); | |
| 187 | return; | |
| 188 | } | |
| 189 | tp->t_state |= TS_BUSY; | |
| a72d8a9f MD |
190 | while ((n = q_to_b(&tp->t_outq, buf, sizeof(buf))) > 0) { |
| 191 | /* | |
| 192 | * Dummy up ttyv1, etc. | |
| 193 | */ | |
| 03bcb4ab | 194 | if (minor(tp->t_dev) == 0) { |
| 94131955 | 195 | pwrite(1, buf, n, -1); |
| 03bcb4ab | 196 | } |
| a72d8a9f | 197 | } |
| 6f7b98e0 MD |
198 | tp->t_state &= ~TS_BUSY; |
| 199 | ttwwakeup(tp); | |
| 200 | } | |
| 201 | ||
| aaf8b91f MD |
202 | static |
| 203 | void | |
| 9aeb4297 | 204 | vcons_intr(void *tpx, struct intrframe *frame __unused) |
| aaf8b91f MD |
205 | { |
| 206 | struct tty *tp = tpx; | |
| 207 | unsigned char buf[32]; | |
| 208 | int i; | |
| 209 | int n; | |
| 210 | ||
| 1fdc022f MD |
211 | /* |
| 212 | * If we aren't open we only have synchronous traffic via the | |
| 213 | * debugger and do not need to poll. | |
| 214 | */ | |
| 215 | if ((tp->t_state & TS_ISOPEN) == 0) | |
| 216 | return; | |
| 217 | ||
| 218 | /* | |
| 219 | * Only poll if we are open and haven't been stolen by the debugger. | |
| 220 | */ | |
| aaf8b91f MD |
221 | if (console_stolen_by_kernel == 0 && (tp->t_state & TS_ISOPEN)) { |
| 222 | do { | |
| b09fd398 | 223 | n = extpread(0, buf, sizeof(buf), O_FNONBLOCKING, -1LL); |
| aaf8b91f MD |
224 | for (i = 0; i < n; ++i) |
| 225 | (*linesw[tp->t_line].l_rint)(buf[i], tp); | |
| 226 | } while (n > 0); | |
| 227 | } | |
| aaf8b91f MD |
228 | } |
| 229 | ||
| 6f7b98e0 MD |
230 | /************************************************************************ |
| 231 | * KERNEL CONSOLE INTERFACE * | |
| 232 | ************************************************************************ | |
| 233 | * | |
| 234 | * Kernel direct-call interface console driver | |
| 235 | */ | |
| 236 | static cn_probe_t vconsprobe; | |
| 237 | static cn_init_t vconsinit; | |
| ce81f184 | 238 | static cn_init_fini_t vconsinit_fini; |
| 6f7b98e0 MD |
239 | static cn_term_t vconsterm; |
| 240 | static cn_getc_t vconsgetc; | |
| 241 | static cn_checkc_t vconscheckc; | |
| 242 | static cn_putc_t vconsputc; | |
| 243 | ||
| ce81f184 | 244 | CONS_DRIVER(vcons, vconsprobe, vconsinit, vconsinit_fini, vconsterm, vconsgetc, |
| 6f7b98e0 MD |
245 | vconscheckc, vconsputc, NULL); |
| 246 | ||
| 3c5a0ece | 247 | static struct termios init_tio; |
| 30809ad1 | 248 | static struct consdev *vconsole; |
| 3c5a0ece | 249 | |
| 6f7b98e0 MD |
250 | static void |
| 251 | vconsprobe(struct consdev *cp) | |
| 252 | { | |
| e7f2d7de | 253 | cp->cn_pri = CN_NORMAL; |
| ce81f184 | 254 | cp->cn_probegood = 1; |
| 3c5a0ece | 255 | } |
| e7f2d7de | 256 | |
| 3c5a0ece SS |
257 | /* |
| 258 | * This is a little bulky handler to set proper terminal | |
| 259 | * settings in the case of a signal which might lead to | |
| 260 | * termination or suspension. | |
| 261 | */ | |
| 262 | static void | |
| 263 | vconssignal(int sig) | |
| 264 | { | |
| 265 | struct termios curtio; | |
| 266 | struct sigaction sa, osa; | |
| 267 | sigset_t ss, oss; | |
| 268 | ||
| 269 | tcgetattr(0, &curtio); | |
| 270 | tcsetattr(0, TCSAFLUSH, &init_tio); | |
| 271 | bzero(&sa, sizeof(sa)); | |
| 272 | sigemptyset(&sa.sa_mask); | |
| 273 | sa.sa_handler = SIG_DFL; | |
| 274 | sigaction(sig, &sa, &osa); | |
| 275 | sigemptyset(&ss); | |
| 276 | sigaddset(&ss, sig); | |
| 277 | sigprocmask(SIG_UNBLOCK, &ss, &oss); | |
| 278 | raise(sig); /* now hand down the sig */ | |
| 279 | sigprocmask(SIG_SETMASK, &oss, NULL); | |
| 280 | sigaction(sig, &osa, NULL); | |
| 281 | tcsetattr(0, TCSAFLUSH, &curtio); | |
| 3c5a0ece SS |
282 | } |
| 283 | ||
| 30809ad1 | 284 | static void |
| 0df380ef MD |
285 | vconswinchsig(int __unused sig) |
| 286 | { | |
| 287 | signalintr(3); | |
| 288 | } | |
| 289 | ||
| 290 | static void | |
| 291 | vconswinch_intr(void *arg __unused, void *frame __unused) | |
| 30809ad1 SS |
292 | { |
| 293 | struct winsize newsize; | |
| 294 | ||
| 295 | if (vconsole != NULL && vconsole->cn_dev->si_tty != NULL) { | |
| 296 | ioctl(0, TIOCGWINSZ, &newsize); | |
| 297 | /* | |
| 298 | * ttioctl(vconsole->cn_dev->si_tty, TIOCSWINSZ, &newsize, 0); | |
| 299 | * I wished. Unfortunately this needs a curproc, so do it | |
| 300 | * manually. | |
| 301 | */ | |
| 302 | if (bcmp(&newsize, &vconsole->cn_dev->si_tty->t_winsize, | |
| 303 | sizeof(newsize)) != 0) { | |
| 304 | vconsole->cn_dev->si_tty->t_winsize = newsize; | |
| 305 | pgsignal(vconsole->cn_dev->si_tty->t_pgrp, SIGWINCH, 1); | |
| 306 | } | |
| 307 | } | |
| 308 | } | |
| 309 | ||
| 310 | static void | |
| 311 | vconscleanup(void) | |
| 3c5a0ece | 312 | { |
| db74ac99 SS |
313 | /* |
| 314 | * We might catch stray SIGIOs, so try hard. | |
| 315 | */ | |
| 316 | while (tcsetattr(0, TCSAFLUSH, &init_tio) != 0 && errno == EINTR) | |
| 317 | /* NOTHING */; | |
| 6f7b98e0 MD |
318 | } |
| 319 | ||
| 320 | static void | |
| 321 | vconsinit(struct consdev *cp) | |
| 322 | { | |
| 3c5a0ece SS |
323 | struct sigaction sa; |
| 324 | ||
| 30809ad1 SS |
325 | vconsole = cp; |
| 326 | ||
| 3c5a0ece SS |
327 | tcgetattr(0, &init_tio); |
| 328 | bzero(&sa, sizeof(sa)); | |
| 329 | sigemptyset(&sa.sa_mask); | |
| 330 | sa.sa_handler = vconssignal; | |
| 331 | sigaction(SIGTSTP, &sa, NULL); | |
| 332 | sigaction(SIGINT, &sa, NULL); | |
| 333 | sigaction(SIGTERM, &sa, NULL); | |
| 334 | atexit(vconscleanup); | |
| 3c5a0ece | 335 | vcons_set_mode(0); |
| 6f7b98e0 MD |
336 | } |
| 337 | ||
| 338 | static void | |
| ce81f184 MD |
339 | vconsinit_fini(struct consdev *cp) |
| 340 | { | |
| 0df380ef | 341 | struct sigaction sa; |
| a72d8a9f MD |
342 | cdev_t dev; |
| 343 | int i; | |
| 344 | ||
| 345 | /* | |
| 0df380ef MD |
346 | * We have to do this here rather then in early boot to be able |
| 347 | * to use the interrupt subsystem. | |
| 348 | */ | |
| 349 | register_int(3, vconswinch_intr, NULL, "swinch", NULL, 0); | |
| 350 | bzero(&sa, sizeof(sa)); | |
| 351 | sigemptyset(&sa.sa_mask); | |
| 352 | sa.sa_handler = vconswinchsig; | |
| 353 | sigaction(SIGWINCH, &sa, NULL); | |
| 354 | ||
| 355 | /* | |
| a72d8a9f MD |
356 | * Implement ttyv0-ttyv7. At the moment ttyv1-7 are sink nulls. |
| 357 | */ | |
| a72d8a9f | 358 | for (i = 0; i < 8; ++i) { |
| cd29885a | 359 | kprintf("vconsinit_fini(): make_dev for ttyv%d\n", i); |
| a72d8a9f MD |
360 | dev = make_dev(&vcons_ops, i, |
| 361 | UID_ROOT, GID_WHEEL, 0600, "ttyv%d", i); | |
| cd29885a MD |
362 | kprintf("dev is: %x\n", dev); |
| 363 | if (i == 0) { | |
| a72d8a9f | 364 | cp->cn_dev = dev; |
| cd29885a MD |
365 | kprintf("dev0 is: %x\n", dev); |
| 366 | } | |
| a72d8a9f | 367 | } |
| db74ac99 | 368 | EVENTHANDLER_REGISTER(shutdown_final, vconscleanup, NULL, SHUTDOWN_PRI_LAST); |
| ce81f184 MD |
369 | } |
| 370 | ||
| 371 | static void | |
| 6f7b98e0 MD |
372 | vconsterm(struct consdev *vp) |
| 373 | { | |
| 30809ad1 | 374 | vconsole = NULL; |
| 3c5a0ece | 375 | vconscleanup(); |
| 6f7b98e0 MD |
376 | } |
| 377 | ||
| 378 | static int | |
| ce81f184 | 379 | vconsgetc(void *private) |
| 6f7b98e0 MD |
380 | { |
| 381 | unsigned char c; | |
| 6092278a MD |
382 | ssize_t n; |
| 383 | ||
| aaf8b91f | 384 | console_stolen_by_kernel = 1; |
| 6092278a | 385 | for (;;) { |
| 94131955 | 386 | n = pread(0, &c, 1, -1); |
| 03bcb4ab | 387 | if (n == 1) |
| aaf8b91f | 388 | break; |
| 6092278a MD |
389 | if (n < 0 && errno == EINTR) |
| 390 | continue; | |
| 391 | panic("vconsgetc: EOF on console %d %d", n ,errno); | |
| 392 | } | |
| aaf8b91f MD |
393 | console_stolen_by_kernel = 0; |
| 394 | return((int)c); | |
| 6f7b98e0 MD |
395 | } |
| 396 | ||
| 397 | static int | |
| ce81f184 | 398 | vconscheckc(void *private) |
| 6f7b98e0 MD |
399 | { |
| 400 | unsigned char c; | |
| 401 | ||
| b09fd398 | 402 | if (extpread(0, &c, 1, O_FNONBLOCKING, -1LL) == 1) |
| 6f7b98e0 MD |
403 | return((int)c); |
| 404 | return(-1); | |
| 405 | } | |
| 406 | ||
| 407 | static void | |
| ce81f184 | 408 | vconsputc(void *private, int c) |
| 6f7b98e0 MD |
409 | { |
| 410 | char cc = c; | |
| 411 | ||
| 94131955 | 412 | pwrite(1, &cc, 1, -1); |
| 6f7b98e0 MD |
413 | } |
| 414 | ||
| cb118784 MD |
415 | void |
| 416 | vcons_set_mode(int in_debugger) | |
| 417 | { | |
| 418 | struct termios tio; | |
| 419 | ||
| 420 | if (tcgetattr(0, &tio) < 0) | |
| 421 | return; | |
| 422 | cfmakeraw(&tio); | |
| 1b40cca7 | 423 | tio.c_oflag |= OPOST | ONLCR; |
| cb118784 MD |
424 | tio.c_lflag |= ISIG; |
| 425 | if (in_debugger) { | |
| 3c5a0ece SS |
426 | tio.c_cc[VINTR] = init_tio.c_cc[VINTR]; |
| 427 | tio.c_cc[VSUSP] = init_tio.c_cc[VSUSP]; | |
| 428 | tio.c_cc[VSTATUS] = init_tio.c_cc[VSTATUS]; | |
| cb118784 | 429 | } else { |
| 5f99c60c YT |
430 | tio.c_cc[VINTR] = _POSIX_VDISABLE; |
| 431 | tio.c_cc[VSUSP] = _POSIX_VDISABLE; | |
| 432 | tio.c_cc[VSTATUS] = _POSIX_VDISABLE; | |
| cb118784 MD |
433 | } |
| 434 | tcsetattr(0, TCSAFLUSH, &tio); | |
| 435 | } |