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