1 /* $NetBSD: hpckbd.c,v 1.19 2006/10/12 21:19:13 uwe Exp $ */ 2 3 /*- 4 * Copyright (c) 1999-2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by UCHIYAMA Yasushi. 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: hpckbd.c,v 1.19 2006/10/12 21:19:13 uwe Exp $"); 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/device.h> 45 46 #include <sys/tty.h> 47 48 #include <machine/bus.h> 49 #include <machine/intr.h> 50 51 #include <machine/config_hook.h> 52 #include <machine/platid.h> 53 #include <machine/platid_mask.h> 54 55 #include "opt_wsdisplay_compat.h" 56 #include "opt_pckbd_layout.h" 57 #include <dev/wscons/wsksymdef.h> 58 #include <dev/wscons/wsconsio.h> 59 #include <dev/wscons/wskbdvar.h> 60 #include <dev/wscons/wsksymdef.h> 61 #include <dev/wscons/wsksymvar.h> 62 #include <dev/pckbport/wskbdmap_mfii.h> 63 #ifdef WSDISPLAY_COMPAT_RAWKBD 64 #include <dev/hpc/pckbd_encode.h> 65 #endif 66 67 #include <dev/hpc/hpckbdvar.h> 68 #include <dev/hpc/hpckbdkeymap.h> 69 70 struct hpckbd_softc; 71 72 #define NEVENTQ 32 73 struct hpckbd_eventq { 74 u_int hq_type; 75 int hq_data; 76 }; 77 78 struct hpckbd_core { 79 struct hpckbd_if hc_if; 80 struct hpckbd_ic_if *hc_ic; 81 const uint8_t *hc_keymap; 82 const int *hc_special; 83 int hc_polling; 84 int hc_console; 85 #define NEVENTQ 32 86 struct hpckbd_eventq hc_eventq[NEVENTQ]; 87 struct hpckbd_eventq *hc_head, *hc_tail; 88 int hc_nevents; 89 int hc_enabled; 90 struct device *hc_wskbddev; 91 struct hpckbd_softc* hc_sc; /* back link */ 92 #ifdef WSDISPLAY_COMPAT_RAWKBD 93 int hc_rawkbd; 94 #endif 95 }; 96 97 struct hpckbd_softc { 98 struct device sc_dev; 99 struct hpckbd_core *sc_core; 100 struct hpckbd_core sc_coredata; 101 }; 102 103 int hpckbd_match(struct device *, struct cfdata *, void *); 104 void hpckbd_attach(struct device *, struct device *, void *); 105 106 void hpckbd_initcore(struct hpckbd_core *, struct hpckbd_ic_if *, int); 107 void hpckbd_initif(struct hpckbd_core *); 108 int hpckbd_getevent(struct hpckbd_core *, u_int *, int *); 109 int hpckbd_putevent(struct hpckbd_core *, u_int, int); 110 void hpckbd_keymap_lookup(struct hpckbd_core*); 111 void hpckbd_keymap_setup(struct hpckbd_core *, const keysym_t *, int); 112 int __hpckbd_input(void *, int, int); 113 void __hpckbd_input_hook(void *); 114 115 CFATTACH_DECL(hpckbd, sizeof(struct hpckbd_softc), 116 hpckbd_match, hpckbd_attach, NULL, NULL); 117 118 /* wskbd accessopts */ 119 int hpckbd_enable(void *, int); 120 void hpckbd_set_leds(void *, int); 121 int hpckbd_ioctl(void *, u_long, caddr_t, int, struct lwp *); 122 123 /* consopts */ 124 struct hpckbd_core hpckbd_consdata; 125 void hpckbd_cngetc(void *, u_int *, int*); 126 void hpckbd_cnpollc(void *, int); 127 128 const struct wskbd_accessops hpckbd_accessops = { 129 hpckbd_enable, 130 hpckbd_set_leds, 131 hpckbd_ioctl, 132 }; 133 134 const struct wskbd_consops hpckbd_consops = { 135 hpckbd_cngetc, 136 hpckbd_cnpollc, 137 NULL, 138 }; 139 140 struct wskbd_mapdata hpckbd_keymapdata = { 141 pckbd_keydesctab, 142 #ifdef PCKBD_LAYOUT 143 PCKBD_LAYOUT 144 #else 145 KB_US 146 #endif 147 }; 148 149 int 150 hpckbd_match(struct device *parent __unused, 151 struct cfdata *cf __unused, void *aux __unused) 152 { 153 return (1); 154 } 155 156 void 157 hpckbd_attach(struct device *parent __unused, struct device *self, void *aux) 158 { 159 struct hpckbd_attach_args *haa = aux; 160 struct hpckbd_softc *sc = device_private(self); 161 struct hpckbd_ic_if *ic = haa->haa_ic; 162 struct wskbddev_attach_args wa; 163 164 /* 165 * Initialize core if it isn't console 166 */ 167 if (hpckbd_consdata.hc_ic == ic) { 168 sc->sc_core = &hpckbd_consdata; 169 /* The core has been initialized in hpckbd_cnattach. */ 170 } else { 171 sc->sc_core = &sc->sc_coredata; 172 hpckbd_initcore(sc->sc_core, ic, 0 /* not console */); 173 } 174 175 if (sc->sc_core->hc_keymap == default_keymap) 176 printf(": no keymap."); 177 178 printf("\n"); 179 180 /* 181 * setup hpckbd public interface for parent controller. 182 */ 183 hpckbd_initif(sc->sc_core); 184 185 /* 186 * attach wskbd 187 */ 188 wa.console = sc->sc_core->hc_console; 189 wa.keymap = &hpckbd_keymapdata; 190 wa.accessops = &hpckbd_accessops; 191 wa.accesscookie = sc->sc_core; 192 sc->sc_core->hc_wskbddev = config_found(self, &wa, wskbddevprint); 193 } 194 195 int 196 hpckbd_print(void *aux __unused, const char *pnp) 197 { 198 return (pnp ? QUIET : UNCONF); 199 } 200 201 void 202 hpckbd_initcore(struct hpckbd_core *hc, struct hpckbd_ic_if *ic, int console) 203 { 204 hc->hc_polling = 0; 205 hc->hc_console = console; 206 hc->hc_ic = ic; 207 208 /* setup event queue */ 209 hc->hc_head = hc->hc_tail = hc->hc_eventq; 210 hc->hc_nevents = 0; 211 212 hpckbd_keymap_lookup(hc); 213 } 214 215 void 216 hpckbd_initif(struct hpckbd_core *hc) 217 { 218 struct hpckbd_if *kbdif = &hc->hc_if; 219 220 kbdif->hi_ctx = hc; 221 kbdif->hi_input = __hpckbd_input; 222 kbdif->hi_input_hook = __hpckbd_input_hook; 223 hpckbd_ic_establish(hc->hc_ic, &hc->hc_if); 224 } 225 226 int 227 hpckbd_putevent(struct hpckbd_core* hc, u_int type, int data) 228 { 229 int s = spltty(); 230 231 if (hc->hc_nevents == NEVENTQ) { 232 splx(s); 233 return (0); /* queue is full */ 234 } 235 236 hc->hc_nevents++; 237 hc->hc_tail->hq_type = type; 238 hc->hc_tail->hq_data = data; 239 if (&hc->hc_eventq[NEVENTQ] <= ++hc->hc_tail) 240 hc->hc_tail = hc->hc_eventq; 241 splx(s); 242 243 return (1); 244 } 245 246 int 247 hpckbd_getevent(struct hpckbd_core* hc, u_int *type, int *data) 248 { 249 int s = spltty(); 250 251 if (hc->hc_nevents == 0) { 252 splx(s); 253 return (0); /* queue is empty */ 254 } 255 256 *type = hc->hc_head->hq_type; 257 *data = hc->hc_head->hq_data; 258 hc->hc_nevents--; 259 if (&hc->hc_eventq[NEVENTQ] <= ++hc->hc_head) 260 hc->hc_head = hc->hc_eventq; 261 splx(s); 262 263 return (1); 264 } 265 266 void 267 hpckbd_keymap_setup(struct hpckbd_core *hc __unused, 268 const keysym_t *map, int mapsize) 269 { 270 int i; 271 struct wscons_keydesc *desc; 272 273 /* fix keydesc table */ 274 /* 275 * XXX The way this is done is really wrong. The __UNCONST() 276 * is a hint as to what is wrong. This actually ends up modifying 277 * initialized data which is marked "const". 278 * The reason we get away with it here is apparently that text 279 * and read-only data gets mapped read/write on the platforms 280 * using this code. 281 */ 282 desc = (struct wscons_keydesc *)__UNCONST(hpckbd_keymapdata.keydesc); 283 for (i = 0; desc[i].name != 0; i++) { 284 if ((desc[i].name & KB_MACHDEP) && desc[i].map == NULL) { 285 desc[i].map = map; 286 desc[i].map_size = mapsize; 287 } 288 } 289 290 return; 291 } 292 293 void 294 hpckbd_keymap_lookup(struct hpckbd_core *hc) 295 { 296 const struct hpckbd_keymap_table *tab; 297 platid_mask_t mask; 298 299 for (tab = hpckbd_keymap_table; tab->ht_platform != NULL; tab++) { 300 301 mask = PLATID_DEREF(tab->ht_platform); 302 303 if (platid_match(&platid, &mask)) { 304 hc->hc_keymap = tab->ht_keymap; 305 hc->hc_special = tab->ht_special; 306 #if !defined(PCKBD_LAYOUT) 307 hpckbd_keymapdata.layout = tab->ht_layout; 308 #endif 309 if (tab->ht_cmdmap.map) { 310 hpckbd_keymap_setup(hc, tab->ht_cmdmap.map, 311 tab->ht_cmdmap.size); 312 #if !defined(PCKBD_LAYOUT) 313 hpckbd_keymapdata.layout |= KB_MACHDEP; 314 #endif 315 } else { 316 hpckbd_keymapdata.layout &= ~KB_MACHDEP; 317 } 318 return; 319 } 320 } 321 322 /* no keymap. use default. */ 323 hc->hc_keymap = default_keymap; 324 hc->hc_special = default_special_keymap; 325 #if !defined(PCKBD_LAYOUT) 326 hpckbd_keymapdata.layout = KB_US; 327 #endif 328 } 329 330 void 331 __hpckbd_input_hook(void *arg __unused) 332 { 333 #if 0 334 struct hpckbd_core *hc = arg; 335 336 if (hc->hc_polling) { 337 hc->hc_type = WSCONS_EVENT_ALL_KEYS_UP; 338 } 339 #endif 340 } 341 342 int 343 __hpckbd_input(void *arg, int flag, int scancode) 344 { 345 struct hpckbd_core *hc = arg; 346 int type, key; 347 348 if (flag) { 349 type = WSCONS_EVENT_KEY_DOWN; 350 } else { 351 type = WSCONS_EVENT_KEY_UP; 352 } 353 354 key = hc->hc_keymap[scancode]; 355 if (key == UNK) { 356 #ifdef DEBUG 357 printf("hpckbd: unknown scan code %#x (%d, %d)\n", 358 scancode, scancode >> 3, 359 scancode - ((scancode >> 3) << 3)); 360 #endif /* DEBUG */ 361 return (0); 362 } 363 364 if (key == IGN) { 365 return (0); 366 } 367 368 if (key == SPL) { 369 if (!flag) 370 return (0); 371 372 if (scancode == hc->hc_special[KEY_SPECIAL_OFF]) { 373 config_hook_call(CONFIG_HOOK_BUTTONEVENT, 374 CONFIG_HOOK_BUTTONEVENT_POWER, NULL); 375 } else if (scancode == hc->hc_special[KEY_SPECIAL_LIGHT]) { 376 static int onoff; /* XXX -uch */ 377 config_hook_call(CONFIG_HOOK_BUTTONEVENT, 378 CONFIG_HOOK_BUTTONEVENT_LIGHT, 379 (void *)(onoff ^= 1)); 380 } else { 381 #ifdef DEBUG 382 printf("unknown special key %d\n", scancode); 383 #endif 384 } 385 386 return (0); 387 } 388 389 if (hc->hc_polling) { 390 if (hpckbd_putevent(hc, type, key) == 0) 391 printf("hpckbd: queue over flow\n"); 392 } else { 393 #ifdef WSDISPLAY_COMPAT_RAWKBD 394 if (hc->hc_rawkbd) { 395 int n; 396 u_char data[16]; 397 n = pckbd_encode(type, key, data); 398 wskbd_rawinput(hc->hc_wskbddev, data, n); 399 } else 400 #endif 401 wskbd_input(hc->hc_wskbddev, type, key); 402 } 403 404 return (0); 405 } 406 407 /* 408 * console support routines 409 */ 410 int 411 hpckbd_cnattach(struct hpckbd_ic_if *ic) 412 { 413 struct hpckbd_core *hc = &hpckbd_consdata; 414 415 hpckbd_initcore(hc, ic, 1 /* console */); 416 417 /* attach controller */ 418 hpckbd_initif(hc); 419 420 /* attach wskbd */ 421 wskbd_cnattach(&hpckbd_consops, hc, &hpckbd_keymapdata); 422 423 return (0); 424 } 425 426 void 427 hpckbd_cngetc(void *arg, u_int *type, int *data) 428 { 429 struct hpckbd_core *hc = arg; 430 431 if (!hc->hc_console || !hc->hc_polling || !hc->hc_ic) 432 return; 433 434 while (hpckbd_getevent(hc, type, data) == 0) /* busy loop */ 435 hpckbd_ic_poll(hc->hc_ic); 436 } 437 438 void 439 hpckbd_cnpollc(void *arg, int on) 440 { 441 struct hpckbd_core *hc = arg; 442 443 hc->hc_polling = on; 444 } 445 446 int 447 hpckbd_enable(void *arg, int on) 448 { 449 struct hpckbd_core *hc = arg; 450 451 if (on) { 452 if (hc->hc_enabled) 453 return (EBUSY); 454 hc->hc_enabled = 1; 455 } else { 456 if (hc->hc_console) 457 return (EBUSY); 458 hc->hc_enabled = 0; 459 } 460 461 return (0); 462 } 463 464 void 465 hpckbd_set_leds(void *arg __unused, int leds __unused) 466 { 467 /* Can you find any LED which tells you about keyboard? */ 468 } 469 470 int 471 hpckbd_ioctl(void *arg, u_long cmd, caddr_t data, int flag __unused, 472 struct lwp *l __unused) 473 { 474 #ifdef WSDISPLAY_COMPAT_RAWKBD 475 struct hpckbd_core *hc = arg; 476 #endif 477 switch (cmd) { 478 case WSKBDIO_GTYPE: 479 *(int *)data = WSKBD_TYPE_HPC_KBD; 480 return (0); 481 case WSKBDIO_SETLEDS: 482 return 0; 483 case WSKBDIO_GETLEDS: 484 *(int *)data = 0; /* dummy for wsconsctl(8) */ 485 return (0); 486 #ifdef WSDISPLAY_COMPAT_RAWKBD 487 case WSKBDIO_SETMODE: 488 hc->hc_rawkbd = (*(int *)data == WSKBD_RAW); 489 return (0); 490 #endif 491 } 492 return (EPASSTHROUGH); 493 } 494