1 /* $NetBSD: kbdsun.c,v 1.9 2005/12/11 12:23:56 christos Exp $ */ 2 /* NetBSD: kbd.c,v 1.29 2001/11/13 06:54:32 lukem Exp */ 3 4 /* 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This software was developed by the Computer Systems Engineering group 9 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 10 * contributed to Berkeley. 11 * 12 * All advertising materials mentioning features or use of this software 13 * must display the following acknowledgement: 14 * This product includes software developed by the University of 15 * California, Lawrence Berkeley Laboratory. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions 19 * are met: 20 * 1. Redistributions of source code must retain the above copyright 21 * notice, this list of conditions and the following disclaimer. 22 * 2. Redistributions in binary form must reproduce the above copyright 23 * notice, this list of conditions and the following disclaimer in the 24 * documentation and/or other materials provided with the distribution. 25 * 3. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * @(#)kbd.c 8.2 (Berkeley) 10/30/93 42 */ 43 44 /* 45 * /dev/kbd middle layer for sun keyboard off a serial line 46 * This code is used by kbd_zs and sunkbd drivers (lower layer). 47 */ 48 49 #include <sys/cdefs.h> 50 __KERNEL_RCSID(0, "$NetBSD: kbdsun.c,v 1.9 2005/12/11 12:23:56 christos Exp $"); 51 52 #include <sys/param.h> 53 #include <sys/systm.h> 54 #include <sys/conf.h> 55 #include <sys/device.h> 56 #include <sys/ioctl.h> 57 #include <sys/kernel.h> 58 #include <sys/proc.h> 59 #include <sys/signal.h> 60 #include <sys/signalvar.h> 61 #include <sys/time.h> 62 #include <sys/syslog.h> 63 #include <sys/select.h> 64 #include <sys/poll.h> 65 #include <sys/file.h> 66 67 #include <dev/sun/kbd_reg.h> 68 #include <dev/sun/kbio.h> 69 #include <dev/sun/vuid_event.h> 70 #include <dev/sun/event_var.h> 71 #include <dev/sun/kbd_xlate.h> 72 73 #include <dev/sun/kbdvar.h> 74 #include <dev/sun/kbdsunvar.h> 75 76 77 /* callbacks for the upper /dev/kbd layer */ 78 static int kbd_sun_open(struct kbd_softc *); 79 static int kbd_sun_close(struct kbd_softc *); 80 static int kbd_sun_do_cmd(struct kbd_softc *, int, int); 81 static int kbd_sun_set_leds(struct kbd_softc *, int, int); 82 83 static void kbd_sun_set_leds1(struct kbd_softc *, int); /* aux */ 84 85 const struct kbd_ops kbd_ops_sun = { 86 kbd_sun_open, 87 kbd_sun_close, 88 kbd_sun_do_cmd, 89 kbd_sun_set_leds 90 }; 91 92 /* in user context, wait for keyboard output to finish */ 93 static int kbd_sun_drain_tx(struct kbd_sun_softc *); 94 95 /* helper functions for kbd_sun_input */ 96 static void kbd_sun_was_reset(struct kbd_sun_softc *); 97 static void kbd_sun_new_layout(struct kbd_sun_softc *); 98 99 100 /*********************************************************************** 101 * Callbacks for upper layer. 102 */ 103 104 /* 105 * Initialization to be done at first open. 106 * This is called from kbdopen() or kd_cc_open() 107 * Called with user context. 108 */ 109 static int 110 kbd_sun_open(kbd) 111 struct kbd_softc *kbd; 112 { 113 struct kbd_sun_softc *k = (struct kbd_sun_softc *)kbd; 114 struct kbd_state *ks; 115 int error, ntries, s; 116 117 if (kbd == NULL) 118 return (ENXIO); 119 120 ks = &kbd->k_state; 121 122 /* tolerate extra calls. */ 123 if (k->k_isopen) 124 return (0); 125 126 /* open internal device */ 127 if (k->k_deviopen) 128 (*k->k_deviopen)((struct device *)k, FREAD|FWRITE); 129 130 s = spltty(); 131 132 /* reset the keyboard and find out its type */ 133 kbd_sun_output(k, KBD_CMD_RESET); 134 kbd_sun_start_tx(k); 135 kbd_sun_drain_tx(k); 136 137 /* the wakeup for this is in kbd_sun_was_reset(). */ 138 for (ntries = 30; ntries; ntries--) { 139 error = tsleep((caddr_t)&ks->kbd_id, PZERO | PCATCH, devopn, 140 hz/10); 141 if (ks->kbd_id) 142 break; 143 } 144 145 if (error == EWOULDBLOCK || ks->kbd_id == 0) { /* no response */ 146 log(LOG_ERR, "%s: reset failed\n", kbd->k_dev.dv_xname); 147 148 /* 149 * Allow the open anyway (to keep getty happy) 150 * but assume the "least common denominator". 151 */ 152 error = 0; 153 ks->kbd_id = KB_SUN2; 154 } 155 156 /* earlier than type 4 does not know "layout" */ 157 if (ks->kbd_id >= KB_SUN4) { 158 ks->kbd_layout = 0xff; 159 160 /* ask for the layout */ 161 kbd_sun_output(k, KBD_CMD_GETLAYOUT); 162 kbd_sun_start_tx(k); 163 kbd_sun_drain_tx(k); 164 165 /* the wakeup for this is in kbd_sun_new_layout() */ 166 for (ntries = 200; ntries; ntries--) { 167 error = tsleep((caddr_t)&ks->kbd_layout, PZERO | PCATCH, 168 devopn, hz); 169 if (ks->kbd_layout != 0xff || error) 170 break; 171 DELAY(10000); 172 } 173 if (error == EWOULDBLOCK || ks->kbd_layout == 0xff) { 174 log(LOG_ERR, "%s: no response to get_layout\n", 175 kbd->k_dev.dv_xname); 176 error = 0; 177 ks->kbd_layout = 0; /* US layout */ 178 } 179 } 180 181 /* initialize the table pointers for this type/layout */ 182 kbd_xlate_init(ks); 183 184 splx(s); 185 186 if (error == 0) 187 k->k_isopen = 1; 188 return (error); 189 } 190 191 192 static int 193 kbd_sun_close(kbd) 194 struct kbd_softc *kbd; 195 { 196 197 return (0); /* nothing to do so far */ 198 } 199 200 201 /* 202 * keyboard command ioctl 203 * ``unimplemented commands are ignored'' (blech) 204 * XXX: This is also exported to the fb driver (for bell). 205 */ 206 static int 207 kbd_sun_do_cmd(kbd, cmd, isioctl) 208 struct kbd_softc *kbd; 209 int cmd; 210 int isioctl; 211 { 212 struct kbd_sun_softc *k = (struct kbd_sun_softc *)kbd; 213 struct kbd_state *ks; 214 int error, s; 215 216 error = 0; 217 ks = &kbd->k_state; 218 219 switch (cmd) { 220 221 case KBD_CMD_BELL: 222 case KBD_CMD_NOBELL: 223 /* Supported by type 2, 3, and 4 keyboards */ 224 break; 225 226 case KBD_CMD_CLICK: 227 case KBD_CMD_NOCLICK: 228 /* Unsupported by type 2 keyboards */ 229 if (ks->kbd_id <= KB_SUN2) 230 return (0); 231 ks->kbd_click = (cmd == KBD_CMD_CLICK); 232 break; 233 234 default: 235 return (0); 236 } 237 238 s = spltty(); 239 240 if (isioctl) 241 error = kbd_sun_drain_tx(k); 242 243 if (error == 0) { 244 kbd_sun_output(k, cmd); 245 kbd_sun_start_tx(k); 246 } 247 248 splx(s); 249 250 return (error); 251 } 252 253 254 /* 255 * KIOCSLED. Has user context. 256 * Take care about spl and call kbd_sun_set_leds. 257 */ 258 static int 259 kbd_sun_set_leds(kbd, leds, isioctl) 260 struct kbd_softc *kbd; 261 int leds; 262 int isioctl; 263 { 264 struct kbd_sun_softc *k = (struct kbd_sun_softc *)kbd; 265 266 if (isioctl) { 267 int error, s; 268 s = spltty(); 269 error = kbd_sun_drain_tx(k); 270 if (error == 0) { 271 kbd_sun_set_leds1(kbd, leds); 272 } 273 splx(s); 274 return (error); 275 } 276 else { 277 kbd_sun_set_leds1(kbd, leds); 278 return (0); 279 } 280 } 281 282 283 /* 284 * Safe to call from intterupt handler. Called at spltty() 285 * by kbd_sun_iocsled and kbd_sun_input (via kbd_update_leds). 286 */ 287 static void 288 kbd_sun_set_leds1(kbd, new_leds) 289 struct kbd_softc *kbd; 290 int new_leds; 291 { 292 struct kbd_sun_softc *k = (struct kbd_sun_softc *)kbd; 293 struct kbd_state *ks = &kbd->k_state; 294 295 /* Don't send unless state changes. */ 296 if (ks->kbd_leds == new_leds) 297 return; 298 299 ks->kbd_leds = new_leds; 300 301 /* Only type 4 and later has LEDs anyway. */ 302 if (ks->kbd_id < KB_SUN4) 303 return; 304 305 kbd_sun_output(k, KBD_CMD_SETLED); 306 kbd_sun_output(k, new_leds); 307 kbd_sun_start_tx(k); 308 } 309 310 311 312 /*********************************************************************** 313 * Methods for lower layer to call and related functions. 314 */ 315 316 /* 317 * Enqueue some output for the keyboard 318 * Called at spltty(). 319 */ 320 void 321 kbd_sun_output(k, c) 322 struct kbd_sun_softc *k; 323 int c; /* the data */ 324 { 325 int put; 326 327 put = k->k_tbput; 328 k->k_tbuf[put] = (u_char)c; 329 put = (put + 1) & KBD_TX_RING_MASK; 330 331 /* Would overrun if increment makes (put == get) */ 332 if (put == k->k_tbget) { 333 log(LOG_WARNING, "%s: output overrun\n", 334 k->k_kbd.k_dev.dv_xname); 335 } else { 336 /* OK, really increment. */ 337 k->k_tbput = put; 338 } 339 } 340 341 342 /* 343 * In user context. Called at spltty(). 344 * Wait for output to keyboard to finish. 345 */ 346 static int 347 kbd_sun_drain_tx(k) 348 struct kbd_sun_softc *k; 349 { 350 int error = 0, bail = 0; 351 352 while ((k->k_txflags & K_TXBUSY) && (!error) && (bail<1000)) { 353 k->k_txflags |= K_TXWANT; 354 error = tsleep(&k->k_txflags, PZERO | PCATCH, "kbdout", 1); 355 bail++; 356 } 357 if (bail==1000) 358 error=EIO; 359 return (error); 360 } 361 362 /* 363 * Start the sending data from the output queue 364 * Called at spltty(). 365 */ 366 void 367 kbd_sun_start_tx(k) 368 struct kbd_sun_softc *k; 369 { 370 int get; 371 u_char c; 372 373 if (k->k_txflags & K_TXBUSY) 374 return; 375 376 /* Is there anything to send? */ 377 get = k->k_tbget; 378 if (get == k->k_tbput) { 379 /* Nothing to send. Wake drain waiters. */ 380 if (k->k_txflags & K_TXWANT) { 381 k->k_txflags &= ~K_TXWANT; 382 wakeup(&k->k_txflags); 383 } 384 return; 385 } 386 387 /* Have something to send. */ 388 c = k->k_tbuf[get]; 389 get = (get + 1) & KBD_TX_RING_MASK; 390 k->k_tbget = get; 391 k->k_txflags |= K_TXBUSY; 392 393 /* Pass data down to the underlying device. */ 394 (*k->k_write_data)(k, c); 395 } 396 397 398 /* 399 * Called by underlying driver's softint() routine on input, 400 * which passes us the raw hardware make/break codes. 401 * Called at spltty() 402 */ 403 int 404 kbd_sun_input(k, code) 405 struct kbd_sun_softc *k; 406 int code; 407 { 408 struct kbd_softc *kbd = (struct kbd_softc *)k; 409 410 /* XXX - Input errors already handled. */ 411 412 /* Are we expecting special input? */ 413 if (k->k_expect) { 414 if (k->k_expect & KBD_EXPECT_IDCODE) { 415 /* We read a KBD_RESET last time. */ 416 kbd->k_state.kbd_id = code; 417 kbd_sun_was_reset(k); 418 } 419 if (k->k_expect & KBD_EXPECT_LAYOUT) { 420 /* We read a KBD_LAYOUT last time. */ 421 kbd->k_state.kbd_layout = code; 422 kbd_sun_new_layout(k); 423 } 424 k->k_expect = 0; 425 return(0); 426 } 427 428 /* Is this one of the "special" input codes? */ 429 if (KBD_SPECIAL(code)) { 430 switch (code) { 431 case KBD_RESET: 432 k->k_expect |= KBD_EXPECT_IDCODE; 433 /* Fake an "all-up" to resync. translation. */ 434 code = KBD_IDLE; 435 break; 436 437 case KBD_LAYOUT: 438 k->k_expect |= KBD_EXPECT_LAYOUT; 439 return(0); 440 441 case KBD_ERROR: 442 log(LOG_WARNING, "%s: received error indicator\n", 443 kbd->k_dev.dv_xname); 444 return(-1); 445 446 case KBD_IDLE: 447 /* Let this go to the translator. */ 448 break; 449 } 450 } 451 452 kbd_input(kbd, code); 453 return(0); 454 } 455 456 457 /* 458 * Called by kbd_sun_input to handle keyboard's response to reset. 459 * Called at spltty(). 460 */ 461 static void 462 kbd_sun_was_reset(k) 463 struct kbd_sun_softc *k; 464 { 465 struct kbd_state *ks = &k->k_kbd.k_state; 466 467 /* 468 * On first identification, wake up anyone waiting for type 469 * and set up the table pointers. 470 */ 471 wakeup((caddr_t)&ks->kbd_id); 472 473 /* Restore keyclick, if necessary */ 474 switch (ks->kbd_id) { 475 476 case KB_SUN2: 477 /* Type 2 keyboards don't support keyclick */ 478 break; 479 480 case KB_SUN3: 481 /* Type 3 keyboards come up with keyclick on */ 482 if (!ks->kbd_click) { 483 /* turn off the click */ 484 kbd_sun_output(k, KBD_CMD_NOCLICK); 485 kbd_sun_start_tx(k); 486 } 487 break; 488 489 case KB_SUN4: 490 /* Type 4 keyboards come up with keyclick off */ 491 if (ks->kbd_click) { 492 /* turn on the click */ 493 kbd_sun_output(k, KBD_CMD_CLICK); 494 kbd_sun_start_tx(k); 495 } 496 break; 497 default: 498 printf("%s: unknown keyboard type ID %u\n", 499 k->k_kbd.k_dev.dv_xname, (unsigned int)ks->kbd_id); 500 } 501 502 /* LEDs are off after reset. */ 503 ks->kbd_leds = 0; 504 } 505 506 /* 507 * Called by kbd_sun_input to handle response to layout request. 508 * Called at spltty(). 509 */ 510 static void 511 kbd_sun_new_layout(k) 512 struct kbd_sun_softc *k; 513 { 514 struct kbd_state *ks = &k->k_kbd.k_state; 515 516 /* 517 * On first identification, wake up anyone waiting for type 518 * and set up the table pointers. 519 */ 520 wakeup((caddr_t)&ks->kbd_layout); 521 522 /* XXX: switch decoding tables? */ 523 } 524