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