1 /* $OpenBSD: hilkbd.c,v 1.16 2014/01/26 17:48:08 miod Exp $ */ 2 /* 3 * Copyright (c) 2003, Miodrag Vallat. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 * 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/device.h> 32 #include <sys/ioctl.h> 33 #include <sys/kernel.h> 34 #include <sys/timeout.h> 35 36 #include <machine/autoconf.h> 37 #include <machine/bus.h> 38 #include <machine/cpu.h> 39 40 #include <dev/hil/hilreg.h> 41 #include <dev/hil/hilvar.h> 42 #include <dev/hil/hildevs.h> 43 44 #include <dev/wscons/wsconsio.h> 45 #include <dev/wscons/wskbdvar.h> 46 #include <dev/wscons/wsksymdef.h> 47 #include <dev/wscons/wsksymvar.h> 48 #ifdef WSDISPLAY_COMPAT_RAWKBD 49 #include <dev/wscons/wskbdraw.h> 50 #endif 51 52 #include <dev/hil/hilkbdmap.h> 53 54 struct hilkbd_softc { 55 struct hildev_softc sc_hildev; 56 57 int sc_numleds; 58 int sc_ledstate; 59 int sc_enabled; 60 int sc_console; 61 int sc_lastarrow; 62 63 struct device *sc_wskbddev; 64 65 #ifdef WSDISPLAY_COMPAT_RAWKBD 66 int sc_rawkbd; 67 #endif 68 }; 69 70 int hilkbdprobe(struct device *, void *, void *); 71 void hilkbdattach(struct device *, struct device *, void *); 72 int hilkbddetach(struct device *, int); 73 74 struct cfdriver hilkbd_cd = { 75 NULL, "hilkbd", DV_DULL 76 }; 77 78 struct cfattach hilkbd_ca = { 79 sizeof(struct hilkbd_softc), hilkbdprobe, hilkbdattach, hilkbddetach, 80 }; 81 82 int hilkbd_enable(void *, int); 83 void hilkbd_set_leds(void *, int); 84 int hilkbd_ioctl(void *, u_long, caddr_t, int, struct proc *); 85 86 const struct wskbd_accessops hilkbd_accessops = { 87 hilkbd_enable, 88 hilkbd_set_leds, 89 hilkbd_ioctl, 90 }; 91 92 void hilkbd_cngetc(void *, u_int *, int *); 93 void hilkbd_cnpollc(void *, int); 94 void hilkbd_cnbell(void *, u_int, u_int, u_int); 95 96 const struct wskbd_consops hilkbd_consops = { 97 hilkbd_cngetc, 98 hilkbd_cnpollc, 99 hilkbd_cnbell, 100 }; 101 102 struct wskbd_mapdata hilkbd_keymapdata = { 103 hilkbd_keydesctab, 104 #ifdef HILKBD_LAYOUT 105 HILKBD_LAYOUT, 106 #else 107 KB_US | KB_DEFAULT, 108 #endif 109 }; 110 111 struct wskbd_mapdata hilkbd_keymapdata_ps2 = { 112 hilkbd_keydesctab_ps2, 113 #ifdef HILKBD_LAYOUT 114 HILKBD_LAYOUT, 115 #else 116 KB_US | KB_DEFAULT, 117 #endif 118 }; 119 120 void hilkbd_bell(struct hil_softc *, u_int, u_int, u_int); 121 void hilkbd_callback(struct hildev_softc *, u_int, u_int8_t *); 122 void hilkbd_decode(struct hilkbd_softc *, u_int8_t, u_int *, int *, int); 123 int hilkbd_is_console(int); 124 125 int seen_hilkbd_console; 126 127 int 128 hilkbdprobe(struct device *parent, void *match, void *aux) 129 { 130 struct hil_attach_args *ha = aux; 131 132 if (ha->ha_type != HIL_DEVICE_KEYBOARD && 133 ha->ha_type != HIL_DEVICE_BUTTONBOX) 134 return (0); 135 136 return (1); 137 } 138 139 void 140 hilkbdattach(struct device *parent, struct device *self, void *aux) 141 { 142 struct hilkbd_softc *sc = (void *)self; 143 struct hil_attach_args *ha = aux; 144 struct wskbddev_attach_args a; 145 u_int8_t layoutcode; 146 int ps2; 147 148 sc->hd_code = ha->ha_code; 149 sc->hd_type = ha->ha_type; 150 sc->hd_infolen = ha->ha_infolen; 151 bcopy(ha->ha_info, sc->hd_info, ha->ha_infolen); 152 sc->hd_fn = hilkbd_callback; 153 154 if (ha->ha_type == HIL_DEVICE_KEYBOARD) { 155 /* 156 * Determine the keyboard language configuration, but don't 157 * override a user-specified setting. 158 */ 159 layoutcode = ha->ha_id & (MAXHILKBDLAYOUT - 1); 160 #ifndef HILKBD_LAYOUT 161 if (layoutcode < MAXHILKBDLAYOUT && 162 hilkbd_layouts[layoutcode] != -1) 163 hilkbd_keymapdata.layout = 164 hilkbd_keymapdata_ps2.layout = 165 hilkbd_layouts[layoutcode]; 166 #endif 167 168 printf(", layout %x", layoutcode); 169 } 170 171 /* 172 * Interpret the identification bytes, if any 173 */ 174 if (ha->ha_infolen > 2 && (ha->ha_info[1] & HIL_IOB) != 0) { 175 /* HILIOB_PROMPT is not always reported... */ 176 sc->sc_numleds = (ha->ha_info[2] & HILIOB_PMASK) >> 4; 177 if (sc->sc_numleds != 0) 178 printf(", %d leds", sc->sc_numleds); 179 } 180 181 printf("\n"); 182 183 /* 184 * Red lettered keyboards come in two flavours, the old one 185 * with only one control key, to the left of the escape key, 186 * and the modern one which has a PS/2 like layout, and leds. 187 * 188 * Unfortunately for us, they use the same device ID range. 189 * We'll differentiate them by looking at the leds property. 190 */ 191 ps2 = (sc->sc_numleds != 0); 192 193 /* Do not consider button boxes as console devices. */ 194 if (ha->ha_type == HIL_DEVICE_BUTTONBOX) 195 a.console = 0; 196 else 197 a.console = hilkbd_is_console(ha->ha_console); 198 a.keymap = ps2 ? &hilkbd_keymapdata_ps2 : &hilkbd_keymapdata; 199 a.accessops = &hilkbd_accessops; 200 a.accesscookie = sc; 201 202 if (a.console) { 203 sc->sc_console = sc->sc_enabled = 1; 204 wskbd_cnattach(&hilkbd_consops, sc, a.keymap); 205 } else { 206 sc->sc_console = sc->sc_enabled = 0; 207 } 208 209 sc->sc_wskbddev = config_found(self, &a, wskbddevprint); 210 211 /* 212 * If this is an old keyboard with a numeric pad but no ``num lock'' 213 * key, simulate it being pressed so that the keyboard runs in 214 * numeric mode. 215 */ 216 if (!ps2 && sc->sc_wskbddev != NULL) { 217 wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_DOWN, 80); 218 wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_UP, 80); 219 } 220 } 221 222 int 223 hilkbddetach(struct device *self, int flags) 224 { 225 struct hilkbd_softc *sc = (void *)self; 226 227 /* 228 * Handle console keyboard for the best. It should have been set 229 * as the first device in the loop anyways. 230 */ 231 if (sc->sc_console) { 232 wskbd_cndetach(); 233 seen_hilkbd_console = 0; 234 } 235 236 if (sc->sc_wskbddev != NULL) 237 return config_detach(sc->sc_wskbddev, flags); 238 239 return (0); 240 } 241 242 int 243 hilkbd_enable(void *v, int on) 244 { 245 struct hilkbd_softc *sc = v; 246 247 if (on) { 248 if (sc->sc_enabled) 249 return (EBUSY); 250 } else { 251 if (sc->sc_console) 252 return (EBUSY); 253 } 254 255 sc->sc_enabled = on; 256 257 return (0); 258 } 259 260 void 261 hilkbd_set_leds(void *v, int leds) 262 { 263 struct hilkbd_softc *sc = v; 264 int changemask; 265 266 if (sc->sc_numleds == 0) 267 return; 268 269 changemask = leds ^ sc->sc_ledstate; 270 if (changemask == 0) 271 return; 272 273 /* We do not handle more than 3 leds here */ 274 if (changemask & WSKBD_LED_SCROLL) 275 send_hildev_cmd((struct hildev_softc *)sc, 276 (leds & WSKBD_LED_SCROLL) ? HIL_PROMPT1 : HIL_ACK1, 277 NULL, NULL); 278 if (changemask & WSKBD_LED_NUM) 279 send_hildev_cmd((struct hildev_softc *)sc, 280 (leds & WSKBD_LED_NUM) ? HIL_PROMPT2 : HIL_ACK2, 281 NULL, NULL); 282 if (changemask & WSKBD_LED_CAPS) 283 send_hildev_cmd((struct hildev_softc *)sc, 284 (leds & WSKBD_LED_CAPS) ? HIL_PROMPT3 : HIL_ACK3, 285 NULL, NULL); 286 287 sc->sc_ledstate = leds; 288 } 289 290 int 291 hilkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 292 { 293 struct hilkbd_softc *sc = v; 294 295 switch (cmd) { 296 case WSKBDIO_GTYPE: 297 *(int *)data = WSKBD_TYPE_HIL; 298 return 0; 299 case WSKBDIO_SETLEDS: 300 hilkbd_set_leds(v, *(int *)data); 301 return 0; 302 case WSKBDIO_GETLEDS: 303 *(int *)data = sc->sc_ledstate; 304 return 0; 305 #ifdef WSDISPLAY_COMPAT_RAWKBD 306 case WSKBDIO_SETMODE: 307 sc->sc_rawkbd = *(int *)data == WSKBD_RAW; 308 return 0; 309 #endif 310 case WSKBDIO_COMPLEXBELL: 311 #define d ((struct wskbd_bell_data *)data) 312 hilkbd_bell((struct hil_softc *)sc->hd_parent, 313 d->pitch, d->period, d->volume); 314 #undef d 315 return 0; 316 } 317 318 return -1; 319 } 320 321 void 322 hilkbd_cngetc(void *v, u_int *type, int *data) 323 { 324 struct hilkbd_softc *sc = v; 325 u_int8_t c, stat; 326 327 for (;;) { 328 while (hil_poll_data((struct hildev_softc *)sc, &stat, &c) != 0) 329 ; 330 331 /* 332 * Disregard keyboard data packet header. 333 * Note that no key generates it, so we're safe. 334 */ 335 if (c != HIL_KBDBUTTON) 336 break; 337 } 338 339 hilkbd_decode(sc, c, type, data, HIL_KBDBUTTON); 340 } 341 342 void 343 hilkbd_cnpollc(void *v, int on) 344 { 345 struct hilkbd_softc *sc = v; 346 347 hil_set_poll((struct hil_softc *)sc->hd_parent, on); 348 } 349 350 void 351 hilkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 352 { 353 struct hilkbd_softc *sc = v; 354 355 hilkbd_bell((struct hil_softc *)sc->hd_parent, 356 pitch, period, volume); 357 } 358 359 void 360 hilkbd_bell(struct hil_softc *sc, u_int pitch, u_int period, u_int volume) 361 { 362 u_int8_t buf[2]; 363 364 /* XXX there could be at least a pitch -> HIL pitch conversion here */ 365 #define BELLDUR 80 /* tone duration in msec (10-2560) */ 366 #define BELLFREQ 8 /* tone frequency (0-63) */ 367 buf[0] = ar_format(period - 10); 368 buf[1] = BELLFREQ; 369 send_hil_cmd(sc, HIL_SETTONE, buf, 2, NULL); 370 } 371 372 void 373 hilkbd_callback(struct hildev_softc *dev, u_int buflen, u_int8_t *buf) 374 { 375 struct hilkbd_softc *sc = (struct hilkbd_softc *)dev; 376 u_int type; 377 int kbdtype, key; 378 int i, s; 379 380 /* 381 * Ignore packet if we don't need it 382 */ 383 if (sc->sc_enabled == 0) 384 return; 385 386 if (buflen == 0) 387 return; 388 switch ((kbdtype = *buf & HIL_KBDDATA)) { 389 case HIL_BUTTONBOX: 390 case HIL_KBDBUTTON: 391 break; 392 default: 393 return; 394 } 395 396 #ifdef WSDISPLAY_COMPAT_RAWKBD 397 if (sc->sc_rawkbd) { 398 u_char cbuf[HILBUFSIZE * 2]; 399 int c, j = 0; 400 401 for (i = 1, buf++; i < buflen; i++) { 402 hilkbd_decode(sc, *buf++, &type, &key, kbdtype); 403 c = hilkbd_raw[key]; 404 if (c == RAWKEY_Null) 405 continue; 406 /* fake extended scancode if necessary */ 407 if (c & 0x80) 408 cbuf[j++] = 0xe0; 409 cbuf[j] = c & 0x7f; 410 if (type == WSCONS_EVENT_KEY_UP) 411 cbuf[j] |= 0x80; 412 j++; 413 } 414 415 s = spltty(); 416 wskbd_rawinput(sc->sc_wskbddev, cbuf, j); 417 splx(s); 418 } else 419 #endif 420 { 421 s = spltty(); 422 for (i = 1, buf++; i < buflen; i++) { 423 hilkbd_decode(sc, *buf++, &type, &key, kbdtype); 424 if (sc->sc_wskbddev != NULL) 425 wskbd_input(sc->sc_wskbddev, type, key); 426 } 427 splx(s); 428 } 429 } 430 431 void 432 hilkbd_decode(struct hilkbd_softc *sc, u_int8_t data, u_int *type, int *key, 433 int kbdtype) 434 { 435 if (kbdtype == HIL_BUTTONBOX) { 436 if (data == 0x02) /* repeat arrow */ 437 data = sc->sc_lastarrow; 438 else if (data >= 0xf8) 439 sc->sc_lastarrow = data; 440 } 441 442 *type = (data & 1) ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; 443 *key = data >> 1; 444 } 445 446 int 447 hilkbd_is_console(int hil_is_console) 448 { 449 /* if not first hil keyboard, then not the console */ 450 if (seen_hilkbd_console) 451 return (0); 452 453 /* if PDC console does not match hil bus path, then not the console */ 454 if (hil_is_console == 0) 455 return (0); 456 457 seen_hilkbd_console = 1; 458 return (1); 459 } 460