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