pcons.c revision 1.2
1/* $OpenBSD: pcons.c,v 1.2 2001/09/04 13:22:13 art 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/* XXXXXXXX - this is in MI code in NetBSD */ 60/* 61 * Stuff to handle debugger magic key sequences. 62 */ 63#define CNS_LEN 128 64#define CNS_MAGIC_VAL(x) ((x)&0x1ff) 65#define CNS_MAGIC_NEXT(x) (((x)>>9)&0x7f) 66#define CNS_TERM 0x7f /* End of sequence */ 67 68typedef struct cnm_state { 69 int cnm_state; 70 u_short *cnm_magic; 71} cnm_state_t; 72extern int db_console; 73#ifdef DDB 74#define cn_trap() do { if (db_console) Debugger(); } while (0) 75#else 76#define cn_trap() 77#endif 78#define cn_isconsole(d) ((d) == cn_tab->cn_dev) 79void cn_init_magic __P((cnm_state_t *cnm)); 80void cn_destroy_magic __P((cnm_state_t *cnm)); 81int cn_set_magic __P((char *magic)); 82int cn_get_magic __P((char *magic, int len)); 83/* This should be called for each byte read */ 84#ifndef cn_check_magic 85#define cn_check_magic(d, k, s) \ 86 do { \ 87 if (cn_isconsole(d)) { \ 88 int v = (s).cnm_magic[(s).cnm_state]; \ 89 if ((k) == CNS_MAGIC_VAL(v)) { \ 90 (s).cnm_state = CNS_MAGIC_NEXT(v); \ 91 if ((s).cnm_state == CNS_TERM) { \ 92 cn_trap(); \ 93 (s).cnm_state = 0; \ 94 } \ 95 } else { \ 96 (s).cnm_state = 0; \ 97 } \ 98 } \ 99 } while (/* CONSTCOND */ 0) 100#endif 101 102/* Encode out-of-band events this way when passing to cn_check_magic() */ 103#define CNC_BREAK 0x100 104 105/* XXXXXXXXXX - end of this part of cnmagic, more at the end of this file. */ 106 107#include <sparc64/dev/cons.h> 108 109static int pconsmatch __P((struct device *, void *, void *)); 110static void pconsattach __P((struct device *, struct device *, void *)); 111 112struct cfattach pcons_ca = { 113 sizeof(struct pconssoftc), pconsmatch, pconsattach 114}; 115 116struct cfdriver pcons_cd = { 117 NULL, "pcons", DV_TTY 118}; 119 120extern struct cfdriver pcons_cd; 121static struct cnm_state pcons_cnm_state; 122 123static int pconsprobe __P((void)); 124extern struct consdev *cn_tab; 125 126static int 127pconsmatch(parent, match, aux) 128 struct device *parent; 129 void *match; 130 void *aux; 131{ 132 struct mainbus_attach_args *ma = aux; 133 extern int prom_cngetc __P((dev_t)); 134 135 /* Only attach if no other console has attached. */ 136 return ((strcmp("pcons", ma->ma_name) == 0) && 137 (cn_tab->cn_getc == prom_cngetc)); 138 139} 140 141static void pcons_poll __P((void *)); 142 143static void 144pconsattach(parent, self, aux) 145 struct device *parent, *self; 146 void *aux; 147{ 148 struct pconssoftc *sc = (struct pconssoftc *) self; 149 150 printf("\n"); 151 if (!pconsprobe()) 152 return; 153 154 cn_init_magic(&pcons_cnm_state); 155 cn_set_magic("+++++"); 156 timeout_set(&sc->sc_poll_to, pcons_poll, sc); 157} 158 159static void pconsstart __P((struct tty *)); 160static int pconsparam __P((struct tty *, struct termios *)); 161 162int 163pconsopen(dev, flag, mode, p) 164 dev_t dev; 165 int flag, mode; 166 struct proc *p; 167{ 168 struct pconssoftc *sc; 169 int unit = minor(dev); 170 struct tty *tp; 171 172 if (unit >= pcons_cd.cd_ndevs) 173 return ENXIO; 174 sc = pcons_cd.cd_devs[unit]; 175 if (!sc) 176 return ENXIO; 177 if (!(tp = sc->of_tty)) { 178 sc->of_tty = tp = ttymalloc(); 179 tty_attach(tp); 180 } 181 tp->t_oproc = pconsstart; 182 tp->t_param = pconsparam; 183 tp->t_dev = dev; 184 cn_tab->cn_dev = dev; 185 if (!(tp->t_state & TS_ISOPEN)) { 186 ttychars(tp); 187 tp->t_iflag = TTYDEF_IFLAG; 188 tp->t_oflag = TTYDEF_OFLAG; 189 tp->t_cflag = TTYDEF_CFLAG; 190 tp->t_lflag = TTYDEF_LFLAG; 191 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 192 pconsparam(tp, &tp->t_termios); 193 ttsetwater(tp); 194 } else if ((tp->t_state&TS_XCLUDE) && suser(p->p_ucred, &p->p_acflag)) 195 return EBUSY; 196 tp->t_state |= TS_CARR_ON; 197 198 if (!(sc->of_flags & OFPOLL)) { 199 sc->of_flags |= OFPOLL; 200 timeout_add(&sc->sc_poll_to, 1); 201 } 202 203 return (*linesw[tp->t_line].l_open)(dev, tp); 204} 205 206int 207pconsclose(dev, flag, mode, p) 208 dev_t dev; 209 int flag, mode; 210 struct proc *p; 211{ 212 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 213 struct tty *tp = sc->of_tty; 214 215 timeout_del(&sc->sc_poll_to); 216 sc->of_flags &= ~OFPOLL; 217 (*linesw[tp->t_line].l_close)(tp, flag); 218 ttyclose(tp); 219 return 0; 220} 221 222int 223pconsread(dev, uio, flag) 224 dev_t dev; 225 struct uio *uio; 226 int flag; 227{ 228 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 229 struct tty *tp = sc->of_tty; 230 231 return (*linesw[tp->t_line].l_read)(tp, uio, flag); 232} 233 234int 235pconswrite(dev, uio, flag) 236 dev_t dev; 237 struct uio *uio; 238 int flag; 239{ 240 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 241 struct tty *tp = sc->of_tty; 242 243 return (*linesw[tp->t_line].l_write)(tp, uio, flag); 244} 245 246int 247pconsioctl(dev, cmd, data, flag, p) 248 dev_t dev; 249 u_long cmd; 250 caddr_t data; 251 int flag; 252 struct proc *p; 253{ 254 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 255 struct tty *tp = sc->of_tty; 256 int error; 257 258 if ((error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p)) >= 0) 259 return error; 260 if ((error = ttioctl(tp, cmd, data, flag, p)) >= 0) 261 return error; 262 return ENOTTY; 263} 264 265struct tty * 266pconstty(dev) 267 dev_t dev; 268{ 269 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 270 271 return sc->of_tty; 272} 273 274int 275pconsstop(tp, flag) 276 struct tty *tp; 277 int flag; 278{ 279 return 0; 280} 281 282static void 283pconsstart(tp) 284 struct tty *tp; 285{ 286 struct clist *cl; 287 int s, len; 288 u_char buf[OFBURSTLEN]; 289 290 s = spltty(); 291 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { 292 splx(s); 293 return; 294 } 295 tp->t_state |= TS_BUSY; 296 splx(s); 297 cl = &tp->t_outq; 298 len = q_to_b(cl, buf, OFBURSTLEN); 299 OF_write(stdout, buf, len); 300 s = spltty(); 301 tp->t_state &= ~TS_BUSY; 302 if (cl->c_cc) { 303 tp->t_state |= TS_TIMEOUT; 304 timeout_add(&tp->t_rstrt_to, 1); 305 } 306 if (cl->c_cc <= tp->t_lowat) { 307 if (tp->t_state & TS_ASLEEP) { 308 tp->t_state &= ~TS_ASLEEP; 309 wakeup(cl); 310 } 311 selwakeup(&tp->t_wsel); 312 } 313 splx(s); 314} 315 316static int 317pconsparam(tp, t) 318 struct tty *tp; 319 struct termios *t; 320{ 321 tp->t_ispeed = t->c_ispeed; 322 tp->t_ospeed = t->c_ospeed; 323 tp->t_cflag = t->c_cflag; 324 return 0; 325} 326 327static void 328pcons_poll(aux) 329 void *aux; 330{ 331 struct pconssoftc *sc = aux; 332 struct tty *tp = sc->of_tty; 333 char ch; 334 335 while (OF_read(stdin, &ch, 1) > 0) { 336 cn_check_magic(tp->t_dev, ch, pcons_cnm_state); 337 if (tp && (tp->t_state & TS_ISOPEN)) 338 (*linesw[tp->t_line].l_rint)(ch, tp); 339 } 340 timeout_add(&sc->sc_poll_to, 1); 341} 342 343int 344pconsprobe() 345{ 346 if (!stdin) stdin = OF_stdin(); 347 if (!stdout) stdout = OF_stdout(); 348 349 return (stdin && stdout); 350} 351 352void 353pcons_cnpollc(dev, on) 354 dev_t dev; 355 int on; 356{ 357 struct pconssoftc *sc = NULL; 358 359 if (pcons_cd.cd_devs) 360 sc = pcons_cd.cd_devs[minor(dev)]; 361 362 if (on) { 363 if (!sc) return; 364 if (sc->of_flags & OFPOLL) 365 timeout_del(&sc->sc_poll_to); 366 sc->of_flags &= ~OFPOLL; 367 } else { 368 /* Resuming kernel. */ 369 if (sc && !(sc->of_flags & OFPOLL)) { 370 sc->of_flags |= OFPOLL; 371 timeout_add(&sc->sc_poll_to, 1); 372 } 373 } 374} 375 376void pcons_dopoll __P((void)); 377void 378pcons_dopoll() { 379 pcons_poll((void*)pcons_cd.cd_devs[0]); 380} 381 382/* XXXXXXXX --- more cnmagic stuff. */ 383#define ENCODE_STATE(c, n) (short)(((c)&0x1ff)|(((n)&0x7f)<<9)) 384 385static unsigned short cn_magic[CNS_LEN]; 386 387/* 388 * Initialize a cnm_state_t. 389 */ 390void 391cn_init_magic(cnm_state_t *cnm) 392{ 393 cnm->cnm_state = 0; 394 cnm->cnm_magic = cn_magic; 395} 396 397/* 398 * Destroy a cnm_state_t. 399 */ 400void 401cn_destroy_magic(cnm_state_t *cnm) 402{ 403 cnm->cnm_state = 0; 404 cnm->cnm_magic = NULL; 405} 406 407/* 408 * Translate a magic string to a state 409 * machine table. 410 */ 411int 412cn_set_magic(char *magic) 413{ 414 unsigned int i, c, n; 415 unsigned short m[CNS_LEN]; 416 417 for (i=0; i<CNS_LEN; i++) { 418 c = (*magic++)&0xff; 419 n = *magic ? i+1 : CNS_TERM; 420 switch (c) { 421 case 0: 422 /* End of string */ 423 if (i == 0) { 424 /* empty string? */ 425 cn_magic[0] = 0; 426#ifdef DEBUG 427 printf("cn_set_magic(): empty!\n"); 428#endif 429 return (0); 430 } 431 do { 432 cn_magic[i] = m[i]; 433 } while (i--); 434 return(0); 435 case 0x27: 436 /* Escape sequence */ 437 c = (*magic++)&0xff; 438 n = *magic ? i+1 : CNS_TERM; 439 switch (c) { 440 case 0x27: 441 break; 442 case 0x01: 443 /* BREAK */ 444 c = CNC_BREAK; 445 break; 446 case 0x02: 447 /* NUL */ 448 c = 0; 449 break; 450 } 451 /* FALLTHROUGH */ 452 default: 453 /* Transition to the next state. */ 454#ifdef DEBUG 455 if (!cold) 456 printf("mag %d %x:%x\n", i, c, n); 457#endif 458 m[i] = ENCODE_STATE(c, n); 459 break; 460 } 461 } 462 return (EINVAL); 463} 464 465/* 466 * Translatea state machine table back to 467 * a magic string. 468 */ 469int 470cn_get_magic(char *magic, int maglen) { 471 unsigned int i, c; 472 473 for (i=0; i<CNS_LEN; i++) { 474 c = cn_magic[i]; 475 /* Translate a character */ 476 switch (CNS_MAGIC_VAL(c)) { 477 case CNC_BREAK: 478 *magic++ = 0x27; 479 *magic++ = 0x01; 480 break; 481 case 0: 482 *magic++ = 0x27; 483 *magic++ = 0x02; 484 break; 485 case 0x27: 486 *magic++ = 0x27; 487 *magic++ = 0x27; 488 break; 489 default: 490 *magic++ = (c&0x0ff); 491 break; 492 } 493 /* Now go to the next state */ 494 i = CNS_MAGIC_NEXT(c); 495 if (i == CNS_TERM || i == 0) { 496 /* Either termination state or empty machine */ 497 *magic++ = 0; 498 return (0); 499 } 500 } 501 return (EINVAL); 502} 503 504