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