| 1 | /* $NetBSD: pckbd.c,v 1.32 2015/07/16 15:01:04 prlw1 Exp $ */ |
| 2 | |
| 3 | /*- |
| 4 | * Copyright (c) 1998, 2009 The NetBSD Foundation, Inc. |
| 5 | * All rights reserved. |
| 6 | * |
| 7 | * This code is derived from software contributed to The NetBSD Foundation |
| 8 | * by Charles M. Hannum. |
| 9 | * |
| 10 | * Redistribution and use in source and binary forms, with or without |
| 11 | * modification, are permitted provided that the following conditions |
| 12 | * are met: |
| 13 | * 1. Redistributions of source code must retain the above copyright |
| 14 | * notice, this list of conditions and the following disclaimer. |
| 15 | * 2. Redistributions in binary form must reproduce the above copyright |
| 16 | * notice, this list of conditions and the following disclaimer in the |
| 17 | * documentation and/or other materials provided with the distribution. |
| 18 | * |
| 19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
| 20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| 21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
| 23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 29 | * POSSIBILITY OF SUCH DAMAGE. |
| 30 | */ |
| 31 | |
| 32 | /*- |
| 33 | * Copyright (c) 1990 The Regents of the University of California. |
| 34 | * All rights reserved. |
| 35 | * |
| 36 | * This code is derived from software contributed to Berkeley by |
| 37 | * William Jolitz and Don Ahn. |
| 38 | * |
| 39 | * Redistribution and use in source and binary forms, with or without |
| 40 | * modification, are permitted provided that the following conditions |
| 41 | * are met: |
| 42 | * 1. Redistributions of source code must retain the above copyright |
| 43 | * notice, this list of conditions and the following disclaimer. |
| 44 | * 2. Redistributions in binary form must reproduce the above copyright |
| 45 | * notice, this list of conditions and the following disclaimer in the |
| 46 | * documentation and/or other materials provided with the distribution. |
| 47 | * 3. Neither the name of the University nor the names of its contributors |
| 48 | * may be used to endorse or promote products derived from this software |
| 49 | * without specific prior written permission. |
| 50 | * |
| 51 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 52 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 53 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 54 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 55 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 56 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 57 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 58 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 59 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 60 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 61 | * SUCH DAMAGE. |
| 62 | * |
| 63 | * @(#)pccons.c 5.11 (Berkeley) 5/21/91 |
| 64 | */ |
| 65 | |
| 66 | /* |
| 67 | * code to work keyboard for PC-style console |
| 68 | */ |
| 69 | |
| 70 | #include <sys/cdefs.h> |
| 71 | __KERNEL_RCSID(0, "$NetBSD: pckbd.c,v 1.32 2015/07/16 15:01:04 prlw1 Exp $" ); |
| 72 | |
| 73 | #include <sys/param.h> |
| 74 | #include <sys/systm.h> |
| 75 | #include <sys/device.h> |
| 76 | #include <sys/malloc.h> |
| 77 | #include <sys/ioctl.h> |
| 78 | |
| 79 | #include <sys/bus.h> |
| 80 | |
| 81 | #include <dev/pckbport/pckbportvar.h> |
| 82 | |
| 83 | #include <dev/wscons/wsconsio.h> |
| 84 | #include <dev/wscons/wskbdvar.h> |
| 85 | #include <dev/wscons/wsksymdef.h> |
| 86 | #include <dev/wscons/wsksymvar.h> |
| 87 | |
| 88 | #include <dev/pckbport/pckbdreg.h> |
| 89 | #include <dev/pckbport/pckbdvar.h> |
| 90 | #include <dev/pckbport/wskbdmap_mfii.h> |
| 91 | |
| 92 | #include "locators.h" |
| 93 | |
| 94 | #include "opt_pckbd_layout.h" |
| 95 | #include "opt_pckbd_cnattach_may_fail.h" |
| 96 | #include "opt_wsdisplay_compat.h" |
| 97 | |
| 98 | struct pckbd_internal { |
| 99 | int t_isconsole; |
| 100 | pckbport_tag_t t_kbctag; |
| 101 | pckbport_slot_t t_kbcslot; |
| 102 | |
| 103 | int t_translating; |
| 104 | |
| 105 | int t_lastchar; |
| 106 | int t_extended0; |
| 107 | int t_extended1; |
| 108 | int t_releasing; |
| 109 | |
| 110 | struct pckbd_softc *t_sc; /* back pointer */ |
| 111 | }; |
| 112 | |
| 113 | struct pckbd_softc { |
| 114 | device_t sc_dev; |
| 115 | |
| 116 | struct pckbd_internal *id; |
| 117 | int sc_enabled; |
| 118 | |
| 119 | int sc_ledstate; |
| 120 | |
| 121 | device_t sc_wskbddev; |
| 122 | #ifdef WSDISPLAY_COMPAT_RAWKBD |
| 123 | int rawkbd; |
| 124 | #endif |
| 125 | }; |
| 126 | |
| 127 | static int pckbd_is_console(pckbport_tag_t, pckbport_slot_t); |
| 128 | |
| 129 | int pckbdprobe(device_t, cfdata_t, void *); |
| 130 | void pckbdattach(device_t, device_t, void *); |
| 131 | |
| 132 | CFATTACH_DECL_NEW(pckbd, sizeof(struct pckbd_softc), |
| 133 | pckbdprobe, pckbdattach, NULL, NULL); |
| 134 | |
| 135 | int pckbd_enable(void *, int); |
| 136 | void pckbd_set_leds(void *, int); |
| 137 | int pckbd_ioctl(void *, u_long, void *, int, struct lwp *); |
| 138 | |
| 139 | const struct wskbd_accessops pckbd_accessops = { |
| 140 | pckbd_enable, |
| 141 | pckbd_set_leds, |
| 142 | pckbd_ioctl, |
| 143 | }; |
| 144 | |
| 145 | void pckbd_cngetc(void *, u_int *, int *); |
| 146 | void pckbd_cnpollc(void *, int); |
| 147 | void pckbd_cnbell(void *, u_int, u_int, u_int); |
| 148 | |
| 149 | const struct wskbd_consops pckbd_consops = { |
| 150 | pckbd_cngetc, |
| 151 | pckbd_cnpollc, |
| 152 | pckbd_cnbell, |
| 153 | }; |
| 154 | |
| 155 | const struct wskbd_mapdata pckbd_keymapdata = { |
| 156 | pckbd_keydesctab, |
| 157 | #ifdef PCKBD_LAYOUT |
| 158 | PCKBD_LAYOUT, |
| 159 | #else |
| 160 | KB_US, |
| 161 | #endif |
| 162 | }; |
| 163 | |
| 164 | /* |
| 165 | * Hackish support for a bell on the PC Keyboard; when a suitable feeper |
| 166 | * is found, it attaches itself into the pckbd driver here. |
| 167 | */ |
| 168 | void (*pckbd_bell_fn)(void *, u_int, u_int, u_int, int); |
| 169 | void *pckbd_bell_fn_arg; |
| 170 | |
| 171 | void pckbd_bell(u_int, u_int, u_int, int); |
| 172 | |
| 173 | int pckbd_scancode_translate(struct pckbd_internal *, int); |
| 174 | int pckbd_set_xtscancode(pckbport_tag_t, pckbport_slot_t, |
| 175 | struct pckbd_internal *); |
| 176 | int pckbd_init(struct pckbd_internal *, pckbport_tag_t, pckbport_slot_t, |
| 177 | int); |
| 178 | void pckbd_input(void *, int); |
| 179 | |
| 180 | static int pckbd_decode(struct pckbd_internal *, int, u_int *, int *); |
| 181 | static int pckbd_led_encode(int); |
| 182 | static int pckbd_led_decode(int); |
| 183 | |
| 184 | struct pckbd_internal pckbd_consdata; |
| 185 | |
| 186 | int |
| 187 | pckbd_set_xtscancode(pckbport_tag_t kbctag, pckbport_slot_t kbcslot, |
| 188 | struct pckbd_internal *id) |
| 189 | { |
| 190 | int xt, res = 0; |
| 191 | u_char cmd[2]; |
| 192 | |
| 193 | /* |
| 194 | * Some keyboard/8042 combinations do not seem to work if the keyboard |
| 195 | * is set to table 1; in fact, it would appear that some keyboards just |
| 196 | * ignore the command altogether. So by default, we use the AT scan |
| 197 | * codes and have the 8042 translate them. Unfortunately, this is |
| 198 | * known to not work on some PS/2 machines. We try desperately to deal |
| 199 | * with this by checking the (lack of a) translate bit in the 8042 and |
| 200 | * attempting to set the keyboard to XT mode. If this all fails, well, |
| 201 | * tough luck. If the PCKBC_CANT_TRANSLATE pckbc flag was set, we |
| 202 | * enable software translation. |
| 203 | * |
| 204 | * XXX It would perhaps be a better choice to just use AT scan codes |
| 205 | * and not bother with this. |
| 206 | */ |
| 207 | xt = pckbport_xt_translation(kbctag, kbcslot, 1); |
| 208 | if (xt == 1) { |
| 209 | /* The 8042 is translating for us; use AT codes. */ |
| 210 | cmd[0] = KBC_SETTABLE; |
| 211 | cmd[1] = 2; |
| 212 | res = pckbport_poll_cmd(kbctag, kbcslot, cmd, 2, 0, 0, 0); |
| 213 | if (res) { |
| 214 | u_char cmdb[1]; |
| 215 | aprint_debug("pckbd: error setting scanset 2\n" ); |
| 216 | /* |
| 217 | * XXX at least one keyboard is reported to lock up |
| 218 | * if a "set table" is attempted, thus the "reset". |
| 219 | * XXX ignore errors, scanset 2 should be |
| 220 | * default anyway. |
| 221 | */ |
| 222 | cmdb[0] = KBC_RESET; |
| 223 | (void)pckbport_poll_cmd(kbctag, kbcslot, cmdb, 1, 1, 0, 1); |
| 224 | pckbport_flush(kbctag, kbcslot); |
| 225 | res = 0; |
| 226 | } |
| 227 | if (id != NULL) |
| 228 | id->t_translating = 1; |
| 229 | } else if (xt == -1) { |
| 230 | /* Software translation required */ |
| 231 | if (id != NULL) |
| 232 | id->t_translating = 0; |
| 233 | } else { |
| 234 | /* Stupid 8042; set keyboard to XT codes. */ |
| 235 | cmd[0] = KBC_SETTABLE; |
| 236 | cmd[1] = 1; |
| 237 | res = pckbport_poll_cmd(kbctag, kbcslot, cmd, 2, 0, 0, 0); |
| 238 | if (res) |
| 239 | aprint_debug("pckbd: error setting scanset 1\n" ); |
| 240 | if (id != NULL) |
| 241 | id->t_translating = 1; |
| 242 | } |
| 243 | return res; |
| 244 | } |
| 245 | |
| 246 | static int |
| 247 | pckbd_is_console(pckbport_tag_t tag, pckbport_slot_t slot) |
| 248 | { |
| 249 | |
| 250 | return pckbd_consdata.t_isconsole && |
| 251 | tag == pckbd_consdata.t_kbctag && slot == pckbd_consdata.t_kbcslot; |
| 252 | } |
| 253 | |
| 254 | static bool |
| 255 | pckbd_suspend(device_t dv, const pmf_qual_t *qual) |
| 256 | { |
| 257 | struct pckbd_softc *sc = device_private(dv); |
| 258 | u_char cmd[1]; |
| 259 | int res; |
| 260 | |
| 261 | /* XXX duped from pckbd_enable, but we want to disable |
| 262 | * it even if it's the console kbd |
| 263 | */ |
| 264 | cmd[0] = KBC_DISABLE; |
| 265 | res = pckbport_enqueue_cmd(sc->id->t_kbctag, |
| 266 | sc->id->t_kbcslot, cmd, 1, 0, 1, 0); |
| 267 | if (res) |
| 268 | return false; |
| 269 | |
| 270 | pckbport_slot_enable(sc->id->t_kbctag, |
| 271 | sc->id->t_kbcslot, 0); |
| 272 | |
| 273 | sc->sc_enabled = 0; |
| 274 | return true; |
| 275 | } |
| 276 | |
| 277 | static bool |
| 278 | pckbd_resume(device_t dv, const pmf_qual_t *qual) |
| 279 | { |
| 280 | struct pckbd_softc *sc = device_private(dv); |
| 281 | u_char cmd[1], resp[1]; |
| 282 | int res; |
| 283 | |
| 284 | /* XXX jmcneill reset the keyboard */ |
| 285 | pckbport_flush(sc->id->t_kbctag, sc->id->t_kbcslot); |
| 286 | |
| 287 | cmd[0] = KBC_RESET; |
| 288 | res = pckbport_poll_cmd(sc->id->t_kbctag, |
| 289 | sc->id->t_kbcslot, cmd, 1, 1, resp, 1); |
| 290 | if (res) |
| 291 | aprint_debug("%s: reset error %d\n" , __func__, res); |
| 292 | if (resp[0] != KBR_RSTDONE) |
| 293 | printf("%s: reset response 0x%x\n" , __func__, resp[0]); |
| 294 | |
| 295 | pckbport_flush(sc->id->t_kbctag, sc->id->t_kbcslot); |
| 296 | |
| 297 | pckbd_enable(sc, 1); |
| 298 | |
| 299 | return true; |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | * these are both bad jokes |
| 304 | */ |
| 305 | int |
| 306 | pckbdprobe(device_t parent, cfdata_t cf, void *aux) |
| 307 | { |
| 308 | struct pckbport_attach_args *pa = aux; |
| 309 | int res; |
| 310 | u_char cmd[1], resp[1]; |
| 311 | |
| 312 | /* |
| 313 | * XXX There are rumours that a keyboard can be connected |
| 314 | * to the aux port as well. For me, this didn't work. |
| 315 | * For further experiments, allow it if explicitly |
| 316 | * wired in the config file. |
| 317 | */ |
| 318 | if ((pa->pa_slot != PCKBPORT_KBD_SLOT) && |
| 319 | (cf->cf_loc[PCKBPORTCF_SLOT] == PCKBPORTCF_SLOT_DEFAULT)) |
| 320 | return 0; |
| 321 | |
| 322 | /* Flush any garbage. */ |
| 323 | pckbport_flush(pa->pa_tag, pa->pa_slot); |
| 324 | |
| 325 | /* Reset the keyboard. */ |
| 326 | cmd[0] = KBC_RESET; |
| 327 | res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 1, resp, 1); |
| 328 | if (res) { |
| 329 | aprint_debug("pckbdprobe: reset error %d\n" , res); |
| 330 | /* |
| 331 | * There is probably no keyboard connected. |
| 332 | * Let the probe succeed if the keyboard is used |
| 333 | * as console input - it can be connected later. |
| 334 | */ |
| 335 | return pckbd_is_console(pa->pa_tag, pa->pa_slot) ? 1 : 0; |
| 336 | } |
| 337 | if (resp[0] != KBR_RSTDONE) { |
| 338 | printf("pckbdprobe: reset response 0x%x\n" , resp[0]); |
| 339 | return 0; |
| 340 | } |
| 341 | |
| 342 | /* |
| 343 | * Some keyboards seem to leave a second ack byte after the reset. |
| 344 | * This is kind of stupid, but we account for them anyway by just |
| 345 | * flushing the buffer. |
| 346 | */ |
| 347 | pckbport_flush(pa->pa_tag, pa->pa_slot); |
| 348 | |
| 349 | if (pckbd_set_xtscancode(pa->pa_tag, pa->pa_slot, NULL)) |
| 350 | return 0; |
| 351 | |
| 352 | return 2; |
| 353 | } |
| 354 | |
| 355 | void |
| 356 | pckbdattach(device_t parent, device_t self, void *aux) |
| 357 | { |
| 358 | struct pckbd_softc *sc = device_private(self); |
| 359 | struct pckbport_attach_args *pa = aux; |
| 360 | struct wskbddev_attach_args a; |
| 361 | int isconsole; |
| 362 | u_char cmd[1]; |
| 363 | |
| 364 | aprint_naive("\n" ); |
| 365 | aprint_normal("\n" ); |
| 366 | |
| 367 | sc->sc_dev = self; |
| 368 | isconsole = pckbd_is_console(pa->pa_tag, pa->pa_slot); |
| 369 | |
| 370 | if (isconsole) { |
| 371 | sc->id = &pckbd_consdata; |
| 372 | |
| 373 | /* |
| 374 | * Some keyboards are not enabled after a reset, |
| 375 | * so make sure it is enabled now. |
| 376 | */ |
| 377 | cmd[0] = KBC_ENABLE; |
| 378 | (void) pckbport_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
| 379 | cmd, 1, 0, 0, 0); |
| 380 | sc->sc_enabled = 1; |
| 381 | } else { |
| 382 | sc->id = malloc(sizeof(struct pckbd_internal), |
| 383 | M_DEVBUF, M_WAITOK); |
| 384 | (void) pckbd_init(sc->id, pa->pa_tag, pa->pa_slot, 0); |
| 385 | |
| 386 | /* no interrupts until enabled */ |
| 387 | cmd[0] = KBC_DISABLE; |
| 388 | (void) pckbport_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
| 389 | cmd, 1, 0, 0, 0); |
| 390 | sc->sc_enabled = 0; |
| 391 | } |
| 392 | |
| 393 | sc->id->t_sc = sc; |
| 394 | |
| 395 | pckbport_set_inputhandler(sc->id->t_kbctag, sc->id->t_kbcslot, |
| 396 | pckbd_input, sc, device_xname(sc->sc_dev)); |
| 397 | |
| 398 | a.console = isconsole; |
| 399 | |
| 400 | a.keymap = &pckbd_keymapdata; |
| 401 | |
| 402 | a.accessops = &pckbd_accessops; |
| 403 | a.accesscookie = sc; |
| 404 | |
| 405 | if (!pmf_device_register(self, pckbd_suspend, pckbd_resume)) |
| 406 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
| 407 | |
| 408 | /* |
| 409 | * Attach the wskbd, saving a handle to it. |
| 410 | * XXX XXX XXX |
| 411 | */ |
| 412 | sc->sc_wskbddev = config_found_ia(self, "wskbddev" , &a, wskbddevprint); |
| 413 | } |
| 414 | |
| 415 | int |
| 416 | pckbd_enable(void *v, int on) |
| 417 | { |
| 418 | struct pckbd_softc *sc = v; |
| 419 | int res; |
| 420 | u_char cmd[1]; |
| 421 | |
| 422 | if (on) { |
| 423 | if (sc->sc_enabled) { |
| 424 | aprint_debug("pckbd_enable: bad enable\n" ); |
| 425 | return EBUSY; |
| 426 | } |
| 427 | |
| 428 | pckbport_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 1); |
| 429 | |
| 430 | cmd[0] = KBC_ENABLE; |
| 431 | res = pckbport_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
| 432 | cmd, 1, 0, NULL, 0); |
| 433 | if (res) { |
| 434 | printf("pckbd_enable: command error\n" ); |
| 435 | return (res); |
| 436 | } |
| 437 | |
| 438 | res = pckbd_set_xtscancode(sc->id->t_kbctag, |
| 439 | sc->id->t_kbcslot, sc->id); |
| 440 | if (res) |
| 441 | return res; |
| 442 | |
| 443 | sc->sc_enabled = 1; |
| 444 | } else { |
| 445 | if (sc->id->t_isconsole) |
| 446 | return EBUSY; |
| 447 | |
| 448 | cmd[0] = KBC_DISABLE; |
| 449 | res = pckbport_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
| 450 | cmd, 1, 0, 1, 0); |
| 451 | if (res) { |
| 452 | printf("pckbd_disable: command error\n" ); |
| 453 | return res; |
| 454 | } |
| 455 | |
| 456 | pckbport_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 0); |
| 457 | |
| 458 | sc->sc_enabled = 0; |
| 459 | } |
| 460 | |
| 461 | return 0; |
| 462 | } |
| 463 | |
| 464 | const u_int8_t pckbd_xtbl[] = { |
| 465 | /* 0x00 */ |
| 466 | 0, |
| 467 | 0x43, /* F9 */ |
| 468 | 0x89, /* SunStop */ |
| 469 | 0x3f, /* F5 */ |
| 470 | 0x3d, /* F3 */ |
| 471 | 0x3b, /* F1 */ |
| 472 | 0x3c, /* F2 */ |
| 473 | 0x58, /* F12 */ |
| 474 | 0, |
| 475 | 0x44, /* F10 */ |
| 476 | 0x42, /* F8 */ |
| 477 | 0x40, /* F6 */ |
| 478 | 0x3e, /* F4 */ |
| 479 | 0x0f, /* Tab */ |
| 480 | 0x29, /* ` ~ */ |
| 481 | 0, |
| 482 | /* 0x10 */ |
| 483 | 0, |
| 484 | 0x38, /* Left Alt */ |
| 485 | 0x2a, /* Left Shift */ |
| 486 | 0, |
| 487 | 0x1d, /* Left Ctrl */ |
| 488 | 0x10, /* q */ |
| 489 | 0x02, /* 1 ! */ |
| 490 | 0, |
| 491 | 0, |
| 492 | 0, |
| 493 | 0x2c, /* z */ |
| 494 | 0x1f, /* s */ |
| 495 | 0x1e, /* a */ |
| 496 | 0x11, /* w */ |
| 497 | 0x03, /* 2 @ */ |
| 498 | 0, |
| 499 | /* 0x20 */ |
| 500 | 0, |
| 501 | 0x2e, /* c */ |
| 502 | 0x2d, /* x */ |
| 503 | 0x20, /* d */ |
| 504 | 0x12, /* e */ |
| 505 | 0x05, /* 4 $ */ |
| 506 | 0x04, /* 3 # */ |
| 507 | 0, |
| 508 | 0, |
| 509 | 0x39, /* Space */ |
| 510 | 0x2f, /* v */ |
| 511 | 0x21, /* f */ |
| 512 | 0x14, /* t */ |
| 513 | 0x13, /* r */ |
| 514 | 0x06, /* 5 % */ |
| 515 | 0, |
| 516 | /* 0x30 */ |
| 517 | 0, |
| 518 | 0x31, /* n */ |
| 519 | 0x30, /* b */ |
| 520 | 0x23, /* h */ |
| 521 | 0x22, /* g */ |
| 522 | 0x15, /* y */ |
| 523 | 0x07, /* 6 ^ */ |
| 524 | 0, |
| 525 | 0, |
| 526 | 0, |
| 527 | 0x32, /* m */ |
| 528 | 0x24, /* j */ |
| 529 | 0x16, /* u */ |
| 530 | 0x08, /* 7 & */ |
| 531 | 0x09, /* 8 * */ |
| 532 | 0, |
| 533 | /* 0x40 */ |
| 534 | 0, |
| 535 | 0x33, /* , < */ |
| 536 | 0x25, /* k */ |
| 537 | 0x17, /* i */ |
| 538 | 0x18, /* o */ |
| 539 | 0x0b, /* 0 ) */ |
| 540 | 0x0a, /* 9 ( */ |
| 541 | 0, |
| 542 | 0, |
| 543 | 0x34, /* . > */ |
| 544 | 0x35, /* / ? */ |
| 545 | 0x26, /* l */ |
| 546 | 0x27, /* ; : */ |
| 547 | 0x19, /* p */ |
| 548 | 0x0c, /* - _ */ |
| 549 | 0, |
| 550 | /* 0x50 */ |
| 551 | 0, |
| 552 | 0, |
| 553 | 0x28, /* ' " */ |
| 554 | 0, |
| 555 | 0x1a, /* [ { */ |
| 556 | 0x0d, /* = + */ |
| 557 | 0, |
| 558 | 0, |
| 559 | 0x3a, /* Caps Lock */ |
| 560 | 0x36, /* Right Shift */ |
| 561 | 0x1c, /* Return */ |
| 562 | 0x1b, /* ] } */ |
| 563 | 0, |
| 564 | 0x2b, /* \ | */ |
| 565 | 0, |
| 566 | 0, |
| 567 | /* 0x60 */ |
| 568 | 0, |
| 569 | 0, |
| 570 | 0, |
| 571 | 0, |
| 572 | 0, |
| 573 | 0, |
| 574 | 0x0e, /* Back Space */ |
| 575 | 0, |
| 576 | 0, |
| 577 | 0x4f, /* KP 1 */ |
| 578 | 0, |
| 579 | 0x4b, /* KP 4 */ |
| 580 | 0x47, /* KP 7 */ |
| 581 | 0, |
| 582 | 0, |
| 583 | 0, |
| 584 | /* 0x70 */ |
| 585 | 0x52, /* KP 0 */ |
| 586 | 0x53, /* KP . */ |
| 587 | 0x50, /* KP 2 */ |
| 588 | 0x4c, /* KP 5 */ |
| 589 | 0x4d, /* KP 6 */ |
| 590 | 0x48, /* KP 8 */ |
| 591 | 0x01, /* Escape */ |
| 592 | 0x45, /* Num Lock */ |
| 593 | 0x57, /* F11 */ |
| 594 | 0x4e, /* KP + */ |
| 595 | 0x51, /* KP 3 */ |
| 596 | 0x4a, /* KP - */ |
| 597 | 0x37, /* KP * */ |
| 598 | 0x49, /* KP 9 */ |
| 599 | 0x46, /* Scroll Lock */ |
| 600 | 0, |
| 601 | /* 0x80 */ |
| 602 | 0, |
| 603 | 0, |
| 604 | 0, |
| 605 | 0x41, /* F7 (produced as an actual 8 bit code) */ |
| 606 | 0, /* Alt-Print Screen */ |
| 607 | 0, |
| 608 | 0, |
| 609 | 0, |
| 610 | 0, |
| 611 | 0, |
| 612 | 0, |
| 613 | 0, |
| 614 | 0, |
| 615 | 0, |
| 616 | 0, |
| 617 | 0, |
| 618 | /* 0x90 */ |
| 619 | 0xdb, /* Left Meta */ |
| 620 | 0x88, /* SunHelp */ |
| 621 | 0x8a, /* SunAgain */ |
| 622 | 0x8c, /* SunUndo */ |
| 623 | 0x8e, /* SunCopy */ |
| 624 | 0x90, /* SunPaste */ |
| 625 | 0x92, /* SunCut */ |
| 626 | 0x8b, /* SunProps */ |
| 627 | 0x8d, /* SunFront */ |
| 628 | 0x8f, /* SunOpen */ |
| 629 | 0x91 /* SunFind */ |
| 630 | }; |
| 631 | |
| 632 | const u_int8_t pckbd_xtbl_ext[] = { |
| 633 | /* 0x00 */ |
| 634 | 0, |
| 635 | 0, |
| 636 | 0, |
| 637 | 0, |
| 638 | 0, |
| 639 | 0, |
| 640 | 0, |
| 641 | 0, |
| 642 | 0, |
| 643 | 0, |
| 644 | 0, |
| 645 | 0, |
| 646 | 0, |
| 647 | 0, |
| 648 | 0, |
| 649 | 0, |
| 650 | /* 0x10 */ |
| 651 | 0, |
| 652 | 0x38, /* Right Alt */ |
| 653 | 0, /* E0 12, to be ignored */ |
| 654 | 0, |
| 655 | 0x1d, /* Right Ctrl */ |
| 656 | 0, |
| 657 | 0, |
| 658 | 0, |
| 659 | 0, |
| 660 | 0, |
| 661 | 0, |
| 662 | 0, |
| 663 | 0, |
| 664 | 0, |
| 665 | 0, |
| 666 | 0, |
| 667 | /* 0x20 */ |
| 668 | 0, |
| 669 | 0, |
| 670 | 0, |
| 671 | 0, |
| 672 | 0, |
| 673 | 0, |
| 674 | 0, |
| 675 | 0, |
| 676 | 0, |
| 677 | 0, |
| 678 | 0, |
| 679 | 0, |
| 680 | 0, |
| 681 | 0, |
| 682 | 0, |
| 683 | 0xdd, /* Compose */ |
| 684 | /* 0x30 */ |
| 685 | 0, |
| 686 | 0, |
| 687 | 0, |
| 688 | 0, |
| 689 | 0, |
| 690 | 0, |
| 691 | 0, |
| 692 | 0, |
| 693 | 0, |
| 694 | 0, |
| 695 | 0, |
| 696 | 0, |
| 697 | 0, |
| 698 | 0, |
| 699 | 0, |
| 700 | 0, |
| 701 | /* 0x40 */ |
| 702 | 0, |
| 703 | 0, |
| 704 | 0, |
| 705 | 0, |
| 706 | 0, |
| 707 | 0, |
| 708 | 0, |
| 709 | 0, |
| 710 | 0, |
| 711 | 0, |
| 712 | 0xb5, /* KP / */ |
| 713 | 0, |
| 714 | 0, |
| 715 | 0, |
| 716 | 0, |
| 717 | 0, |
| 718 | /* 0x50 */ |
| 719 | 0, |
| 720 | 0, |
| 721 | 0, |
| 722 | 0, |
| 723 | 0, |
| 724 | 0, |
| 725 | 0, |
| 726 | 0, |
| 727 | 0, |
| 728 | 0, |
| 729 | 0x1c, /* KP Return */ |
| 730 | 0, |
| 731 | 0, |
| 732 | 0, |
| 733 | 0, |
| 734 | 0, |
| 735 | /* 0x60 */ |
| 736 | 0, |
| 737 | 0, |
| 738 | 0, |
| 739 | 0, |
| 740 | 0, |
| 741 | 0, |
| 742 | 0, |
| 743 | 0, |
| 744 | 0, |
| 745 | 0x4f, /* End */ |
| 746 | 0, |
| 747 | 0x4b, /* Left */ |
| 748 | 0x47, /* Home */ |
| 749 | 0, |
| 750 | 0, |
| 751 | 0, |
| 752 | /* 0x70 */ |
| 753 | 0x52, /* Insert */ |
| 754 | 0x53, /* Delete */ |
| 755 | 0x50, /* Down */ |
| 756 | 0, |
| 757 | 0x4d, /* Right */ |
| 758 | 0x48, /* Up */ |
| 759 | 0, |
| 760 | 0, |
| 761 | 0, |
| 762 | 0, |
| 763 | 0x51, /* Page Down */ |
| 764 | 0, |
| 765 | 0x37, /* Print Screen */ |
| 766 | 0x49, /* Page Up */ |
| 767 | 0x46, /* Ctrl-Break */ |
| 768 | 0 |
| 769 | }; |
| 770 | |
| 771 | /* |
| 772 | * Translate scan codes from set 2 to set 1 |
| 773 | */ |
| 774 | int |
| 775 | pckbd_scancode_translate(struct pckbd_internal *id, int datain) |
| 776 | { |
| 777 | if (id->t_translating != 0) |
| 778 | return datain; |
| 779 | |
| 780 | if (datain == KBR_BREAK) { |
| 781 | id->t_releasing = 0x80; /* next keycode is a release */ |
| 782 | return 0; /* consume scancode */ |
| 783 | } |
| 784 | |
| 785 | /* |
| 786 | * Handle extended sequences |
| 787 | */ |
| 788 | if (datain == KBR_EXTENDED0 || datain == KBR_EXTENDED1) |
| 789 | return datain; |
| 790 | |
| 791 | /* |
| 792 | * Convert BREAK sequence (14 77 -> 1D 45) |
| 793 | */ |
| 794 | if (id->t_extended1 == 2 && datain == 0x14) |
| 795 | return 0x1d | id->t_releasing; |
| 796 | else if (id->t_extended1 == 1 && datain == 0x77) |
| 797 | return 0x45 | id->t_releasing; |
| 798 | |
| 799 | if (id->t_extended0 != 0) { |
| 800 | if (datain >= sizeof pckbd_xtbl_ext) |
| 801 | datain = 0; |
| 802 | else |
| 803 | datain = pckbd_xtbl_ext[datain]; |
| 804 | } else { |
| 805 | if (datain >= sizeof pckbd_xtbl) |
| 806 | datain = 0; |
| 807 | else |
| 808 | datain = pckbd_xtbl[datain]; |
| 809 | } |
| 810 | |
| 811 | /* |
| 812 | * If we are mapping in the range 128-254, then make this |
| 813 | * an extended keycode, as table 1 codes are limited to |
| 814 | * the range 0-127 (the top bit is used for key up/break). |
| 815 | */ |
| 816 | if (datain > 0x7f) { |
| 817 | datain &= 0x7f; |
| 818 | id->t_extended0 = 0x80; |
| 819 | } |
| 820 | |
| 821 | if (datain == 0) { |
| 822 | /* |
| 823 | * We don't know how to translate this scan code, but |
| 824 | * we can't silently eat it either (because there might |
| 825 | * have been an extended byte transmitted already). |
| 826 | * Hopefully this value will be harmless to the upper |
| 827 | * layers. |
| 828 | */ |
| 829 | return 0xff; |
| 830 | } |
| 831 | return datain | id->t_releasing; |
| 832 | } |
| 833 | |
| 834 | static int |
| 835 | pckbd_decode(struct pckbd_internal *id, int datain, u_int *type, int *dataout) |
| 836 | { |
| 837 | int key; |
| 838 | int releasing; |
| 839 | |
| 840 | if (datain == KBR_EXTENDED0) { |
| 841 | id->t_extended0 = 0x80; |
| 842 | return 0; |
| 843 | } else if (datain == KBR_EXTENDED1) { |
| 844 | id->t_extended1 = 2; |
| 845 | return 0; |
| 846 | } |
| 847 | |
| 848 | releasing = datain & 0x80; |
| 849 | datain &= 0x7f; |
| 850 | |
| 851 | if (id->t_extended0 == 0x80) { |
| 852 | switch (datain) { |
| 853 | case 0x2a: |
| 854 | case 0x36: |
| 855 | id->t_extended0 = 0; |
| 856 | return 0; |
| 857 | default: |
| 858 | break; |
| 859 | } |
| 860 | } |
| 861 | |
| 862 | /* map extended keys to (unused) codes 128-254 */ |
| 863 | key = datain | id->t_extended0; |
| 864 | id->t_extended0 = 0; |
| 865 | |
| 866 | /* |
| 867 | * process PAUSE (also break) key (EXT1 1D 45 EXT1 9D C5): |
| 868 | * map to (unused) code 7F |
| 869 | */ |
| 870 | if (id->t_extended1 == 2 && (datain == 0x1d || datain == 0x9d)) { |
| 871 | id->t_extended1 = 1; |
| 872 | return 0; |
| 873 | } else if (id->t_extended1 == 1 && |
| 874 | (datain == 0x45 || datain == 0xc5)) { |
| 875 | id->t_extended1 = 0; |
| 876 | key = 0x7f; |
| 877 | } else if (id->t_extended1 > 0) { |
| 878 | id->t_extended1 = 0; |
| 879 | } |
| 880 | |
| 881 | if (id->t_translating != 0) { |
| 882 | id->t_releasing = releasing; |
| 883 | } else { |
| 884 | /* id->t_releasing computed in pckbd_scancode_translate() */ |
| 885 | } |
| 886 | |
| 887 | if (id->t_releasing) { |
| 888 | id->t_releasing = 0; |
| 889 | id->t_lastchar = 0; |
| 890 | *type = WSCONS_EVENT_KEY_UP; |
| 891 | } else { |
| 892 | /* Always ignore typematic keys */ |
| 893 | if (key == id->t_lastchar) |
| 894 | return 0; |
| 895 | id->t_lastchar = key; |
| 896 | *type = WSCONS_EVENT_KEY_DOWN; |
| 897 | } |
| 898 | |
| 899 | *dataout = key; |
| 900 | return 1; |
| 901 | } |
| 902 | |
| 903 | int |
| 904 | pckbd_init(struct pckbd_internal *t, pckbport_tag_t kbctag, |
| 905 | pckbport_slot_t kbcslot, int console) |
| 906 | { |
| 907 | |
| 908 | memset(t, 0, sizeof(struct pckbd_internal)); |
| 909 | |
| 910 | t->t_isconsole = console; |
| 911 | t->t_kbctag = kbctag; |
| 912 | t->t_kbcslot = kbcslot; |
| 913 | |
| 914 | return pckbd_set_xtscancode(kbctag, kbcslot, t); |
| 915 | } |
| 916 | |
| 917 | static int |
| 918 | pckbd_led_encode(int led) |
| 919 | { |
| 920 | int res; |
| 921 | |
| 922 | res = 0; |
| 923 | |
| 924 | if (led & WSKBD_LED_SCROLL) |
| 925 | res |= 0x01; |
| 926 | if (led & WSKBD_LED_NUM) |
| 927 | res |= 0x02; |
| 928 | if (led & WSKBD_LED_CAPS) |
| 929 | res |= 0x04; |
| 930 | return res; |
| 931 | } |
| 932 | |
| 933 | static int |
| 934 | pckbd_led_decode(int led) |
| 935 | { |
| 936 | int res; |
| 937 | |
| 938 | res = 0; |
| 939 | if (led & 0x01) |
| 940 | res |= WSKBD_LED_SCROLL; |
| 941 | if (led & 0x02) |
| 942 | res |= WSKBD_LED_NUM; |
| 943 | if (led & 0x04) |
| 944 | res |= WSKBD_LED_CAPS; |
| 945 | return res; |
| 946 | } |
| 947 | |
| 948 | void |
| 949 | pckbd_set_leds(void *v, int leds) |
| 950 | { |
| 951 | struct pckbd_softc *sc = v; |
| 952 | u_char cmd[2]; |
| 953 | |
| 954 | cmd[0] = KBC_MODEIND; |
| 955 | cmd[1] = pckbd_led_encode(leds); |
| 956 | sc->sc_ledstate = cmd[1]; |
| 957 | |
| 958 | (void)pckbport_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
| 959 | cmd, 2, 0, 0, 0); |
| 960 | } |
| 961 | |
| 962 | /* |
| 963 | * Got a console receive interrupt - |
| 964 | * the console processor wants to give us a character. |
| 965 | */ |
| 966 | void |
| 967 | pckbd_input(void *vsc, int data) |
| 968 | { |
| 969 | struct pckbd_softc *sc = vsc; |
| 970 | int key; |
| 971 | u_int type; |
| 972 | |
| 973 | data = pckbd_scancode_translate(sc->id, data); |
| 974 | if (data == 0) |
| 975 | return; |
| 976 | |
| 977 | #ifdef WSDISPLAY_COMPAT_RAWKBD |
| 978 | if (sc->rawkbd) { |
| 979 | u_char d = data; |
| 980 | wskbd_rawinput(sc->sc_wskbddev, &d, 1); |
| 981 | return; |
| 982 | } |
| 983 | #endif |
| 984 | if (pckbd_decode(sc->id, data, &type, &key)) |
| 985 | wskbd_input(sc->sc_wskbddev, type, key); |
| 986 | } |
| 987 | |
| 988 | int |
| 989 | pckbd_ioctl(void *v, u_long cmd, void *data, int flag, |
| 990 | struct lwp *l) |
| 991 | { |
| 992 | struct pckbd_softc *sc = v; |
| 993 | |
| 994 | switch (cmd) { |
| 995 | case WSKBDIO_GTYPE: |
| 996 | *(int *)data = WSKBD_TYPE_PC_XT; |
| 997 | return 0; |
| 998 | case WSKBDIO_SETLEDS: |
| 999 | { |
| 1000 | int res; |
| 1001 | u_char cmdb[2]; |
| 1002 | |
| 1003 | cmdb[0] = KBC_MODEIND; |
| 1004 | cmdb[1] = pckbd_led_encode(*(int *)data); |
| 1005 | sc->sc_ledstate = cmdb[1]; |
| 1006 | res = pckbport_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
| 1007 | cmdb, 2, 0, 1, 0); |
| 1008 | return res; |
| 1009 | } |
| 1010 | case WSKBDIO_GETLEDS: |
| 1011 | *(int *)data = pckbd_led_decode(sc->sc_ledstate); |
| 1012 | return 0; |
| 1013 | case WSKBDIO_COMPLEXBELL: |
| 1014 | #define d ((struct wskbd_bell_data *)data) |
| 1015 | /* |
| 1016 | * Keyboard can't beep directly; we have an |
| 1017 | * externally-provided global hook to do this. |
| 1018 | */ |
| 1019 | pckbd_bell(d->pitch, d->period, d->volume, 0); |
| 1020 | #undef d |
| 1021 | return 0; |
| 1022 | #ifdef WSDISPLAY_COMPAT_RAWKBD |
| 1023 | case WSKBDIO_SETMODE: |
| 1024 | sc->rawkbd = (*(int *)data == WSKBD_RAW); |
| 1025 | return 0; |
| 1026 | #endif |
| 1027 | } |
| 1028 | return EPASSTHROUGH; |
| 1029 | } |
| 1030 | |
| 1031 | void |
| 1032 | pckbd_bell(u_int pitch, u_int period, u_int volume, int poll) |
| 1033 | { |
| 1034 | |
| 1035 | if (pckbd_bell_fn != NULL) |
| 1036 | (*pckbd_bell_fn)(pckbd_bell_fn_arg, pitch, period, |
| 1037 | volume, poll); |
| 1038 | } |
| 1039 | |
| 1040 | void |
| 1041 | pckbd_unhook_bell(void (*fn)(void *, u_int, u_int, u_int, int), void *arg) |
| 1042 | { |
| 1043 | if (pckbd_bell_fn != fn && pckbd_bell_fn_arg != arg) |
| 1044 | return; |
| 1045 | pckbd_bell_fn = NULL; |
| 1046 | pckbd_bell_fn_arg = NULL; |
| 1047 | } |
| 1048 | |
| 1049 | void |
| 1050 | pckbd_hookup_bell(void (*fn)(void *, u_int, u_int, u_int, int), void *arg) |
| 1051 | { |
| 1052 | |
| 1053 | if (pckbd_bell_fn == NULL) { |
| 1054 | pckbd_bell_fn = fn; |
| 1055 | pckbd_bell_fn_arg = arg; |
| 1056 | } |
| 1057 | } |
| 1058 | |
| 1059 | int |
| 1060 | pckbd_cnattach(pckbport_tag_t kbctag, int kbcslot) |
| 1061 | { |
| 1062 | int res; |
| 1063 | u_char cmd[1]; |
| 1064 | |
| 1065 | res = pckbd_init(&pckbd_consdata, kbctag, kbcslot, 1); |
| 1066 | /* We may allow the console to be attached if no keyboard is present */ |
| 1067 | #if defined(PCKBD_CNATTACH_MAY_FAIL) |
| 1068 | if (res) |
| 1069 | return res; |
| 1070 | #endif |
| 1071 | |
| 1072 | /* Just to be sure. */ |
| 1073 | cmd[0] = KBC_ENABLE; |
| 1074 | res = pckbport_poll_cmd(kbctag, kbcslot, cmd, 1, 0, 0, 0); |
| 1075 | |
| 1076 | #if defined(PCKBD_CNATTACH_MAY_FAIL) |
| 1077 | if (res) |
| 1078 | return res; |
| 1079 | #endif |
| 1080 | |
| 1081 | wskbd_cnattach(&pckbd_consops, &pckbd_consdata, &pckbd_keymapdata); |
| 1082 | |
| 1083 | return res; |
| 1084 | } |
| 1085 | |
| 1086 | /* ARGSUSED */ |
| 1087 | void |
| 1088 | pckbd_cngetc(void *v, u_int *type, int *data) |
| 1089 | { |
| 1090 | struct pckbd_internal *t = v; |
| 1091 | int val; |
| 1092 | |
| 1093 | for (;;) { |
| 1094 | val = pckbport_poll_data(t->t_kbctag, t->t_kbcslot); |
| 1095 | if (val == -1) |
| 1096 | continue; |
| 1097 | |
| 1098 | val = pckbd_scancode_translate(t, val); |
| 1099 | if (val == 0) |
| 1100 | continue; |
| 1101 | |
| 1102 | if (pckbd_decode(t, val, type, data)) |
| 1103 | return; |
| 1104 | } |
| 1105 | } |
| 1106 | |
| 1107 | void |
| 1108 | pckbd_cnpollc(void *v, int on) |
| 1109 | { |
| 1110 | struct pckbd_internal *t = v; |
| 1111 | |
| 1112 | pckbport_set_poll(t->t_kbctag, t->t_kbcslot, on); |
| 1113 | } |
| 1114 | |
| 1115 | void |
| 1116 | pckbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) |
| 1117 | { |
| 1118 | |
| 1119 | pckbd_bell(pitch, period, volume, 1); |
| 1120 | } |
| 1121 | |