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