| 1 | /* |
| 2 | * Copyright (c) 1992 The Regents of the University of California. |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * This software was developed by the Computer Systems Engineering group |
| 6 | * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and |
| 7 | * contributed to Berkeley. |
| 8 | * |
| 9 | * All advertising materials mentioning features or use of this software |
| 10 | * must display the following acknowledgement: |
| 11 | * This product includes software developed by the University of |
| 12 | * California, Lawrence Berkeley Laboratories. |
| 13 | * |
| 14 | * %sccs.include.redist.c% |
| 15 | * |
| 16 | * @(#)cons.c 7.2 (Berkeley) %G% |
| 17 | * |
| 18 | * from: $Header: cons.c,v 1.10 92/07/10 00:02:42 torek Exp $ |
| 19 | */ |
| 20 | |
| 21 | /* |
| 22 | * Console (indirect) driver. |
| 23 | */ |
| 24 | |
| 25 | #include "sys/param.h" |
| 26 | #include "sys/proc.h" |
| 27 | #include "sys/systm.h" |
| 28 | #include "sys/ioctl.h" |
| 29 | #include "sys/tty.h" |
| 30 | #include "sys/file.h" |
| 31 | #include "sys/conf.h" |
| 32 | |
| 33 | #include "machine/bsd_openprom.h" |
| 34 | #include "machine/psl.h" |
| 35 | |
| 36 | #include "zs.h" |
| 37 | |
| 38 | struct tty *constty = 0; /* virtual console output device */ |
| 39 | struct tty *fbconstty = 0; /* tty structure for frame buffer console */ |
| 40 | int rom_console_input; /* when set, hardclock calls cnrom() */ |
| 41 | |
| 42 | int cons_ocount; /* output byte count */ |
| 43 | |
| 44 | extern struct promvec *promvec; |
| 45 | |
| 46 | /* |
| 47 | * The output driver may munge the minor number in cons.t_dev. |
| 48 | */ |
| 49 | struct tty cons; /* rom console tty device */ |
| 50 | static void cnstart __P((struct tty *)); |
| 51 | static void cnfbstart __P((struct tty *)); |
| 52 | static void cnfbstop __P((struct tty *, int)); |
| 53 | static void cnfbdma __P((void *)); |
| 54 | |
| 55 | extern char partab[]; |
| 56 | |
| 57 | consinit() |
| 58 | { |
| 59 | register struct tty *tp = &cons; |
| 60 | register int in, out; |
| 61 | void zsconsole(), bwtwoconsole(); |
| 62 | |
| 63 | tp->t_dev = makedev(0, 0); /* /dev/console */ |
| 64 | tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; |
| 65 | tp->t_param = (int (*)(struct tty *, struct termios *))nullop; |
| 66 | in = *promvec->pv_stdin; |
| 67 | out = *promvec->pv_stdout; |
| 68 | switch (in) { |
| 69 | |
| 70 | #if NZS > 0 |
| 71 | case PROMDEV_TTYA: |
| 72 | zsconsole(tp, 0, 0); |
| 73 | break; |
| 74 | |
| 75 | case PROMDEV_TTYB: |
| 76 | zsconsole(tp, 1, 0); |
| 77 | break; |
| 78 | #endif |
| 79 | |
| 80 | case PROMDEV_KBD: |
| 81 | /* |
| 82 | * Tell the keyboard driver to direct ASCII input here. |
| 83 | */ |
| 84 | kbd_ascii(tp); |
| 85 | break; |
| 86 | |
| 87 | default: |
| 88 | rom_console_input = 1; |
| 89 | printf("unknown console input source %d; using rom\n", in); |
| 90 | break; |
| 91 | } |
| 92 | switch (out) { |
| 93 | |
| 94 | #if NZS > 0 |
| 95 | case PROMDEV_TTYA: |
| 96 | zsconsole(tp, 0, 1); |
| 97 | break; |
| 98 | |
| 99 | case PROMDEV_TTYB: |
| 100 | zsconsole(tp, 1, 1); |
| 101 | break; |
| 102 | #endif |
| 103 | |
| 104 | case PROMDEV_SCREEN: |
| 105 | fbconstty = tp; |
| 106 | tp->t_oproc = cnfbstart; |
| 107 | tp->t_stop = cnfbstop; |
| 108 | break; |
| 109 | |
| 110 | default: |
| 111 | printf("unknown console output sink %d; using rom\n", out); |
| 112 | tp->t_oproc = cnstart; |
| 113 | tp->t_stop = (void (*)(struct tty *, int))nullop; |
| 114 | break; |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | /* ARGSUSED */ |
| 119 | cnopen(dev, flag, mode, p) |
| 120 | dev_t dev; |
| 121 | int flag, mode; |
| 122 | struct proc *p; |
| 123 | { |
| 124 | register struct tty *tp = &cons; |
| 125 | |
| 126 | if ((tp->t_state & TS_ISOPEN) == 0) { |
| 127 | /* |
| 128 | * Leave baud rate alone! |
| 129 | */ |
| 130 | ttychars(tp); |
| 131 | tp->t_iflag = TTYDEF_IFLAG; |
| 132 | tp->t_oflag = TTYDEF_OFLAG; |
| 133 | tp->t_lflag = TTYDEF_LFLAG; |
| 134 | tp->t_cflag = TTYDEF_CFLAG; |
| 135 | tp->t_state = TS_ISOPEN | TS_CARR_ON; |
| 136 | ttsetwater(tp); |
| 137 | } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) |
| 138 | return (EBUSY); |
| 139 | return ((*linesw[tp->t_line].l_open)(dev, tp)); |
| 140 | } |
| 141 | |
| 142 | /* ARGSUSED */ |
| 143 | cnclose(dev, flag, mode, p) |
| 144 | dev_t dev; |
| 145 | int flag, mode; |
| 146 | struct proc *p; |
| 147 | { |
| 148 | register struct tty *tp = &cons; |
| 149 | |
| 150 | (*linesw[tp->t_line].l_close)(tp, flag); |
| 151 | ttyclose(tp); |
| 152 | return (0); |
| 153 | } |
| 154 | |
| 155 | /* ARGSUSED */ |
| 156 | cnread(dev, uio, flag) |
| 157 | dev_t dev; |
| 158 | struct uio *uio; |
| 159 | int flag; |
| 160 | { |
| 161 | register struct tty *tp = &cons; |
| 162 | |
| 163 | return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); |
| 164 | } |
| 165 | |
| 166 | /* ARGSUSED */ |
| 167 | cnwrite(dev, uio, flag) |
| 168 | dev_t dev; |
| 169 | struct uio *uio; |
| 170 | int flag; |
| 171 | { |
| 172 | register struct tty *tp; |
| 173 | |
| 174 | if ((tp = constty) == NULL || |
| 175 | (tp->t_state & (TS_CARR_ON|TS_ISOPEN)) != (TS_CARR_ON|TS_ISOPEN)) |
| 176 | tp = &cons; |
| 177 | return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); |
| 178 | } |
| 179 | |
| 180 | cnioctl(dev, cmd, data, flag, p) |
| 181 | dev_t dev; |
| 182 | int cmd; |
| 183 | caddr_t data; |
| 184 | int flag; |
| 185 | struct proc *p; |
| 186 | { |
| 187 | register struct tty *tp; |
| 188 | int error; |
| 189 | |
| 190 | /* |
| 191 | * Superuser can always use this to wrest control of console |
| 192 | * output from the "virtual" console. |
| 193 | */ |
| 194 | if (cmd == TIOCCONS && constty) { |
| 195 | error = suser(p->p_ucred, (u_short *)NULL); |
| 196 | if (error) |
| 197 | return (error); |
| 198 | constty = NULL; |
| 199 | return (0); |
| 200 | } |
| 201 | tp = &cons; |
| 202 | if ((error = linesw[tp->t_line].l_ioctl(tp, cmd, data, flag, p)) >= 0) |
| 203 | return (error); |
| 204 | if ((error = ttioctl(tp, cmd, data, flag)) >= 0) |
| 205 | return (error); |
| 206 | return (ENOTTY); |
| 207 | } |
| 208 | |
| 209 | cnselect(dev, which, p) |
| 210 | dev_t dev; |
| 211 | int which; |
| 212 | struct proc *p; |
| 213 | { |
| 214 | |
| 215 | return (ttselect(makedev(major(dev), 0), which, p)); |
| 216 | } |
| 217 | |
| 218 | /* |
| 219 | * The rest of this code is run only when we are using the ROM vectors. |
| 220 | */ |
| 221 | |
| 222 | /* |
| 223 | * Generic output. We just call putchar. (Very bad for performance.) |
| 224 | */ |
| 225 | static void |
| 226 | cnstart(tp) |
| 227 | register struct tty *tp; |
| 228 | { |
| 229 | register int c, s; |
| 230 | register void (*putc)(int); |
| 231 | |
| 232 | s = spltty(); |
| 233 | if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { |
| 234 | splx(s); |
| 235 | return; |
| 236 | } |
| 237 | putc = promvec->pv_putchar; |
| 238 | while (tp->t_outq.c_cc) { |
| 239 | c = getc(&tp->t_outq); |
| 240 | /* |
| 241 | * *%&!*& ROM monitor console putchar is not reentrant! |
| 242 | * splhigh/tty around it so as not to run so long with |
| 243 | * clock interrupts blocked. |
| 244 | */ |
| 245 | (void) splhigh(); |
| 246 | (*putc)(c & 0177); |
| 247 | (void) spltty(); |
| 248 | } |
| 249 | if (tp->t_state & TS_ASLEEP) { /* can't happen? */ |
| 250 | tp->t_state &= ~TS_ASLEEP; |
| 251 | wakeup((caddr_t)&tp->t_outq); |
| 252 | } |
| 253 | selwakeup(&tp->t_wsel); |
| 254 | splx(s); |
| 255 | } |
| 256 | |
| 257 | /* |
| 258 | * Frame buffer output. |
| 259 | * We use pseudo-DMA, via the ROM `write string' function, called from |
| 260 | * software clock interrupts. |
| 261 | */ |
| 262 | static void |
| 263 | cnfbstart(tp) |
| 264 | register struct tty *tp; |
| 265 | { |
| 266 | register int s; |
| 267 | |
| 268 | s = spltty(); /* paranoid: splsoftclock should suffice */ |
| 269 | if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { |
| 270 | splx(s); |
| 271 | return; |
| 272 | } |
| 273 | /* |
| 274 | * If there are sleepers, and output has drained below low |
| 275 | * water mark, awaken. |
| 276 | */ |
| 277 | if (tp->t_outq.c_cc <= tp->t_lowat) { |
| 278 | if (tp->t_state & TS_ASLEEP) { |
| 279 | tp->t_state &= ~TS_ASLEEP; |
| 280 | wakeup((caddr_t)&tp->t_outq); |
| 281 | } |
| 282 | selwakeup(&tp->t_wsel); |
| 283 | } |
| 284 | if (tp->t_outq.c_cc) { |
| 285 | tp->t_state |= TS_BUSY; |
| 286 | if (s == 0) { |
| 287 | (void) splsoftclock(); |
| 288 | cnfbdma((void *)tp); |
| 289 | } else |
| 290 | timeout(cnfbdma, tp, 1); |
| 291 | } |
| 292 | splx(s); |
| 293 | } |
| 294 | |
| 295 | /* |
| 296 | * Stop frame buffer output: just assert TS_FLUSH if necessary. |
| 297 | */ |
| 298 | static void |
| 299 | cnfbstop(tp, flag) |
| 300 | register struct tty *tp; |
| 301 | int flag; |
| 302 | { |
| 303 | register int s = spltty(); /* paranoid */ |
| 304 | |
| 305 | if ((tp->t_state & (TS_BUSY | TS_TTSTOP)) == TS_BUSY) |
| 306 | tp->t_state |= TS_FLUSH; |
| 307 | splx(s); |
| 308 | } |
| 309 | |
| 310 | /* |
| 311 | * Do pseudo-dma (called from software interrupt). |
| 312 | */ |
| 313 | static void |
| 314 | cnfbdma(tpaddr) |
| 315 | void *tpaddr; |
| 316 | { |
| 317 | register struct tty *tp = tpaddr; |
| 318 | register char *p, *q; |
| 319 | register int n, c, s; |
| 320 | |
| 321 | s = spltty(); /* paranoid */ |
| 322 | if (tp->t_state & TS_FLUSH) { |
| 323 | tp->t_state &= ~(TS_BUSY | TS_FLUSH); |
| 324 | splx(s); |
| 325 | } else { |
| 326 | tp->t_state &= ~TS_BUSY; |
| 327 | splx(s); |
| 328 | p = tp->t_outq.c_cf; |
| 329 | n = ndqb(&tp->t_outq, 0); |
| 330 | for (q = p, c = n; --c >= 0; q++) |
| 331 | if (*q & 0200) /* high bits seem to be bad */ |
| 332 | *q &= ~0200; |
| 333 | (*promvec->pv_putstr)(p, n); |
| 334 | ndflush(&tp->t_outq, n); |
| 335 | } |
| 336 | if (tp->t_line) |
| 337 | (*linesw[tp->t_line].l_start)(tp); |
| 338 | else |
| 339 | cnfbstart(tp); |
| 340 | } |
| 341 | |
| 342 | /* |
| 343 | * The following is for rom console input. The rom will not call |
| 344 | * an `interrupt' routine on console input ready, so we must poll. |
| 345 | * This is all rather sad. |
| 346 | */ |
| 347 | volatile int cn_rxc; /* XXX receive `silo' */ |
| 348 | |
| 349 | /* called from hardclock, which is above spltty, so no tty calls! */ |
| 350 | cnrom() |
| 351 | { |
| 352 | register int c; |
| 353 | |
| 354 | if (cn_rxc >= 0) |
| 355 | return (1); |
| 356 | if ((c = (*promvec->pv_nbgetchar)()) < 0) |
| 357 | return (0); |
| 358 | cn_rxc = c; |
| 359 | return (1); |
| 360 | } |
| 361 | |
| 362 | /* pseudo console software interrupt scheduled when cnrom() returns 1 */ |
| 363 | cnrint() |
| 364 | { |
| 365 | register struct tty *tp; |
| 366 | register int c, s; |
| 367 | |
| 368 | s = splclock(); |
| 369 | c = cn_rxc; |
| 370 | cn_rxc = -1; |
| 371 | splx(s); |
| 372 | if (c < 0) |
| 373 | return; |
| 374 | tp = &cons; |
| 375 | if ((tp->t_cflag & CSIZE) == CS7) { |
| 376 | /* XXX this should be done elsewhere, if at all */ |
| 377 | if (tp->t_cflag & PARENB) |
| 378 | if (tp->t_cflag & PARODD ? |
| 379 | (partab[c & 0177] & 0200) == (c & 0200) : |
| 380 | (partab[c & 0177] & 0200) != (c & 0200)) |
| 381 | c |= TTY_PE; |
| 382 | c &= ~0200; |
| 383 | } |
| 384 | (*linesw[tp->t_line].l_rint)(c, tp); |
| 385 | } |
| 386 | |
| 387 | cngetc() |
| 388 | { |
| 389 | register int s, c; |
| 390 | |
| 391 | s = splhigh(); |
| 392 | c = (*promvec->pv_getchar)(); |
| 393 | splx(s); |
| 394 | if (c == '\r') |
| 395 | c = '\n'; |
| 396 | return (c); |
| 397 | } |
| 398 | |
| 399 | cnputc(c) |
| 400 | register int c; |
| 401 | { |
| 402 | register int s = splhigh(); |
| 403 | |
| 404 | if (c == '\n') |
| 405 | (*promvec->pv_putchar)('\r'); |
| 406 | (*promvec->pv_putchar)(c); |
| 407 | splx(s); |
| 408 | } |