1 /* $OpenBSD: hilkbd.c,v 1.17 2017/03/11 11:55:03 mpi 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 seen_hilkbd_console = 0; 233 } 234 235 if (sc->sc_wskbddev != NULL) 236 return config_detach(sc->sc_wskbddev, flags); 237 238 return (0); 239 } 240 241 int 242 hilkbd_enable(void *v, int on) 243 { 244 struct hilkbd_softc *sc = v; 245 246 if (on) { 247 if (sc->sc_enabled) 248 return (EBUSY); 249 } else { 250 if (sc->sc_console) 251 return (EBUSY); 252 } 253 254 sc->sc_enabled = on; 255 256 return (0); 257 } 258 259 void 260 hilkbd_set_leds(void *v, int leds) 261 { 262 struct hilkbd_softc *sc = v; 263 int changemask; 264 265 if (sc->sc_numleds == 0) 266 return; 267 268 changemask = leds ^ sc->sc_ledstate; 269 if (changemask == 0) 270 return; 271 272 /* We do not handle more than 3 leds here */ 273 if (changemask & WSKBD_LED_SCROLL) 274 send_hildev_cmd((struct hildev_softc *)sc, 275 (leds & WSKBD_LED_SCROLL) ? HIL_PROMPT1 : HIL_ACK1, 276 NULL, NULL); 277 if (changemask & WSKBD_LED_NUM) 278 send_hildev_cmd((struct hildev_softc *)sc, 279 (leds & WSKBD_LED_NUM) ? HIL_PROMPT2 : HIL_ACK2, 280 NULL, NULL); 281 if (changemask & WSKBD_LED_CAPS) 282 send_hildev_cmd((struct hildev_softc *)sc, 283 (leds & WSKBD_LED_CAPS) ? HIL_PROMPT3 : HIL_ACK3, 284 NULL, NULL); 285 286 sc->sc_ledstate = leds; 287 } 288 289 int 290 hilkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 291 { 292 struct hilkbd_softc *sc = v; 293 294 switch (cmd) { 295 case WSKBDIO_GTYPE: 296 *(int *)data = WSKBD_TYPE_HIL; 297 return 0; 298 case WSKBDIO_SETLEDS: 299 hilkbd_set_leds(v, *(int *)data); 300 return 0; 301 case WSKBDIO_GETLEDS: 302 *(int *)data = sc->sc_ledstate; 303 return 0; 304 #ifdef WSDISPLAY_COMPAT_RAWKBD 305 case WSKBDIO_SETMODE: 306 sc->sc_rawkbd = *(int *)data == WSKBD_RAW; 307 return 0; 308 #endif 309 case WSKBDIO_COMPLEXBELL: 310 #define d ((struct wskbd_bell_data *)data) 311 hilkbd_bell((struct hil_softc *)sc->hd_parent, 312 d->pitch, d->period, d->volume); 313 #undef d 314 return 0; 315 } 316 317 return -1; 318 } 319 320 void 321 hilkbd_cngetc(void *v, u_int *type, int *data) 322 { 323 struct hilkbd_softc *sc = v; 324 u_int8_t c, stat; 325 326 for (;;) { 327 while (hil_poll_data((struct hildev_softc *)sc, &stat, &c) != 0) 328 ; 329 330 /* 331 * Disregard keyboard data packet header. 332 * Note that no key generates it, so we're safe. 333 */ 334 if (c != HIL_KBDBUTTON) 335 break; 336 } 337 338 hilkbd_decode(sc, c, type, data, HIL_KBDBUTTON); 339 } 340 341 void 342 hilkbd_cnpollc(void *v, int on) 343 { 344 struct hilkbd_softc *sc = v; 345 346 hil_set_poll((struct hil_softc *)sc->hd_parent, on); 347 } 348 349 void 350 hilkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 351 { 352 struct hilkbd_softc *sc = v; 353 354 hilkbd_bell((struct hil_softc *)sc->hd_parent, 355 pitch, period, volume); 356 } 357 358 void 359 hilkbd_bell(struct hil_softc *sc, u_int pitch, u_int period, u_int volume) 360 { 361 u_int8_t buf[2]; 362 363 /* XXX there could be at least a pitch -> HIL pitch conversion here */ 364 #define BELLDUR 80 /* tone duration in msec (10-2560) */ 365 #define BELLFREQ 8 /* tone frequency (0-63) */ 366 buf[0] = ar_format(period - 10); 367 buf[1] = BELLFREQ; 368 send_hil_cmd(sc, HIL_SETTONE, buf, 2, NULL); 369 } 370 371 void 372 hilkbd_callback(struct hildev_softc *dev, u_int buflen, u_int8_t *buf) 373 { 374 struct hilkbd_softc *sc = (struct hilkbd_softc *)dev; 375 u_int type; 376 int kbdtype, key; 377 int i, s; 378 379 /* 380 * Ignore packet if we don't need it 381 */ 382 if (sc->sc_enabled == 0) 383 return; 384 385 if (buflen == 0) 386 return; 387 switch ((kbdtype = *buf & HIL_KBDDATA)) { 388 case HIL_BUTTONBOX: 389 case HIL_KBDBUTTON: 390 break; 391 default: 392 return; 393 } 394 395 #ifdef WSDISPLAY_COMPAT_RAWKBD 396 if (sc->sc_rawkbd) { 397 u_char cbuf[HILBUFSIZE * 2]; 398 int c, j = 0; 399 400 for (i = 1, buf++; i < buflen; i++) { 401 hilkbd_decode(sc, *buf++, &type, &key, kbdtype); 402 c = hilkbd_raw[key]; 403 if (c == RAWKEY_Null) 404 continue; 405 /* fake extended scancode if necessary */ 406 if (c & 0x80) 407 cbuf[j++] = 0xe0; 408 cbuf[j] = c & 0x7f; 409 if (type == WSCONS_EVENT_KEY_UP) 410 cbuf[j] |= 0x80; 411 j++; 412 } 413 414 s = spltty(); 415 wskbd_rawinput(sc->sc_wskbddev, cbuf, j); 416 splx(s); 417 } else 418 #endif 419 { 420 s = spltty(); 421 for (i = 1, buf++; i < buflen; i++) { 422 hilkbd_decode(sc, *buf++, &type, &key, kbdtype); 423 if (sc->sc_wskbddev != NULL) 424 wskbd_input(sc->sc_wskbddev, type, key); 425 } 426 splx(s); 427 } 428 } 429 430 void 431 hilkbd_decode(struct hilkbd_softc *sc, u_int8_t data, u_int *type, int *key, 432 int kbdtype) 433 { 434 if (kbdtype == HIL_BUTTONBOX) { 435 if (data == 0x02) /* repeat arrow */ 436 data = sc->sc_lastarrow; 437 else if (data >= 0xf8) 438 sc->sc_lastarrow = data; 439 } 440 441 *type = (data & 1) ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; 442 *key = data >> 1; 443 } 444 445 int 446 hilkbd_is_console(int hil_is_console) 447 { 448 /* if not first hil keyboard, then not the console */ 449 if (seen_hilkbd_console) 450 return (0); 451 452 /* if PDC console does not match hil bus path, then not the console */ 453 if (hil_is_console == 0) 454 return (0); 455 456 seen_hilkbd_console = 1; 457 return (1); 458 } 459