1 /* $NetBSD: kbdsun.c,v 1.3 2003/08/07 16:31:25 agc 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.3 2003/08/07 16:31:25 agc 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, 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_was_reset(). */ 138 error = tsleep((caddr_t)&ks->kbd_id, PZERO | PCATCH, devopn, hz); 139 if (error == EWOULDBLOCK) { /* no response */ 140 log(LOG_ERR, "%s: reset failed\n", kbd->k_dev.dv_xname); 141 142 /* 143 * Allow the open anyway (to keep getty happy) 144 * but assume the "least common denominator". 145 */ 146 error = 0; 147 ks->kbd_id = KB_SUN2; 148 } 149 150 /* earlier than type 4 does not know "layout" */ 151 if (ks->kbd_id >= KB_SUN4) { 152 /* ask for the layout */ 153 kbd_sun_output(k, KBD_CMD_GETLAYOUT); 154 kbd_sun_start_tx(k); 155 kbd_sun_drain_tx(k); 156 157 /* the wakeup for this is in kbd_new_layout() */ 158 error = tsleep((caddr_t)&ks->kbd_layout, PZERO | PCATCH, devopn, hz); 159 if (error == EWOULDBLOCK) { /* no response */ 160 log(LOG_ERR, "%s: no response to get_layout\n", 161 kbd->k_dev.dv_xname); 162 error = 0; 163 ks->kbd_layout = 0; /* US layout */ 164 } 165 } 166 167 /* initialize the table pointers for this type/layout */ 168 kbd_xlate_init(ks); 169 170 splx(s); 171 172 if (error == 0) 173 k->k_isopen = 1; 174 return (error); 175 } 176 177 178 static int 179 kbd_sun_close(kbd) 180 struct kbd_softc *kbd; 181 { 182 183 return (0); /* nothing to do so far */ 184 } 185 186 187 /* 188 * keyboard command ioctl 189 * ``unimplemented commands are ignored'' (blech) 190 * XXX: This is also exported to the fb driver (for bell). 191 */ 192 static int 193 kbd_sun_do_cmd(kbd, cmd, isioctl) 194 struct kbd_softc *kbd; 195 int cmd; 196 int isioctl; 197 { 198 struct kbd_sun_softc *k = (struct kbd_sun_softc *)kbd; 199 struct kbd_state *ks; 200 int error, s; 201 202 error = 0; 203 ks = &kbd->k_state; 204 205 switch (cmd) { 206 207 case KBD_CMD_BELL: 208 case KBD_CMD_NOBELL: 209 /* Supported by type 2, 3, and 4 keyboards */ 210 break; 211 212 case KBD_CMD_CLICK: 213 case KBD_CMD_NOCLICK: 214 /* Unsupported by type 2 keyboards */ 215 if (ks->kbd_id <= KB_SUN2) 216 return (0); 217 ks->kbd_click = (cmd == KBD_CMD_CLICK); 218 break; 219 220 default: 221 return (0); 222 } 223 224 s = spltty(); 225 226 if (isioctl) 227 error = kbd_sun_drain_tx(k); 228 229 if (error == 0) { 230 kbd_sun_output(k, cmd); 231 kbd_sun_start_tx(k); 232 } 233 234 splx(s); 235 236 return (error); 237 } 238 239 240 /* 241 * KIOCSLED. Has user context. 242 * Take care about spl and call kbd_sun_set_leds. 243 */ 244 static int 245 kbd_sun_set_leds(kbd, leds, isioctl) 246 struct kbd_softc *kbd; 247 int leds; 248 int isioctl; 249 { 250 struct kbd_sun_softc *k = (struct kbd_sun_softc *)kbd; 251 252 if (isioctl) { 253 int error, s; 254 s = spltty(); 255 error = kbd_sun_drain_tx(k); 256 if (error == 0) { 257 kbd_sun_set_leds1(kbd, leds); 258 } 259 splx(s); 260 return (error); 261 } 262 else { 263 kbd_sun_set_leds1(kbd, leds); 264 return (0); 265 } 266 } 267 268 269 /* 270 * Safe to call from intterupt handler. Called at spltty() 271 * by kbd_sun_iocsled and kbd_sun_input (via kbd_update_leds). 272 */ 273 static void 274 kbd_sun_set_leds1(kbd, new_leds) 275 struct kbd_softc *kbd; 276 int new_leds; 277 { 278 struct kbd_sun_softc *k = (struct kbd_sun_softc *)kbd; 279 struct kbd_state *ks = &kbd->k_state; 280 281 /* Don't send unless state changes. */ 282 if (ks->kbd_leds == new_leds) 283 return; 284 285 ks->kbd_leds = new_leds; 286 287 /* Only type 4 and later has LEDs anyway. */ 288 if (ks->kbd_id < KB_SUN4) 289 return; 290 291 kbd_sun_output(k, KBD_CMD_SETLED); 292 kbd_sun_output(k, new_leds); 293 kbd_sun_start_tx(k); 294 } 295 296 297 298 /*********************************************************************** 299 * Methods for lower layer to call and related functions. 300 */ 301 302 /* 303 * Enqueue some output for the keyboard 304 * Called at spltty(). 305 */ 306 void 307 kbd_sun_output(k, c) 308 struct kbd_sun_softc *k; 309 int c; /* the data */ 310 { 311 int put; 312 313 put = k->k_tbput; 314 k->k_tbuf[put] = (u_char)c; 315 put = (put + 1) & KBD_TX_RING_MASK; 316 317 /* Would overrun if increment makes (put == get) */ 318 if (put == k->k_tbget) { 319 log(LOG_WARNING, "%s: output overrun\n", 320 k->k_kbd.k_dev.dv_xname); 321 } else { 322 /* OK, really increment. */ 323 k->k_tbput = put; 324 } 325 } 326 327 328 /* 329 * In user context. Called at spltty(). 330 * Wait for output to keyboard to finish. 331 */ 332 static int 333 kbd_sun_drain_tx(k) 334 struct kbd_sun_softc *k; 335 { 336 int error = 0; 337 338 while (k->k_txflags & K_TXBUSY && !error) { 339 k->k_txflags |= K_TXWANT; 340 error = tsleep((caddr_t)&k->k_txflags, 341 PZERO | PCATCH, "kbdout", 0); 342 } 343 344 return (error); 345 } 346 347 /* 348 * Start the sending data from the output queue 349 * Called at spltty(). 350 */ 351 void 352 kbd_sun_start_tx(k) 353 struct kbd_sun_softc *k; 354 { 355 int get; 356 u_char c; 357 358 if (k->k_txflags & K_TXBUSY) 359 return; 360 361 /* Is there anything to send? */ 362 get = k->k_tbget; 363 if (get == k->k_tbput) { 364 /* Nothing to send. Wake drain waiters. */ 365 if (k->k_txflags & K_TXWANT) { 366 k->k_txflags &= ~K_TXWANT; 367 wakeup((caddr_t)&k->k_txflags); 368 } 369 return; 370 } 371 372 /* Have something to send. */ 373 c = k->k_tbuf[get]; 374 get = (get + 1) & KBD_TX_RING_MASK; 375 k->k_tbget = get; 376 k->k_txflags |= K_TXBUSY; 377 378 /* Pass data down to the underlying device. */ 379 (*k->k_write_data)(k, c); 380 } 381 382 383 /* 384 * Called by underlying driver's softint() routine on input, 385 * which passes us the raw hardware make/break codes. 386 * Called at spltty() 387 */ 388 void 389 kbd_sun_input(k, code) 390 struct kbd_sun_softc *k; 391 int code; 392 { 393 struct kbd_softc *kbd = (struct kbd_softc *)k; 394 395 /* XXX - Input errors already handled. */ 396 397 /* Are we expecting special input? */ 398 if (k->k_expect) { 399 if (k->k_expect & KBD_EXPECT_IDCODE) { 400 /* We read a KBD_RESET last time. */ 401 kbd->k_state.kbd_id = code; 402 kbd_sun_was_reset(k); 403 } 404 if (k->k_expect & KBD_EXPECT_LAYOUT) { 405 /* We read a KBD_LAYOUT last time. */ 406 kbd->k_state.kbd_layout = code; 407 kbd_sun_new_layout(k); 408 } 409 k->k_expect = 0; 410 return; 411 } 412 413 /* Is this one of the "special" input codes? */ 414 if (KBD_SPECIAL(code)) { 415 switch (code) { 416 case KBD_RESET: 417 k->k_expect |= KBD_EXPECT_IDCODE; 418 /* Fake an "all-up" to resync. translation. */ 419 code = KBD_IDLE; 420 break; 421 422 case KBD_LAYOUT: 423 k->k_expect |= KBD_EXPECT_LAYOUT; 424 return; 425 426 case KBD_ERROR: 427 log(LOG_WARNING, "%s: received error indicator\n", 428 kbd->k_dev.dv_xname); 429 return; 430 431 case KBD_IDLE: 432 /* Let this go to the translator. */ 433 break; 434 } 435 } 436 437 kbd_input(kbd, code); 438 } 439 440 441 /* 442 * Called by kbd_sun_input to handle keyboard's response to reset. 443 * Called at spltty(). 444 */ 445 static void 446 kbd_sun_was_reset(k) 447 struct kbd_sun_softc *k; 448 { 449 struct kbd_state *ks = &k->k_kbd.k_state; 450 451 /* 452 * On first identification, wake up anyone waiting for type 453 * and set up the table pointers. 454 */ 455 wakeup((caddr_t)&ks->kbd_id); 456 457 /* Restore keyclick, if necessary */ 458 switch (ks->kbd_id) { 459 460 case KB_SUN2: 461 /* Type 2 keyboards don't support keyclick */ 462 break; 463 464 case KB_SUN3: 465 /* Type 3 keyboards come up with keyclick on */ 466 if (!ks->kbd_click) { 467 /* turn off the click */ 468 kbd_sun_output(k, KBD_CMD_NOCLICK); 469 kbd_sun_start_tx(k); 470 } 471 break; 472 473 case KB_SUN4: 474 /* Type 4 keyboards come up with keyclick off */ 475 if (ks->kbd_click) { 476 /* turn on the click */ 477 kbd_sun_output(k, KBD_CMD_CLICK); 478 kbd_sun_start_tx(k); 479 } 480 break; 481 } 482 483 /* LEDs are off after reset. */ 484 ks->kbd_leds = 0; 485 } 486 487 /* 488 * Called by kbd_sun_input to handle response to layout request. 489 * Called at spltty(). 490 */ 491 static void 492 kbd_sun_new_layout(k) 493 struct kbd_sun_softc *k; 494 { 495 struct kbd_state *ks = &k->k_kbd.k_state; 496 497 /* 498 * On first identification, wake up anyone waiting for type 499 * and set up the table pointers. 500 */ 501 wakeup((caddr_t)&ks->kbd_layout); 502 503 /* XXX: switch decoding tables? */ 504 } 505