1 /* $NetBSD: kbdsun.c,v 1.6 2005/02/21 03:46:38 heas 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.6 2005/02/21 03:46:38 heas 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 = 200; ntries; ntries--) { 139 error = tsleep((caddr_t)&ks->kbd_id, PZERO | PCATCH, devopn, 140 hz); 141 if (ks->kbd_id) 142 break; 143 DELAY(10000); 144 } 145 146 if (error == EWOULDBLOCK || ks->kbd_id == 0) { /* no response */ 147 log(LOG_ERR, "%s: reset failed\n", kbd->k_dev.dv_xname); 148 149 /* 150 * Allow the open anyway (to keep getty happy) 151 * but assume the "least common denominator". 152 */ 153 error = 0; 154 ks->kbd_id = KB_SUN2; 155 } 156 157 /* earlier than type 4 does not know "layout" */ 158 if (ks->kbd_id >= KB_SUN4) { 159 ks->kbd_layout = 0xff; 160 161 /* ask for the layout */ 162 kbd_sun_output(k, KBD_CMD_GETLAYOUT); 163 kbd_sun_start_tx(k); 164 kbd_sun_drain_tx(k); 165 166 /* the wakeup for this is in kbd_sun_new_layout() */ 167 for (ntries = 200; ntries; ntries--) { 168 error = tsleep((caddr_t)&ks->kbd_layout, PZERO | PCATCH, 169 devopn, hz); 170 if (ks->kbd_layout != 0xff || error) 171 break; 172 DELAY(10000); 173 } 174 if (error == EWOULDBLOCK || ks->kbd_layout == 0xff) { 175 log(LOG_ERR, "%s: no response to get_layout\n", 176 kbd->k_dev.dv_xname); 177 error = 0; 178 ks->kbd_layout = 0; /* US layout */ 179 } 180 } 181 182 /* initialize the table pointers for this type/layout */ 183 kbd_xlate_init(ks); 184 185 splx(s); 186 187 if (error == 0) 188 k->k_isopen = 1; 189 return (error); 190 } 191 192 193 static int 194 kbd_sun_close(kbd) 195 struct kbd_softc *kbd; 196 { 197 198 return (0); /* nothing to do so far */ 199 } 200 201 202 /* 203 * keyboard command ioctl 204 * ``unimplemented commands are ignored'' (blech) 205 * XXX: This is also exported to the fb driver (for bell). 206 */ 207 static int 208 kbd_sun_do_cmd(kbd, cmd, isioctl) 209 struct kbd_softc *kbd; 210 int cmd; 211 int isioctl; 212 { 213 struct kbd_sun_softc *k = (struct kbd_sun_softc *)kbd; 214 struct kbd_state *ks; 215 int error, s; 216 217 error = 0; 218 ks = &kbd->k_state; 219 220 switch (cmd) { 221 222 case KBD_CMD_BELL: 223 case KBD_CMD_NOBELL: 224 /* Supported by type 2, 3, and 4 keyboards */ 225 break; 226 227 case KBD_CMD_CLICK: 228 case KBD_CMD_NOCLICK: 229 /* Unsupported by type 2 keyboards */ 230 if (ks->kbd_id <= KB_SUN2) 231 return (0); 232 ks->kbd_click = (cmd == KBD_CMD_CLICK); 233 break; 234 235 default: 236 return (0); 237 } 238 239 s = spltty(); 240 241 if (isioctl) 242 error = kbd_sun_drain_tx(k); 243 244 if (error == 0) { 245 kbd_sun_output(k, cmd); 246 kbd_sun_start_tx(k); 247 } 248 249 splx(s); 250 251 return (error); 252 } 253 254 255 /* 256 * KIOCSLED. Has user context. 257 * Take care about spl and call kbd_sun_set_leds. 258 */ 259 static int 260 kbd_sun_set_leds(kbd, leds, isioctl) 261 struct kbd_softc *kbd; 262 int leds; 263 int isioctl; 264 { 265 struct kbd_sun_softc *k = (struct kbd_sun_softc *)kbd; 266 267 if (isioctl) { 268 int error, s; 269 s = spltty(); 270 error = kbd_sun_drain_tx(k); 271 if (error == 0) { 272 kbd_sun_set_leds1(kbd, leds); 273 } 274 splx(s); 275 return (error); 276 } 277 else { 278 kbd_sun_set_leds1(kbd, leds); 279 return (0); 280 } 281 } 282 283 284 /* 285 * Safe to call from intterupt handler. Called at spltty() 286 * by kbd_sun_iocsled and kbd_sun_input (via kbd_update_leds). 287 */ 288 static void 289 kbd_sun_set_leds1(kbd, new_leds) 290 struct kbd_softc *kbd; 291 int new_leds; 292 { 293 struct kbd_sun_softc *k = (struct kbd_sun_softc *)kbd; 294 struct kbd_state *ks = &kbd->k_state; 295 296 /* Don't send unless state changes. */ 297 if (ks->kbd_leds == new_leds) 298 return; 299 300 ks->kbd_leds = new_leds; 301 302 /* Only type 4 and later has LEDs anyway. */ 303 if (ks->kbd_id < KB_SUN4) 304 return; 305 306 kbd_sun_output(k, KBD_CMD_SETLED); 307 kbd_sun_output(k, new_leds); 308 kbd_sun_start_tx(k); 309 } 310 311 312 313 /*********************************************************************** 314 * Methods for lower layer to call and related functions. 315 */ 316 317 /* 318 * Enqueue some output for the keyboard 319 * Called at spltty(). 320 */ 321 void 322 kbd_sun_output(k, c) 323 struct kbd_sun_softc *k; 324 int c; /* the data */ 325 { 326 int put; 327 328 put = k->k_tbput; 329 k->k_tbuf[put] = (u_char)c; 330 put = (put + 1) & KBD_TX_RING_MASK; 331 332 /* Would overrun if increment makes (put == get) */ 333 if (put == k->k_tbget) { 334 log(LOG_WARNING, "%s: output overrun\n", 335 k->k_kbd.k_dev.dv_xname); 336 } else { 337 /* OK, really increment. */ 338 k->k_tbput = put; 339 } 340 } 341 342 343 /* 344 * In user context. Called at spltty(). 345 * Wait for output to keyboard to finish. 346 */ 347 static int 348 kbd_sun_drain_tx(k) 349 struct kbd_sun_softc *k; 350 { 351 int error = 0; 352 353 while (k->k_txflags & K_TXBUSY && !error) { 354 k->k_txflags |= K_TXWANT; 355 error = tsleep((caddr_t)&k->k_txflags, 356 PZERO | PCATCH, "kbdout", 0); 357 } 358 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((caddr_t)&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