pcons.c revision 1.27
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 63struct 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 89typedef 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) 100void cn_init_magic(cnm_state_t *cnm); 101void cn_destroy_magic(cnm_state_t *cnm); 102int cn_set_magic(char *magic); 103int 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 130int pconsmatch(struct device *, void *, void *); 131void pconsattach(struct device *, struct device *, void *); 132 133const struct cfattach pcons_ca = { 134 sizeof(struct pconssoftc), pconsmatch, pconsattach 135}; 136 137struct cfdriver pcons_cd = { 138 NULL, "pcons", DV_TTY 139}; 140 141extern struct cfdriver pcons_cd; 142static struct cnm_state pcons_cnm_state; 143 144static int pconsprobe(void); 145static void pcons_wsdisplay_init(struct pconssoftc *); 146extern struct consdev *cn_tab; 147 148cons_decl(prom_); 149 150int 151pconsmatch(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 160void pcons_poll(void *); 161 162void 163pconsattach(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 200void pconsstart(struct tty *); 201int pconsparam(struct tty *, struct termios *); 202 203int 204pconsopen(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 247int 248pconsclose(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 260int 261pconsread(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 269int 270pconswrite(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 278int 279pconsioctl(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 292struct tty * 293pconstty(dev_t dev) 294{ 295 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 296 297 return sc->of_tty; 298} 299 300int 301pconsstop(struct tty *tp, int flag) 302{ 303 return 0; 304} 305 306void 307pconsstart(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 339int 340pconsparam(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 348void 349pcons_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 366int 367pconsprobe(void) 368{ 369 if (!stdin) stdin = OF_stdin(); 370 if (!stdout) stdout = OF_stdout(); 371 372 return (stdin && stdout); 373} 374 375void 376pcons_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 402static unsigned short cn_magic[CNS_LEN]; 403 404/* 405 * Initialize a cnm_state_t. 406 */ 407void 408cn_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 */ 417void 418cn_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 */ 428int 429cn_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 */ 486int 487cn_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 523int pcons_alloc_screen(void *, const struct wsscreen_descr *, void **, 524 int *, int *, uint32_t *); 525void pcons_cursor(void *, int, int, int); 526void pcons_free_screen(void *, void *); 527int pcons_ioctl(void *, u_long, caddr_t, int, struct proc *); 528int pcons_mapchar(void *, int, unsigned int *); 529paddr_t pcons_mmap(void *, off_t, int); 530int pcons_putchar(void *, int, int, u_int, uint32_t); 531int pcons_show_screen(void *, void *, int, void (*)(void *, int, int), 532 void *); 533 534struct wsdisplay_emulops pcons_emulops = { 535 NULL, 536 pcons_mapchar, 537 pcons_putchar 538}; 539 540struct wsscreen_descr pcons_stdscreen = { 541 "dumb", 80, 34, &pcons_emulops, 12, 22, 0 542}; 543 544const struct wsscreen_descr *pcons_scrlist[] = { 545 &pcons_stdscreen 546}; 547 548struct wsscreen_list pcons_screenlist = { 549 1, pcons_scrlist 550}; 551 552struct 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 560int 561pcons_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 588void 589pcons_free_screen(void *v, void *cookie) 590{ 591 struct pconssoftc *sc = v; 592 593 sc->sc_nscreens--; 594} 595 596int 597pcons_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 610paddr_t 611pcons_mmap(void *v, off_t off, int prot) 612{ 613 return ((paddr_t)-1); 614} 615 616int 617pcons_show_screen(void *v, void *cookie, int waitok, 618 void (*cb)(void *, int, int), void *arg) 619{ 620 return (0); 621} 622 623int 624pcons_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 635int 636pcons_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 649void 650pcons_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