1 /* $OpenBSD: pcons.c,v 1.18 2010/06/28 14:13:31 deraadt Exp $ */ 2 /* $NetBSD: pcons.c,v 1.7 2001/05/02 10:32:20 scw Exp $ */ 3 4 /*- 5 * Copyright (c) 2000 Eduardo E. Horvath 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 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. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Default console driver. Uses the PROM or whatever 34 * driver(s) are appropriate. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/conf.h> 40 #include <sys/device.h> 41 #include <sys/file.h> 42 #include <sys/ioctl.h> 43 #include <sys/kernel.h> 44 #include <sys/proc.h> 45 #include <sys/tty.h> 46 #include <sys/time.h> 47 #include <sys/syslog.h> 48 49 #include <machine/autoconf.h> 50 #include <machine/openfirm.h> 51 #include <machine/conf.h> 52 #include <machine/cpu.h> 53 #include <machine/psl.h> 54 55 #include <dev/cons.h> 56 57 #include "wsdisplay.h" 58 59 #if NWSDISPLAY > 0 60 #include <dev/wscons/wsconsio.h> 61 #include <dev/wscons/wsdisplayvar.h> 62 #endif 63 64 struct pconssoftc { 65 struct device of_dev; 66 67 #if NWSDISPLAY > 0 68 int sc_wsdisplay; 69 u_int sc_nscreens; 70 #endif 71 72 struct tty *of_tty; 73 struct timeout sc_poll_to; 74 int of_flags; 75 }; 76 /* flags: */ 77 #define OFPOLL 1 78 79 #define OFBURSTLEN 128 /* max number of bytes to write in one chunk */ 80 81 /* XXXXXXXX - this is in MI code in NetBSD */ 82 /* 83 * Stuff to handle debugger magic key sequences. 84 */ 85 #define CNS_LEN 128 86 #define CNS_MAGIC_VAL(x) ((x)&0x1ff) 87 #define CNS_MAGIC_NEXT(x) (((x)>>9)&0x7f) 88 #define CNS_TERM 0x7f /* End of sequence */ 89 90 typedef struct cnm_state { 91 int cnm_state; 92 u_short *cnm_magic; 93 } cnm_state_t; 94 #ifdef DDB 95 #include <ddb/db_var.h> 96 #define cn_trap() do { if (db_console) Debugger(); } while (0) 97 #else 98 #define cn_trap() 99 #endif 100 #define cn_isconsole(d) ((d) == cn_tab->cn_dev) 101 void cn_init_magic(cnm_state_t *cnm); 102 void cn_destroy_magic(cnm_state_t *cnm); 103 int cn_set_magic(char *magic); 104 int cn_get_magic(char *magic, int len); 105 /* This should be called for each byte read */ 106 #ifndef cn_check_magic 107 #define cn_check_magic(d, k, s) \ 108 do { \ 109 if (cn_isconsole(d)) { \ 110 int v = (s).cnm_magic[(s).cnm_state]; \ 111 if ((k) == CNS_MAGIC_VAL(v)) { \ 112 (s).cnm_state = CNS_MAGIC_NEXT(v); \ 113 if ((s).cnm_state == CNS_TERM) { \ 114 cn_trap(); \ 115 (s).cnm_state = 0; \ 116 } \ 117 } else { \ 118 (s).cnm_state = 0; \ 119 } \ 120 } \ 121 } while (/* CONSTCOND */ 0) 122 #endif 123 124 /* Encode out-of-band events this way when passing to cn_check_magic() */ 125 #define CNC_BREAK 0x100 126 127 /* XXXXXXXXXX - end of this part of cnmagic, more at the end of this file. */ 128 129 #include <sparc64/dev/cons.h> 130 131 int pconsmatch(struct device *, void *, void *); 132 void pconsattach(struct device *, struct device *, void *); 133 134 struct cfattach pcons_ca = { 135 sizeof(struct pconssoftc), pconsmatch, pconsattach 136 }; 137 138 struct cfdriver pcons_cd = { 139 NULL, "pcons", DV_TTY 140 }; 141 142 extern struct cfdriver pcons_cd; 143 static struct cnm_state pcons_cnm_state; 144 145 static int pconsprobe(void); 146 static void pcons_wsdisplay_init(struct pconssoftc *); 147 extern struct consdev *cn_tab; 148 149 cons_decl(prom_); 150 151 int 152 pconsmatch(parent, match, aux) 153 struct device *parent; 154 void *match; 155 void *aux; 156 { 157 struct mainbus_attach_args *ma = aux; 158 159 /* Only attach if no other console has attached. */ 160 return (strcmp("pcons", ma->ma_name) == 0 && 161 cn_tab->cn_getc == prom_cngetc); 162 } 163 164 void pcons_poll(void *); 165 166 void 167 pconsattach(parent, self, aux) 168 struct device *parent, *self; 169 void *aux; 170 { 171 struct pconssoftc *sc = (struct pconssoftc *) self; 172 #if NWSDISPLAY > 0 173 char buffer[128]; 174 extern struct consdev wsdisplay_cons; 175 extern int wsdisplay_getc_dummy(dev_t); 176 #endif 177 178 printf("\n"); 179 if (!pconsprobe()) 180 return; 181 182 #if NWSDISPLAY > 0 183 /* 184 * Attach a dumb wsdisplay device if a wscons input driver has 185 * registered as the console, or is about to do so (usb keyboards). 186 */ 187 if (wsdisplay_cons.cn_getc != wsdisplay_getc_dummy) 188 sc->sc_wsdisplay = 1; 189 else { 190 if (OF_getprop(OF_instance_to_package(stdin), "compatible", 191 buffer, sizeof(buffer)) != -1 && 192 strncmp("usb", buffer, 3) == 0) 193 sc->sc_wsdisplay = 1; 194 } 195 196 if (sc->sc_wsdisplay != 0) { 197 pcons_wsdisplay_init(sc); 198 return; 199 } 200 #endif 201 cn_init_magic(&pcons_cnm_state); 202 cn_set_magic("+++++"); 203 timeout_set(&sc->sc_poll_to, pcons_poll, sc); 204 } 205 206 void pconsstart(struct tty *); 207 int pconsparam(struct tty *, struct termios *); 208 209 int 210 pconsopen(dev, flag, mode, p) 211 dev_t dev; 212 int flag, mode; 213 struct proc *p; 214 { 215 struct pconssoftc *sc; 216 int unit = minor(dev); 217 struct tty *tp; 218 219 if (unit >= pcons_cd.cd_ndevs) 220 return ENXIO; 221 sc = pcons_cd.cd_devs[unit]; 222 if (!sc) 223 return ENXIO; 224 #if NWSDISPLAY > 0 225 if (sc->sc_wsdisplay != 0) 226 return ENXIO; 227 #endif 228 if (!(tp = sc->of_tty)) { 229 sc->of_tty = tp = ttymalloc(0); 230 } 231 tp->t_oproc = pconsstart; 232 tp->t_param = pconsparam; 233 tp->t_dev = dev; 234 cn_tab->cn_dev = dev; 235 if (!(tp->t_state & TS_ISOPEN)) { 236 ttychars(tp); 237 tp->t_iflag = TTYDEF_IFLAG; 238 tp->t_oflag = TTYDEF_OFLAG; 239 tp->t_cflag = TTYDEF_CFLAG; 240 tp->t_lflag = TTYDEF_LFLAG; 241 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 242 pconsparam(tp, &tp->t_termios); 243 ttsetwater(tp); 244 } else if ((tp->t_state & TS_XCLUDE) && suser(p, 0)) 245 return EBUSY; 246 tp->t_state |= TS_CARR_ON; 247 248 if (!(sc->of_flags & OFPOLL)) { 249 sc->of_flags |= OFPOLL; 250 timeout_add(&sc->sc_poll_to, 1); 251 } 252 253 return (*linesw[tp->t_line].l_open)(dev, tp, p); 254 } 255 256 int 257 pconsclose(dev, flag, mode, p) 258 dev_t dev; 259 int flag, mode; 260 struct proc *p; 261 { 262 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 263 struct tty *tp = sc->of_tty; 264 265 timeout_del(&sc->sc_poll_to); 266 sc->of_flags &= ~OFPOLL; 267 (*linesw[tp->t_line].l_close)(tp, flag, p); 268 ttyclose(tp); 269 return 0; 270 } 271 272 int 273 pconsread(dev, uio, flag) 274 dev_t dev; 275 struct uio *uio; 276 int flag; 277 { 278 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 279 struct tty *tp = sc->of_tty; 280 281 return (*linesw[tp->t_line].l_read)(tp, uio, flag); 282 } 283 284 int 285 pconswrite(dev, uio, flag) 286 dev_t dev; 287 struct uio *uio; 288 int flag; 289 { 290 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 291 struct tty *tp = sc->of_tty; 292 293 return (*linesw[tp->t_line].l_write)(tp, uio, flag); 294 } 295 296 int 297 pconsioctl(dev, cmd, data, flag, p) 298 dev_t dev; 299 u_long cmd; 300 caddr_t data; 301 int flag; 302 struct proc *p; 303 { 304 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 305 struct tty *tp = sc->of_tty; 306 int error; 307 308 if ((error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p)) >= 0) 309 return error; 310 if ((error = ttioctl(tp, cmd, data, flag, p)) >= 0) 311 return error; 312 return ENOTTY; 313 } 314 315 struct tty * 316 pconstty(dev) 317 dev_t dev; 318 { 319 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 320 321 return sc->of_tty; 322 } 323 324 int 325 pconsstop(tp, flag) 326 struct tty *tp; 327 int flag; 328 { 329 return 0; 330 } 331 332 void 333 pconsstart(tp) 334 struct tty *tp; 335 { 336 struct clist *cl; 337 int s, len; 338 u_char buf[OFBURSTLEN]; 339 340 s = spltty(); 341 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { 342 splx(s); 343 return; 344 } 345 tp->t_state |= TS_BUSY; 346 splx(s); 347 cl = &tp->t_outq; 348 len = q_to_b(cl, buf, OFBURSTLEN); 349 OF_write(stdout, buf, len); 350 s = spltty(); 351 tp->t_state &= ~TS_BUSY; 352 if (cl->c_cc) { 353 tp->t_state |= TS_TIMEOUT; 354 timeout_add(&tp->t_rstrt_to, 1); 355 } 356 if (cl->c_cc <= tp->t_lowat) { 357 if (tp->t_state & TS_ASLEEP) { 358 tp->t_state &= ~TS_ASLEEP; 359 wakeup(cl); 360 } 361 selwakeup(&tp->t_wsel); 362 } 363 splx(s); 364 } 365 366 int 367 pconsparam(tp, t) 368 struct tty *tp; 369 struct termios *t; 370 { 371 tp->t_ispeed = t->c_ispeed; 372 tp->t_ospeed = t->c_ospeed; 373 tp->t_cflag = t->c_cflag; 374 return 0; 375 } 376 377 void 378 pcons_poll(aux) 379 void *aux; 380 { 381 struct pconssoftc *sc = aux; 382 struct tty *tp = sc->of_tty; 383 char ch; 384 385 while (OF_read(stdin, &ch, 1) > 0) { 386 cn_check_magic(tp->t_dev, ch, pcons_cnm_state); 387 if (tp && (tp->t_state & TS_ISOPEN)) { 388 if (ch == '\b') 389 ch = '\177'; 390 (*linesw[tp->t_line].l_rint)(ch, tp); 391 } 392 } 393 timeout_add(&sc->sc_poll_to, 1); 394 } 395 396 int 397 pconsprobe() 398 { 399 if (!stdin) stdin = OF_stdin(); 400 if (!stdout) stdout = OF_stdout(); 401 402 return (stdin && stdout); 403 } 404 405 void 406 pcons_cnpollc(dev, on) 407 dev_t dev; 408 int on; 409 { 410 struct pconssoftc *sc = NULL; 411 412 if (pcons_cd.cd_devs) 413 sc = pcons_cd.cd_devs[minor(dev)]; 414 415 if (sc == NULL) 416 return; 417 418 if (on) { 419 if (sc->of_flags & OFPOLL) 420 timeout_del(&sc->sc_poll_to); 421 sc->of_flags &= ~OFPOLL; 422 } else { 423 /* Resuming kernel. */ 424 if (!(sc->of_flags & OFPOLL)) { 425 sc->of_flags |= OFPOLL; 426 timeout_add(&sc->sc_poll_to, 1); 427 } 428 } 429 } 430 431 /* XXXXXXXX --- more cnmagic stuff. */ 432 #define ENCODE_STATE(c, n) (short)(((c)&0x1ff)|(((n)&0x7f)<<9)) 433 434 static unsigned short cn_magic[CNS_LEN]; 435 436 /* 437 * Initialize a cnm_state_t. 438 */ 439 void 440 cn_init_magic(cnm_state_t *cnm) 441 { 442 cnm->cnm_state = 0; 443 cnm->cnm_magic = cn_magic; 444 } 445 446 /* 447 * Destroy a cnm_state_t. 448 */ 449 void 450 cn_destroy_magic(cnm_state_t *cnm) 451 { 452 cnm->cnm_state = 0; 453 cnm->cnm_magic = NULL; 454 } 455 456 /* 457 * Translate a magic string to a state 458 * machine table. 459 */ 460 int 461 cn_set_magic(char *magic) 462 { 463 unsigned int i, c, n; 464 unsigned short m[CNS_LEN]; 465 466 for (i=0; i<CNS_LEN; i++) { 467 c = (*magic++)&0xff; 468 n = *magic ? i+1 : CNS_TERM; 469 switch (c) { 470 case 0: 471 /* End of string */ 472 if (i == 0) { 473 /* empty string? */ 474 cn_magic[0] = 0; 475 #ifdef DEBUG 476 printf("cn_set_magic(): empty!\n"); 477 #endif 478 return (0); 479 } 480 do { 481 cn_magic[i] = m[i]; 482 } while (i--); 483 return(0); 484 case 0x27: 485 /* Escape sequence */ 486 c = (*magic++)&0xff; 487 n = *magic ? i+1 : CNS_TERM; 488 switch (c) { 489 case 0x27: 490 break; 491 case 0x01: 492 /* BREAK */ 493 c = CNC_BREAK; 494 break; 495 case 0x02: 496 /* NUL */ 497 c = 0; 498 break; 499 } 500 /* FALLTHROUGH */ 501 default: 502 /* Transition to the next state. */ 503 #ifdef DEBUG 504 if (!cold) 505 printf("mag %d %x:%x\n", i, c, n); 506 #endif 507 m[i] = ENCODE_STATE(c, n); 508 break; 509 } 510 } 511 return (EINVAL); 512 } 513 514 /* 515 * Translate a state machine table back to 516 * a magic string. 517 */ 518 int 519 cn_get_magic(char *magic, int maglen) { 520 unsigned int i, c; 521 522 for (i=0; i<CNS_LEN; i++) { 523 c = cn_magic[i]; 524 /* Translate a character */ 525 switch (CNS_MAGIC_VAL(c)) { 526 case CNC_BREAK: 527 *magic++ = 0x27; 528 *magic++ = 0x01; 529 break; 530 case 0: 531 *magic++ = 0x27; 532 *magic++ = 0x02; 533 break; 534 case 0x27: 535 *magic++ = 0x27; 536 *magic++ = 0x27; 537 break; 538 default: 539 *magic++ = (c&0x0ff); 540 break; 541 } 542 /* Now go to the next state */ 543 i = CNS_MAGIC_NEXT(c); 544 if (i == CNS_TERM || i == 0) { 545 /* Either termination state or empty machine */ 546 *magic++ = 0; 547 return (0); 548 } 549 } 550 return (EINVAL); 551 } 552 553 #if NWSDISPLAY > 0 554 555 int pcons_alloc_screen(void *, const struct wsscreen_descr *, void **, 556 int *, int *, long *); 557 void pcons_cursor(void *, int, int, int); 558 void pcons_free_screen(void *, void *); 559 int pcons_ioctl(void *, u_long, caddr_t, int, struct proc *); 560 int pcons_mapchar(void *, int, unsigned int *); 561 paddr_t pcons_mmap(void *, off_t, int); 562 int pcons_putchar(void *, int, int, u_int, long); 563 int pcons_show_screen(void *, void *, int, void (*)(void *, int, int), 564 void *); 565 566 struct wsdisplay_emulops pcons_emulops = { 567 NULL, 568 pcons_mapchar, 569 pcons_putchar 570 }; 571 572 struct wsscreen_descr pcons_stdscreen = { 573 "dumb", 80, 34, &pcons_emulops, 12, 22, 0 574 }; 575 576 const struct wsscreen_descr *pcons_scrlist[] = { 577 &pcons_stdscreen 578 }; 579 580 struct wsscreen_list pcons_screenlist = { 581 1, pcons_scrlist 582 }; 583 584 struct wsdisplay_accessops pcons_accessops = { 585 pcons_ioctl, 586 pcons_mmap, 587 pcons_alloc_screen, 588 pcons_free_screen, 589 pcons_show_screen 590 }; 591 592 int 593 pcons_alloc_screen(void *v, const struct wsscreen_descr *typ, void **cookiep, 594 int *curxp, int *curyp, long *attrp) 595 { 596 struct pconssoftc *sc = v; 597 int *rowp, *colp; 598 int row, col; 599 600 if (sc->sc_nscreens > 0) 601 return (ENOMEM); 602 603 row = col = 0; 604 if (romgetcursoraddr(&rowp, &colp) == 0) { 605 if (rowp != NULL) 606 row = *rowp; 607 if (colp != NULL) 608 col = *colp; 609 } 610 611 *cookiep = v; 612 *attrp = 0; 613 *curxp = col; 614 *curyp = row; 615 616 sc->sc_nscreens++; 617 return (0); 618 } 619 620 void 621 pcons_free_screen(void *v, void *cookie) 622 { 623 struct pconssoftc *sc = v; 624 625 sc->sc_nscreens--; 626 } 627 628 int 629 pcons_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p) 630 { 631 switch (cmd) { 632 case WSDISPLAYIO_GTYPE: 633 *(u_int *)data = WSDISPLAY_TYPE_UNKNOWN; 634 break; 635 default: 636 return (-1); 637 } 638 639 return (0); 640 } 641 642 paddr_t 643 pcons_mmap(void *v, off_t off, int prot) 644 { 645 return ((paddr_t)-1); 646 } 647 648 int 649 pcons_show_screen(void *v, void *cookie, int waitok, 650 void (*cb)(void *, int, int), void *arg) 651 { 652 return (0); 653 } 654 655 int 656 pcons_mapchar(void *v, int uc, unsigned int *idx) 657 { 658 if ((uc & 0xff) == uc) { 659 *idx = uc; 660 return (1); 661 } else { 662 *idx = ' '; 663 return (0); 664 } 665 } 666 667 int 668 pcons_putchar(void *v, int row, int col, u_int uc, long attr) 669 { 670 u_char buf[1]; 671 int s; 672 673 buf[0] = (u_char)uc; 674 s = splhigh(); 675 OF_write(stdout, &buf, 1); 676 splx(s); 677 678 return 0; 679 } 680 681 void 682 pcons_wsdisplay_init(struct pconssoftc *sc) 683 { 684 struct wsemuldisplaydev_attach_args waa; 685 int *rowp, *colp; 686 int options, row, col; 687 688 row = col = 0; 689 if (romgetcursoraddr(&rowp, &colp) == 0) { 690 if (rowp != NULL) 691 row = *rowp; 692 if (colp != NULL) 693 col = *colp; 694 } 695 696 options = OF_finddevice("/options"); 697 pcons_stdscreen.nrows = getpropint(options, "screen-#rows", 34); 698 pcons_stdscreen.ncols = getpropint(options, "screen-#columns", 80); 699 700 /* 701 * We claim console here, because we can only get there if stdin 702 * is a keyboard. However, the PROM could have been configured with 703 * stdin being a keyboard and stdout being a serial sink. 704 * But since this combination is not supported under OpenBSD at the 705 * moment, it is reasonably safe to attach a dumb display as console 706 * here. 707 */ 708 wsdisplay_cnattach(&pcons_stdscreen, sc, col, row, 0); 709 710 waa.console = 1; 711 waa.scrdata = &pcons_screenlist; 712 waa.accessops = &pcons_accessops; 713 waa.accesscookie = sc; 714 waa.defaultscreens = 1; 715 716 config_found((struct device *)sc, &waa, wsemuldisplaydevprint); 717 } 718 #endif 719