pcons.c revision 1.14
1/* $OpenBSD: pcons.c,v 1.14 2009/09/05 14:09:35 miod 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 64struct 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 90typedef 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) 101void cn_init_magic(cnm_state_t *cnm); 102void cn_destroy_magic(cnm_state_t *cnm); 103int cn_set_magic(char *magic); 104int 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 131int pconsmatch(struct device *, void *, void *); 132void pconsattach(struct device *, struct device *, void *); 133 134struct cfattach pcons_ca = { 135 sizeof(struct pconssoftc), pconsmatch, pconsattach 136}; 137 138struct cfdriver pcons_cd = { 139 NULL, "pcons", DV_TTY 140}; 141 142extern struct cfdriver pcons_cd; 143static struct cnm_state pcons_cnm_state; 144 145static int pconsprobe(void); 146static void pcons_wsdisplay_init(struct pconssoftc *); 147extern struct consdev *cn_tab; 148 149cons_decl(prom_); 150 151int 152pconsmatch(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 164void pcons_poll(void *); 165 166void 167pconsattach(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 206void pconsstart(struct tty *); 207int pconsparam(struct tty *, struct termios *); 208 209int 210pconsopen(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(); 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); 254} 255 256int 257pconsclose(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); 268 ttyclose(tp); 269 return 0; 270} 271 272int 273pconsread(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 284int 285pconswrite(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 296int 297pconsioctl(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 315struct tty * 316pconstty(dev) 317 dev_t dev; 318{ 319 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 320 321 return sc->of_tty; 322} 323 324int 325pconsstop(tp, flag) 326 struct tty *tp; 327 int flag; 328{ 329 return 0; 330} 331 332void 333pconsstart(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 366int 367pconsparam(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 377void 378pcons_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 396int 397pconsprobe() 398{ 399 if (!stdin) stdin = OF_stdin(); 400 if (!stdout) stdout = OF_stdout(); 401 402 return (stdin && stdout); 403} 404 405void 406pcons_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 434static unsigned short cn_magic[CNS_LEN]; 435 436/* 437 * Initialize a cnm_state_t. 438 */ 439void 440cn_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 */ 449void 450cn_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 */ 460int 461cn_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 */ 518int 519cn_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 555int pcons_alloc_screen(void *, const struct wsscreen_descr *, void **, 556 int *, int *, long *); 557void pcons_cursor(void *, int, int, int); 558void pcons_free_screen(void *, void *); 559int pcons_ioctl(void *, u_long, caddr_t, int, struct proc *); 560int pcons_mapchar(void *, int, unsigned int *); 561paddr_t pcons_mmap(void *, off_t, int); 562int pcons_putchar(void *, int, int, u_int, long); 563int pcons_show_screen(void *, void *, int, void (*)(void *, int, int), 564 void *); 565 566struct wsdisplay_emulops pcons_emulops = { 567 NULL, 568 pcons_mapchar, 569 pcons_putchar 570}; 571 572struct wsscreen_descr pcons_stdscreen = { 573 "dumb", 80, 34, &pcons_emulops, 12, 22, 0 574}; 575 576const struct wsscreen_descr *pcons_scrlist[] = { 577 &pcons_stdscreen 578}; 579 580struct wsscreen_list pcons_screenlist = { 581 1, pcons_scrlist 582}; 583 584struct wsdisplay_accessops pcons_accessops = { 585 pcons_ioctl, 586 pcons_mmap, 587 pcons_alloc_screen, 588 pcons_free_screen, 589 pcons_show_screen 590}; 591 592int 593pcons_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 620void 621pcons_free_screen(void *v, void *cookie) 622{ 623 struct pconssoftc *sc = v; 624 625 sc->sc_nscreens--; 626} 627 628int 629pcons_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 642paddr_t 643pcons_mmap(void *v, off_t off, int prot) 644{ 645 return ((paddr_t)-1); 646} 647 648int 649pcons_show_screen(void *v, void *cookie, int waitok, 650 void (*cb)(void *, int, int), void *arg) 651{ 652 return (0); 653} 654 655int 656pcons_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 667int 668pcons_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 681void 682pcons_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