1 /* $NetBSD: kd.c,v 1.55 2008/04/28 20:23:37 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 1996 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Gordon W. Ross. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Keyboard/Display device. 34 * 35 * This driver exists simply to provide a tty device that 36 * the indirect console driver can point to. 37 * The kbd driver sends its input here. 38 * Output goes to the screen via PROM printf. 39 */ 40 41 #include <sys/cdefs.h> 42 __KERNEL_RCSID(0, "$NetBSD: kd.c,v 1.55 2008/04/28 20:23:37 martin Exp $"); 43 44 #include <sys/param.h> 45 #include <sys/proc.h> 46 #include <sys/systm.h> 47 #include <sys/ioctl.h> 48 #include <sys/tty.h> 49 #include <sys/file.h> 50 #include <sys/conf.h> 51 #include <sys/device.h> 52 #include <sys/kauth.h> 53 54 #include <machine/autoconf.h> 55 #include <machine/cpu.h> 56 #include <machine/mon.h> 57 #include <machine/psl.h> 58 59 #include <dev/cons.h> 60 #include <dev/sun/event_var.h> 61 #include <dev/sun/kbd_xlate.h> 62 #include <dev/sun/kbdvar.h> 63 #include <sun3/dev/zs_cons.h> 64 65 #include "fb.h" 66 67 #define PUT_WSIZE 64 68 69 struct kd_softc { 70 struct device kd_dev; /* required first: base device */ 71 struct tty *kd_tty; 72 73 /* Console input hook */ 74 struct cons_channel *kd_in; 75 }; 76 77 /* 78 * There is no point in pretending there might be 79 * more than one keyboard/display device. 80 */ 81 static struct kd_softc kd_softc; 82 static int kd_is_console; 83 84 static int kdparam(struct tty *, struct termios *); 85 static void kdstart(struct tty *); 86 static void kd_init(struct kd_softc *); 87 static void kd_cons_input(int); 88 static void kd_later(void *); 89 90 dev_type_open(kdopen); 91 dev_type_close(kdclose); 92 dev_type_read(kdread); 93 dev_type_write(kdwrite); 94 dev_type_ioctl(kdioctl); 95 dev_type_tty(kdtty); 96 dev_type_poll(kdpoll); 97 98 const struct cdevsw kd_cdevsw = { 99 kdopen, kdclose, kdread, kdwrite, kdioctl, 100 nostop, kdtty, kdpoll, nommap, ttykqfilter, D_TTY 101 }; 102 103 /* 104 * Prepare the console tty; called on first open of /dev/console 105 */ 106 void 107 kd_init(struct kd_softc *kd) 108 { 109 struct tty *tp; 110 111 tp = ttymalloc(); 112 callout_setfunc(&tp->t_rstrt_ch, kd_later, tp); 113 114 tp->t_oproc = kdstart; 115 tp->t_param = kdparam; 116 tp->t_dev = makedev(cdevsw_lookup_major(&kd_cdevsw), 0); 117 118 tty_attach(tp); 119 kd->kd_tty = tp; 120 121 return; 122 } 123 124 struct tty * 125 kdtty(dev_t dev) 126 { 127 struct kd_softc *kd; 128 129 kd = &kd_softc; /* XXX */ 130 return (kd->kd_tty); 131 } 132 133 int 134 kdopen(dev_t dev, int flag, int mode, struct lwp *l) 135 { 136 struct kd_softc *kd; 137 int error, s, unit; 138 struct tty *tp; 139 static int firstopen = 1; 140 141 unit = minor(dev); 142 if (unit != 0) 143 return ENXIO; 144 kd = &kd_softc; /* XXX */ 145 if (firstopen) { 146 kd_init(kd); 147 firstopen = 0; 148 } 149 tp = kd->kd_tty; 150 151 /* It's simpler to do this up here. */ 152 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) 153 return (EBUSY); 154 155 s = spltty(); 156 157 if ((tp->t_state & TS_ISOPEN) == 0) { 158 /* First open. */ 159 160 /* Notify the input device that serves us */ 161 struct cons_channel *cc = kd->kd_in; 162 if (cc != NULL && 163 (error = (*cc->cc_iopen)(cc)) != 0) { 164 return (error); 165 } 166 167 ttychars(tp); 168 tp->t_iflag = TTYDEF_IFLAG; 169 tp->t_oflag = TTYDEF_OFLAG; 170 tp->t_cflag = TTYDEF_CFLAG; 171 tp->t_lflag = TTYDEF_LFLAG; 172 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 173 (void) kdparam(tp, &tp->t_termios); 174 ttsetwater(tp); 175 /* Flush pending input? Clear translator? */ 176 /* This (pseudo)device always has SOFTCAR */ 177 tp->t_state |= TS_CARR_ON; 178 } 179 180 splx(s); 181 182 return ((*tp->t_linesw->l_open)(dev, tp)); 183 } 184 185 int 186 kdclose(dev_t dev, int flag, int mode, struct lwp *l) 187 { 188 struct kd_softc *kd; 189 struct tty *tp; 190 struct cons_channel *cc; 191 192 kd = &kd_softc; /* XXX */ 193 tp = kd->kd_tty; 194 195 /* XXX This is for cons.c. */ 196 if ((tp->t_state & TS_ISOPEN) == 0) 197 return 0; 198 199 (*tp->t_linesw->l_close)(tp, flag); 200 ttyclose(tp); 201 if ((cc = kd->kd_in) != NULL) 202 (void)(*cc->cc_iclose)(cc); 203 return (0); 204 } 205 206 int 207 kdread(dev_t dev, struct uio *uio, int flag) 208 { 209 struct kd_softc *kd; 210 struct tty *tp; 211 212 kd = &kd_softc; /* XXX */ 213 tp = kd->kd_tty; 214 215 return ((*tp->t_linesw->l_read)(tp, uio, flag)); 216 } 217 218 int 219 kdwrite(dev_t dev, struct uio *uio, int flag) 220 { 221 struct kd_softc *kd; 222 struct tty *tp; 223 224 kd = &kd_softc; /* XXX */ 225 tp = kd->kd_tty; 226 227 return ((*tp->t_linesw->l_write)(tp, uio, flag)); 228 } 229 230 int 231 kdpoll(dev_t dev, int events, struct lwp *l) 232 { 233 struct kd_softc *kd; 234 struct tty *tp; 235 236 kd = &kd_softc; /* XXX */ 237 tp = kd->kd_tty; 238 239 return ((*tp->t_linesw->l_poll)(tp, events, l)); 240 } 241 242 int 243 kdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 244 { 245 struct kd_softc *kd; 246 struct tty *tp; 247 int error; 248 249 kd = &kd_softc; /* XXX */ 250 tp = kd->kd_tty; 251 252 error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l); 253 if (error != EPASSTHROUGH) 254 return error; 255 256 error = ttioctl(tp, cmd, data, flag, l); 257 if (error != EPASSTHROUGH) 258 return error; 259 260 /* Handle any ioctl commands specific to kbd/display. */ 261 /* XXX - Send KB* ioctls to kbd module? */ 262 /* XXX - Send FB* ioctls to fb module? */ 263 264 return EPASSTHROUGH; 265 } 266 267 static int 268 kdparam(struct tty *tp, struct termios *t) 269 { 270 /* XXX - These are ignored... */ 271 tp->t_ispeed = t->c_ispeed; 272 tp->t_ospeed = t->c_ospeed; 273 tp->t_cflag = t->c_cflag; 274 return 0; 275 } 276 277 278 static void kd_putfb(struct tty *); 279 280 static void 281 kdstart(struct tty *tp) 282 { 283 struct clist *cl; 284 int s1, s2; 285 286 s1 = splsoftclock(); 287 s2 = spltty(); 288 if (tp->t_state & (TS_BUSY|TS_TTSTOP|TS_TIMEOUT)) 289 goto out; 290 291 cl = &tp->t_outq; 292 if (ttypull(tp)) { 293 if (kd_is_console) { 294 tp->t_state |= TS_BUSY; 295 if ((s1 & PSL_IPL) == 0) { 296 /* called at level zero - update screen now. */ 297 splx(s2); 298 kd_putfb(tp); 299 s2 = spltty(); 300 tp->t_state &= ~TS_BUSY; 301 } else { 302 /* called at interrupt level - do it later */ 303 callout_schedule(&tp->t_rstrt_ch, 0); 304 } 305 } else { 306 /* 307 * This driver uses the PROM for writing the screen, 308 * and that only works if this is the console device. 309 * If this is not the console, just flush the output. 310 * Sorry. (In that case, use xdm instead of getty.) 311 */ 312 ndflush(cl, cl->c_cc); 313 } 314 } 315 out: 316 splx(s2); 317 splx(s1); 318 } 319 320 /* 321 * Timeout function to do delayed writes to the screen. 322 * Called at splsoftclock when requested by kdstart. 323 */ 324 static void 325 kd_later(void *tpaddr) 326 { 327 struct tty *tp = tpaddr; 328 int s; 329 330 kd_putfb(tp); 331 332 s = spltty(); 333 tp->t_state &= ~TS_BUSY; 334 (*tp->t_linesw->l_start)(tp); 335 splx(s); 336 } 337 338 /* 339 * Put text on the screen using the PROM monitor. 340 * This can take a while, so to avoid missing 341 * interrupts, this is called at splsoftclock. 342 */ 343 static void 344 kd_putfb(struct tty *tp) 345 { 346 char buf[PUT_WSIZE]; 347 struct clist *cl = &tp->t_outq; 348 char *p, *end; 349 int len; 350 351 while ((len = q_to_b(cl, buf, PUT_WSIZE-1)) > 0) { 352 /* PROM will barf if high bits are set. */ 353 p = buf; 354 end = buf + len; 355 while (p < end) 356 *p++ &= 0x7f; 357 (romVectorPtr->fbWriteStr)(buf, len); 358 } 359 } 360 361 void 362 cons_attach_input(struct cons_channel *cc, struct consdev *cn) 363 { 364 struct kd_softc *kd = &kd_softc; 365 366 kd->kd_in = cc; 367 cc->cc_upstream = kd_cons_input; 368 } 369 370 /* 371 * Default PROM-based console input stream 372 */ 373 static int kd_rom_iopen(struct cons_channel *); 374 static int kd_rom_iclose(struct cons_channel *); 375 376 static struct cons_channel prom_cons_channel; 377 378 int 379 kd_rom_iopen(struct cons_channel *cc) 380 { 381 /* No-op */ 382 return (0); 383 } 384 385 int 386 kd_rom_iclose(struct cons_channel *cc) 387 { 388 /* No-op */ 389 return (0); 390 } 391 392 /* 393 * Our "interrupt" routine for input. This is called by 394 * the keyboard driver (dev/sun/kbd.c) at spltty. 395 */ 396 void 397 kd_cons_input(int c) 398 { 399 struct kd_softc *kd = &kd_softc; 400 struct tty *tp; 401 402 /* XXX: Make sure the device is open. */ 403 tp = kd->kd_tty; 404 if (tp == NULL) 405 return; 406 if ((tp->t_state & TS_ISOPEN) == 0) 407 return; 408 409 (*tp->t_linesw->l_rint)(c, tp); 410 } 411 412 413 /**************************************************************** 414 * kd console support 415 ****************************************************************/ 416 417 /* The debugger gets its own key translation state. */ 418 static struct kbd_state kdcn_state; 419 420 static void kdcnprobe(struct consdev *); 421 static void kdcninit(struct consdev *); 422 static int kdcngetc(dev_t); 423 static void kdcnputc(dev_t, int); 424 static void kdcnpollc(dev_t, int); 425 426 struct consdev consdev_kd = { 427 kdcnprobe, 428 kdcninit, 429 kdcngetc, 430 kdcnputc, 431 kdcnpollc, 432 NULL, 433 }; 434 435 /* We never call this. */ 436 static void 437 kdcnprobe(struct consdev *cn) 438 { 439 } 440 441 static void 442 kdcninit(struct consdev *cn) 443 { 444 struct kbd_state *ks = &kdcn_state; 445 446 cn->cn_dev = makedev(cdevsw_lookup_major(&kd_cdevsw), 0); 447 cn->cn_pri = CN_INTERNAL; 448 449 /* This prepares kbd_translate() */ 450 ks->kbd_id = KBD_MIN_TYPE; 451 kbd_xlate_init(ks); 452 453 /* Set up initial PROM input channel for /dev/console */ 454 prom_cons_channel.cc_private = NULL; 455 prom_cons_channel.cc_iopen = kd_rom_iopen; 456 prom_cons_channel.cc_iclose = kd_rom_iclose; 457 cons_attach_input(&prom_cons_channel, cn); 458 459 /* Indicate that it is OK to use the PROM fbwrite */ 460 kd_is_console = 1; 461 } 462 463 static int 464 kdcngetc(dev_t dev) 465 { 466 struct kbd_state *ks = &kdcn_state; 467 int code, class, data, keysym; 468 469 for (;;) { 470 code = zs_getc(zs_conschan); 471 keysym = kbd_code_to_keysym(ks, code); 472 class = KEYSYM_CLASS(keysym); 473 474 switch (class) { 475 case KEYSYM_ASCII: 476 goto out; 477 478 case KEYSYM_CLRMOD: 479 case KEYSYM_SETMOD: 480 data = (keysym & 0x1F); 481 /* Only allow ctrl or shift. */ 482 if (data > KBMOD_SHIFT_R) 483 break; 484 data = 1 << data; 485 if (class == KEYSYM_SETMOD) 486 ks->kbd_modbits |= data; 487 else 488 ks->kbd_modbits &= ~data; 489 break; 490 491 case KEYSYM_ALL_UP: 492 /* No toggle keys here. */ 493 ks->kbd_modbits = 0; 494 break; 495 496 default: /* ignore all other keysyms */ 497 break; 498 } 499 } 500 out: 501 return (keysym); 502 } 503 504 static void 505 kdcnputc(dev_t dev, int c) 506 { 507 (romVectorPtr->fbWriteChar)(c & 0x7f); 508 } 509 510 static void 511 kdcnpollc(dev_t dev, int on) 512 { 513 struct kbd_state *ks = &kdcn_state; 514 515 if (on) { 516 /* Entering debugger. */ 517 #if NFB > 0 518 fb_unblank(); 519 #endif 520 /* Clear shift keys too. */ 521 ks->kbd_modbits = 0; 522 } else { 523 /* Resuming kernel. */ 524 } 525 } 526 527