hidkbd.c revision 1.1 1 /* $OpenBSD: hidkbd.c,v 1.15 2024/10/21 19:05:31 miod Exp $ */
2 /* $NetBSD: hidkbd.c,v 1.1 2024/12/09 22:04:18 jmcneill Exp $ */
3
4 /*
5 * Copyright (c) 1998 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Lennart Augustsson (lennart (at) augustsson.net) at
10 * Carlstedt Research & Technology.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
36 */
37
38 #include "opt_wsdisplay_compat.h"
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/callout.h>
43 #include <sys/kernel.h>
44 #include <sys/device.h>
45 #include <sys/ioctl.h>
46 #include <sys/kmem.h>
47
48 #include <dev/wscons/wsconsio.h>
49 #include <dev/wscons/wskbdvar.h>
50 #include <dev/wscons/wsksymdef.h>
51 #include <dev/wscons/wsksymvar.h>
52
53 #include <dev/hid/hid.h>
54 #include <dev/hid/hidkbdsc.h>
55
56 #ifdef HIDKBD_DEBUG
57 #define DPRINTF(x) do { if (hidkbddebug) printf x; } while (0)
58 #define DPRINTFN(n,x) do { if (hidkbddebug>(n)) printf x; } while (0)
59 int hidkbddebug = 0;
60 #else
61 #define DPRINTF(x)
62 #define DPRINTFN(n,x)
63 #endif
64
65 #define PRESS 0x000
66 #define RELEASE 0x100
67 #define CODEMASK 0x0ff
68
69 #if defined(WSDISPLAY_COMPAT_RAWKBD)
70 #define NN 0 /* no translation */
71 /*
72 * Translate USB keycodes to US keyboard XT scancodes.
73 * Scancodes >= 0x80 represent EXTENDED keycodes.
74 *
75 * See http://www.microsoft.com/whdc/archive/Scancode.mspx
76 */
77 const u_int8_t hidkbd_trtab[256] = {
78 NN, NN, NN, NN, 0x1e, 0x30, 0x2e, 0x20, /* 00 - 07 */
79 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, /* 08 - 0f */
80 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14, /* 10 - 17 */
81 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x02, 0x03, /* 18 - 1f */
82 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, /* 20 - 27 */
83 0x1c, 0x01, 0x0e, 0x0f, 0x39, 0x0c, 0x0d, 0x1a, /* 28 - 2f */
84 0x1b, 0x2b, 0x2b, 0x27, 0x28, 0x29, 0x33, 0x34, /* 30 - 37 */
85 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, /* 38 - 3f */
86 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xb7, 0x46, /* 40 - 47 */
87 0x7f, 0xd2, 0xc7, 0xc9, 0xd3, 0xcf, 0xd1, 0xcd, /* 48 - 4f */
88 0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e, /* 50 - 57 */
89 0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47, /* 58 - 5f */
90 0x48, 0x49, 0x52, 0x53, 0x56, 0xdd, 0xde, 0x59, /* 60 - 67 */
91 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, /* 68 - 6f */
92 0x6c, 0x6d, 0x6e, 0x76, 0x97, NN, 0x93, 0x95, /* 70 - 77 */
93 0x91, 0x92, 0x94, 0x9a, 0x96, 0x98, 0x99, 0xa0, /* 78 - 7f */
94 0xb0, 0xae, NN, NN, NN, 0x7e, NN, 0x73, /* 80 - 87 */
95 0x70, 0x7d, 0x79, 0x7b, 0x5c, NN, NN, NN, /* 88 - 8f */
96 NN, NN, 0x78, 0x77, 0x76, NN, NN, NN, /* 90 - 97 */
97 NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9f */
98 NN, NN, NN, NN, NN, NN, NN, NN, /* a0 - a7 */
99 NN, NN, NN, NN, NN, NN, NN, NN, /* a8 - af */
100 NN, NN, NN, NN, NN, NN, NN, NN, /* b0 - b7 */
101 NN, NN, NN, NN, NN, NN, NN, NN, /* b8 - bf */
102 NN, NN, NN, NN, NN, NN, NN, NN, /* c0 - c7 */
103 NN, NN, NN, NN, NN, NN, NN, NN, /* c8 - cf */
104 NN, NN, NN, NN, NN, NN, NN, NN, /* d0 - d7 */
105 NN, NN, NN, NN, NN, NN, NN, NN, /* d8 - df */
106 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xdc, /* e0 - e7 */
107 NN, NN, NN, NN, NN, NN, NN, NN, /* e8 - ef */
108 NN, NN, NN, NN, NN, NN, NN, NN, /* f0 - f7 */
109 NN, NN, NN, NN, NN, NN, NN, NN, /* f8 - ff */
110 };
111 #endif /* defined(WSDISPLAY_COMPAT_RAWKBD) */
112
113 #if 0
114 static const struct hidkbd_translation apple_tb_trans[] = {
115 { 30, 58 }, /* 1 -> F1 */
116 { 31, 59 }, /* 2 -> F2 */
117 { 32, 60 }, /* 3 -> F3 */
118 { 33, 61 }, /* 4 -> F4 */
119 { 34, 62 }, /* 5 -> F5 */
120 { 35, 63 }, /* 6 -> F6 */
121 { 36, 64 }, /* 7 -> F7 */
122 { 37, 65 }, /* 8 -> F8 */
123 { 38, 66 }, /* 9 -> F9 */
124 { 39, 67 }, /* 0 -> F10 */
125 { 45, 68 }, /* - -> F11 */
126 { 46, 69 } /* = -> F12 */
127 };
128
129 static const struct hidkbd_translation apple_fn_trans[] = {
130 { 40, 73 }, /* return -> insert */
131 { 42, 76 }, /* backspace -> delete */
132 { 58, 233 }, /* F1 -> screen brightness down */
133 { 59, 232 }, /* F2 -> screen brightness up */
134 #ifdef notyet
135 { 60, 0 }, /* F3 */
136 { 61, 0 }, /* F4 */
137 { 62, 0 }, /* F5 -> keyboard backlight down */
138 { 63, 0 }, /* F6 -> keyboard backlight up */
139 { 64, 0 }, /* F7 -> audio back */
140 { 65, 0 }, /* F8 -> audio pause/play */
141 { 66, 0 }, /* F9 -> audio next */
142 #endif
143 #ifdef __macppc__
144 { 60, 127 }, /* F3 -> audio mute */
145 { 61, 129 }, /* F4 -> audio lower */
146 { 62, 128 }, /* F5 -> audio raise */
147 { 63, 83 }, /* F6 -> num lock */
148 { 65, 234 }, /* F8 -> backlight toggle */
149 { 66, 236 }, /* F9 -> backlight lower */
150 { 67, 235 }, /* F10 -> backlight raise */
151 { 39, 84 }, /* keypad divide */
152 { 19, 85 }, /* keypad multiply */
153 { 51, 86 }, /* keypad subtract */
154 { 56, 87 }, /* keypad add */
155 { 13, 89 }, /* keypad 1 */
156 { 14, 90 }, /* keypad 2 */
157 { 15, 91 }, /* keypad 3 */
158 { 24, 92 }, /* keypad 4 */
159 { 12, 93 }, /* keypad 5 */
160 { 18, 94 }, /* keypad 6 */
161 { 36, 95 }, /* keypad 7 */
162 { 37, 96 }, /* keypad 8 */
163 { 38, 97 }, /* keypad 9 */
164 { 16, 98 }, /* keypad 0 */
165 { 55, 99 }, /* keypad decimal */
166 { 45, 103 }, /* keypad equal */
167 #else
168 { 63, 102 }, /* F6 -> sleep */
169 { 67, 127 }, /* F10 -> audio mute */
170 { 68, 129 }, /* F11 -> audio lower */
171 { 69, 128 }, /* F12 -> audio raise */
172 #endif
173 { 79, 77 }, /* right -> end */
174 { 80, 74 }, /* left -> home */
175 { 81, 78 }, /* down -> page down */
176 { 82, 75 } /* up -> page up */
177 };
178
179 static const struct hidkbd_translation apple_mba_trans[] = {
180 { 40, 73 }, /* return -> insert */
181 { 42, 76 }, /* backspace -> delete */
182 #ifdef notyet
183 { 58, 0 }, /* F1 -> screen brightness down */
184 { 59, 0 }, /* F2 -> screen brightness up */
185 { 60, 0 }, /* F3 */
186 { 61, 0 }, /* F4 */
187 { 62, 0 }, /* F5 */
188 { 63, 0 }, /* F6 -> audio back */
189 { 64, 0 }, /* F7 -> audio pause/play */
190 { 65, 0 }, /* F8 -> audio next */
191 #endif
192 { 66, 127 }, /* F9 -> audio mute */
193 { 67, 129 }, /* F10 -> audio lower */
194 { 68, 128 }, /* F11 -> audio raise */
195 #ifdef notyet
196 { 69, 0 }, /* F12 -> eject */
197 #endif
198 { 79, 77 }, /* right -> end */
199 { 80, 74 }, /* left -> home */
200 { 81, 78 }, /* down -> page down */
201 { 82, 75 } /* up -> page up */
202 };
203
204 static const struct hidkbd_translation apple_iso_trans[] = {
205 { 53, 100 }, /* less -> grave */
206 { 100, 53 }
207 };
208 #endif
209
210 #define KEY_ERROR 0x01
211
212 #ifdef HIDKBD_DEBUG
213 #define HIDKBDTRACESIZE 64
214 struct hidkbdtraceinfo {
215 int unit;
216 struct timeval tv;
217 struct hidkbd_data ud;
218 };
219 struct hidkbdtraceinfo hidkbdtracedata[HIDKBDTRACESIZE];
220 int hidkbdtraceindex = 0;
221 int hidkbdtrace = 0;
222 void hidkbdtracedump(void);
223 void
224 hidkbdtracedump(void)
225 {
226 int i;
227 for (i = 0; i < HIDKBDTRACESIZE; i++) {
228 struct hidkbdtraceinfo *p =
229 &hidkbdtracedata[(i+hidkbdtraceindex)%HIDKBDTRACESIZE];
230 printf("%lld.%06ld: key0=0x%02x key1=0x%02x "
231 "key2=0x%02x key3=0x%02x\n",
232 (long long)p->tv.tv_sec, p->tv.tv_usec,
233 p->ud.keycode[0], p->ud.keycode[1],
234 p->ud.keycode[2], p->ud.keycode[3]);
235 }
236 }
237 #endif
238
239 int hidkbd_is_console = 0;
240
241 const char *hidkbd_parse_desc(struct hidkbd *, int, void *, int);
242
243 void hidkbd_decode(struct hidkbd *, struct hidkbd_data *);
244 void hidkbd_delayed_decode(void *addr);
245
246 extern const struct wscons_keydesc hidkbd_keydesctab[];
247
248 struct wskbd_mapdata hidkbd_keymapdata = {
249 hidkbd_keydesctab
250 };
251
252 int
253 hidkbd_attach(device_t self, struct hidkbd *kbd, int console,
254 uint32_t qflags, int id, void *desc, int dlen)
255 {
256 const char *parserr;
257
258 kbd->sc_var = NULL;
259
260 parserr = hidkbd_parse_desc(kbd, id, desc, dlen);
261 if (parserr != NULL) {
262 printf(": %s\n", parserr);
263 return ENXIO;
264 }
265
266 #ifdef DIAGNOSTIC
267 printf(": %d variable keys, %d key codes",
268 kbd->sc_nvar, kbd->sc_nkeycode);
269 #endif
270
271 kbd->sc_device = self;
272 kbd->sc_debounce = (qflags & HIDKBD_SPUR_BUT_UP) != 0;
273
274 /*
275 * Remember if we're the console keyboard.
276 *
277 * XXX This always picks the first (USB) keyboard to attach,
278 * but what else can we really do?
279 */
280 if (console) {
281 kbd->sc_console_keyboard = hidkbd_is_console;
282 /* Don't let any other keyboard have it. */
283 hidkbd_is_console = 0;
284 }
285
286 callout_init(&kbd->sc_delay, 0);
287 callout_setfunc(&kbd->sc_delay, hidkbd_delayed_decode, kbd);
288
289 return 0;
290 }
291
292 void
293 hidkbd_attach_wskbd(struct hidkbd *kbd, kbd_t layout,
294 const struct wskbd_accessops *accessops)
295 {
296 struct wskbddev_attach_args a;
297
298 hidkbd_keymapdata.layout = layout;
299
300 a.console = kbd->sc_console_keyboard;
301 a.keymap = &hidkbd_keymapdata;
302 a.accessops = accessops;
303 a.accesscookie = device_private(kbd->sc_device);
304 kbd->sc_wskbddev = config_found(kbd->sc_device, &a,
305 wskbddevprint, CFARGS_NONE);
306 }
307
308 int
309 hidkbd_detach(struct hidkbd *kbd, int flags)
310 {
311 int rv = 0;
312
313 DPRINTF(("hidkbd_detach: sc=%p flags=%d\n", device_private(kbd->sc_device), flags));
314
315 if (kbd->sc_console_keyboard) {
316 /*
317 * Disconnect our consops and set hidkbd_is_console
318 * back to 1 so that the next USB keyboard attached
319 * to the system will get it.
320 * XXX Should notify some other keyboard that it can be
321 * XXX console, if there are any other keyboards.
322 */
323 device_printf(kbd->sc_device, "was console keyboard\n");
324 hidkbd_is_console = 1;
325 }
326 /* No need to do reference counting of hidkbd, wskbd has all the goo */
327 if (kbd->sc_wskbddev != NULL)
328 rv = config_detach(kbd->sc_wskbddev, flags);
329
330 if (kbd->sc_var != NULL)
331 kmem_free(kbd->sc_var,
332 kbd->sc_nvar * sizeof(struct hidkbd_variable));
333
334 return (rv);
335 }
336
337 uint8_t
338 hidkbd_translate(const struct hidkbd_translation *table, size_t tsize,
339 uint8_t keycode)
340 {
341 for (; tsize != 0; table++, tsize--)
342 if (table->original == keycode)
343 return table->translation;
344 return 0;
345 }
346
347 #if 0
348 void
349 hidkbd_apple_translate(void *vsc, uint8_t *ibuf, u_int ilen,
350 const struct hidkbd_translation* trans, u_int tlen)
351 {
352 struct hidkbd *kbd = vsc;
353 uint8_t *pos, *spos, *epos, xlat;
354
355 spos = ibuf + kbd->sc_keycodeloc.pos / 8;
356 epos = spos + kbd->sc_nkeycode;
357
358 for (pos = spos; pos != epos; pos++) {
359 xlat = hidkbd_translate(trans, tlen, *pos);
360 if (xlat != 0)
361 *pos = xlat;
362 }
363 }
364
365 void
366 hidkbd_apple_munge(void *vsc, uint8_t *ibuf, u_int ilen)
367 {
368 struct hidkbd *kbd = vsc;
369
370 if (!hid_get_data(ibuf, ilen, &kbd->sc_fn))
371 return;
372
373 hidkbd_apple_translate(vsc, ibuf, ilen, apple_fn_trans,
374 nitems(apple_fn_trans));
375 }
376
377 void
378 hidkbd_apple_tb_munge(void *vsc, uint8_t *ibuf, u_int ilen)
379 {
380 struct hidkbd *kbd = vsc;
381
382 if (!hid_get_data(ibuf, ilen, &kbd->sc_fn))
383 return;
384
385 hidkbd_apple_munge(vsc, ibuf, ilen);
386
387 hidkbd_apple_translate(vsc, ibuf, ilen, apple_tb_trans,
388 nitems(apple_tb_trans));
389 }
390
391 void
392 hidkbd_apple_iso_munge(void *vsc, uint8_t *ibuf, u_int ilen)
393 {
394 hidkbd_apple_translate(vsc, ibuf, ilen, apple_iso_trans,
395 nitems(apple_iso_trans));
396 hidkbd_apple_munge(vsc, ibuf, ilen);
397 }
398
399 void
400 hidkbd_apple_mba_munge(void *vsc, uint8_t *ibuf, u_int ilen)
401 {
402 struct hidkbd *kbd = vsc;
403
404 if (!hid_get_data(ibuf, ilen, &kbd->sc_fn))
405 return;
406
407 hidkbd_apple_translate(vsc, ibuf, ilen, apple_mba_trans,
408 nitems(apple_mba_trans));
409 }
410
411 void
412 hidkbd_apple_iso_mba_munge(void *vsc, uint8_t *ibuf, u_int ilen)
413 {
414 hidkbd_apple_translate(vsc, ibuf, ilen, apple_iso_trans,
415 nitems(apple_iso_trans));
416 hidkbd_apple_mba_munge(vsc, ibuf, ilen);
417 }
418 #endif
419
420 void
421 hidkbd_input(struct hidkbd *kbd, uint8_t *data, u_int len)
422 {
423 struct hidkbd_data *ud = &kbd->sc_ndata;
424 int i;
425
426 if (kbd->sc_munge != NULL)
427 (*kbd->sc_munge)(kbd, (uint8_t *)data, len);
428
429 #ifdef HIDKBD_DEBUG
430 if (hidkbddebug > 5) {
431 printf("hidkbd_input: data");
432 for (i = 0; i < len; i++)
433 printf(" 0x%02x", data[i]);
434 printf("\n");
435 }
436 #endif
437
438 /* extract variable keys */
439 for (i = 0; i < kbd->sc_nvar; i++)
440 #ifdef __OpenBSD__
441 ud->var[i] = (u_int8_t)hid_get_data(data, len,
442 &kbd->sc_var[i].loc);
443 #else
444 ud->var[i] = (u_int8_t)hid_get_data(data,
445 &kbd->sc_var[i].loc);
446 #endif
447
448 /* extract keycodes */
449 if (kbd->sc_keycodeloc.pos / 8 + kbd->sc_nkeycode <= len)
450 memcpy(ud->keycode, data + kbd->sc_keycodeloc.pos / 8,
451 kbd->sc_nkeycode);
452 else
453 memset(ud->keycode, 0, kbd->sc_nkeycode);
454
455 if (kbd->sc_debounce && !kbd->sc_polling) {
456 /*
457 * Some keyboards have a peculiar quirk. They sometimes
458 * generate a key up followed by a key down for the same
459 * key after about 10 ms.
460 * We avoid this bug by holding off decoding for 20 ms.
461 */
462 kbd->sc_data = *ud;
463 callout_schedule(&kbd->sc_delay, mstohz(20));
464 } else {
465 hidkbd_decode(kbd, ud);
466 }
467 }
468
469 void
470 hidkbd_delayed_decode(void *addr)
471 {
472 struct hidkbd *kbd = addr;
473
474 hidkbd_decode(kbd, &kbd->sc_data);
475 }
476
477 void
478 hidkbd_decode(struct hidkbd *kbd, struct hidkbd_data *ud)
479 {
480 u_int16_t ibuf[MAXKEYS]; /* chars events */
481 int s;
482 int nkeys, i, j;
483 int key;
484 #define ADDKEY(c) ibuf[nkeys++] = (c)
485
486 #ifdef HIDKBD_DEBUG
487 /*
488 * Keep a trace of the last events. Using printf changes the
489 * timing, so this can be useful sometimes.
490 */
491 if (hidkbdtrace) {
492 struct hidkbdtraceinfo *p = &hidkbdtracedata[hidkbdtraceindex];
493 p->unit = device_unit(kbd->sc_device);
494 microtime(&p->tv);
495 p->ud = *ud;
496 if (++hidkbdtraceindex >= HIDKBDTRACESIZE)
497 hidkbdtraceindex = 0;
498 }
499 if (hidkbddebug > 5) {
500 struct timeval tv;
501 microtime(&tv);
502 DPRINTF((" at %lld.%06ld key0=0x%02x key1=0x%02x "
503 "key2=0x%02x key3=0x%02x\n",
504 (long long)tv.tv_sec, tv.tv_usec,
505 ud->keycode[0], ud->keycode[1],
506 ud->keycode[2], ud->keycode[3]));
507 }
508 #endif
509
510 if (ud->keycode[0] == KEY_ERROR) {
511 DPRINTF(("hidkbd_input: KEY_ERROR\n"));
512 return; /* ignore */
513 }
514 nkeys = 0;
515
516 for (i = 0; i < kbd->sc_nvar; i++)
517 if ((kbd->sc_odata.var[i] & kbd->sc_var[i].mask) !=
518 (ud->var[i] & kbd->sc_var[i].mask)) {
519 ADDKEY(kbd->sc_var[i].key |
520 ((ud->var[i] & kbd->sc_var[i].mask) ?
521 PRESS : RELEASE));
522 }
523
524 if (memcmp(ud->keycode, kbd->sc_odata.keycode, kbd->sc_nkeycode) != 0) {
525 /* Check for released keys. */
526 for (i = 0; i < kbd->sc_nkeycode; i++) {
527 key = kbd->sc_odata.keycode[i];
528 if (key == 0)
529 continue;
530 for (j = 0; j < kbd->sc_nkeycode; j++)
531 if (key == ud->keycode[j])
532 goto rfound;
533 DPRINTFN(3,("hidkbd_decode: relse key=0x%02x\n", key));
534 ADDKEY(key | RELEASE);
535 rfound:
536 ;
537 }
538
539 /* Check for pressed keys. */
540 for (i = 0; i < kbd->sc_nkeycode; i++) {
541 key = ud->keycode[i];
542 if (key == 0)
543 continue;
544 for (j = 0; j < kbd->sc_nkeycode; j++)
545 if (key == kbd->sc_odata.keycode[j])
546 goto pfound;
547 DPRINTFN(2,("hidkbd_decode: press key=0x%02x\n", key));
548 ADDKEY(key | PRESS);
549 pfound:
550 ;
551 }
552 }
553 kbd->sc_odata = *ud;
554
555 if (nkeys == 0)
556 return;
557
558 if (kbd->sc_polling) {
559 DPRINTFN(1,("hidkbd_decode: pollchar = 0x%03x\n", ibuf[0]));
560 memcpy(kbd->sc_pollchars, ibuf, nkeys * sizeof(u_int16_t));
561 kbd->sc_npollchar = nkeys;
562 return;
563 }
564
565 if (kbd->sc_wskbddev == NULL)
566 return;
567
568 #ifdef WSDISPLAY_COMPAT_RAWKBD
569 if (kbd->sc_rawkbd) {
570 u_char cbuf[MAXKEYS * 2];
571 int c;
572
573 for (i = j = 0; i < nkeys; i++) {
574 key = ibuf[i];
575 c = hidkbd_trtab[key & CODEMASK];
576 if (c == NN)
577 continue;
578 if (c & 0x80)
579 cbuf[j++] = 0xe0;
580 cbuf[j] = c & 0x7f;
581 if (key & RELEASE)
582 cbuf[j] |= 0x80;
583 DPRINTFN(1,("hidkbd_decode: raw = %s0x%02x\n",
584 c & 0x80 ? "0xe0 " : "",
585 cbuf[j]));
586 j++;
587 }
588 s = spltty();
589 wskbd_rawinput(kbd->sc_wskbddev, cbuf, j);
590
591 /*
592 * Pass audio, brightness and sleep keys to wskbd_input anyway.
593 */
594 for (i = 0; i < nkeys; i++) {
595 key = ibuf[i];
596 switch (key & CODEMASK) {
597 case 102:
598 case 127:
599 case 128:
600 case 129:
601 case 232:
602 case 233:
603 case 234:
604 case 235:
605 case 236:
606 wskbd_input(kbd->sc_wskbddev,
607 key & RELEASE ? WSCONS_EVENT_KEY_UP :
608 WSCONS_EVENT_KEY_DOWN, key & CODEMASK);
609 break;
610 }
611 }
612 splx(s);
613
614 return;
615 }
616 #endif
617
618 s = spltty();
619 for (i = 0; i < nkeys; i++) {
620 key = ibuf[i];
621 wskbd_input(kbd->sc_wskbddev,
622 key&RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN,
623 key&CODEMASK);
624 }
625 splx(s);
626 #undef ADDKEY
627 }
628
629 int
630 hidkbd_enable(struct hidkbd *kbd, int on)
631 {
632 if (kbd->sc_enabled == on)
633 return EBUSY;
634
635 kbd->sc_enabled = on;
636 return 0;
637 }
638
639 int
640 hidkbd_set_leds(struct hidkbd *kbd, int leds, uint8_t *report)
641 {
642 if (kbd->sc_leds == leds)
643 return 0;
644
645 kbd->sc_leds = leds;
646
647 /*
648 * This is not totally correct, since we did not check the
649 * report size from the descriptor but for keyboards it should
650 * just be a single byte with the relevant bits set.
651 */
652 *report = 0;
653 if ((leds & WSKBD_LED_SCROLL) && kbd->sc_scroloc.size == 1)
654 *report |= 1 << kbd->sc_scroloc.pos;
655 if ((leds & WSKBD_LED_NUM) && kbd->sc_numloc.size == 1)
656 *report |= 1 << kbd->sc_numloc.pos;
657 if ((leds & WSKBD_LED_CAPS) && kbd->sc_capsloc.size == 1)
658 *report |= 1 << kbd->sc_capsloc.pos;
659 if ((leds & WSKBD_LED_COMPOSE) && kbd->sc_compose.size == 1)
660 *report |= 1 << kbd->sc_compose.pos;
661
662 return 1;
663 }
664
665 int
666 hidkbd_ioctl(struct hidkbd *kbd, u_long cmd, void *data, int flag,
667 lwp_t *l)
668 {
669 switch (cmd) {
670 case WSKBDIO_GETLEDS:
671 *(int *)data = kbd->sc_leds;
672 return (0);
673 #ifdef WSDISPLAY_COMPAT_RAWKBD
674 case WSKBDIO_SETMODE:
675 DPRINTF(("hidkbd_ioctl: set raw = %d\n", *(int *)data));
676 kbd->sc_rawkbd = *(int *)data == WSKBD_RAW;
677 return (0);
678 #endif
679 }
680
681 return (EPASSTHROUGH);
682 }
683
684 void
685 hidkbd_cngetc(struct hidkbd *kbd, u_int *type, int *data)
686 {
687 int c;
688
689 c = kbd->sc_pollchars[0];
690 kbd->sc_npollchar--;
691 memmove(kbd->sc_pollchars, kbd->sc_pollchars+1,
692 kbd->sc_npollchar * sizeof(u_int16_t));
693 *type = c & RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
694 *data = c & CODEMASK;
695 }
696
697 const char *
698 hidkbd_parse_desc(struct hidkbd *kbd, int id, void *desc, int dlen)
699 {
700 struct hid_data *d;
701 struct hid_item h;
702 unsigned int i, ivar = 0;
703
704 kbd->sc_nkeycode = 0;
705
706 d = hid_start_parse(desc, dlen, hid_input);
707 while (hid_get_item(d, &h)) {
708 if (h.kind != hid_input || (h.flags & HIO_CONST) ||
709 HID_GET_USAGE_PAGE(h.usage) != HUP_KEYBOARD ||
710 h.report_ID != id)
711 continue;
712 if (h.flags & HIO_VARIABLE)
713 ivar++;
714 }
715 hid_end_parse(d);
716
717 if (ivar > MAXVARS) {
718 DPRINTF((": too many variable keys\n"));
719 ivar = MAXVARS;
720 }
721
722 kbd->sc_nvar = ivar;
723 kbd->sc_var = kmem_zalloc(
724 kbd->sc_nvar * sizeof(struct hidkbd_variable),
725 KM_SLEEP);
726
727 i = 0;
728
729 d = hid_start_parse(desc, dlen, hid_input);
730 while (hid_get_item(d, &h)) {
731 if (h.kind != hid_input || (h.flags & HIO_CONST) ||
732 HID_GET_USAGE_PAGE(h.usage) != HUP_KEYBOARD ||
733 h.report_ID != id)
734 continue;
735
736 DPRINTF(("hidkbd: usage=0x%x flags=0x%x pos=%d size=%d cnt=%d",
737 h.usage, h.flags, h.loc.pos, h.loc.size, h.loc.count));
738 if (h.flags & HIO_VARIABLE) {
739 /* variable reports should be one bit each */
740 if (h.loc.size != 1) {
741 DPRINTF((": bad variable size\n"));
742 continue;
743 }
744
745 /* variable report */
746 if (i < MAXVARS) {
747 kbd->sc_var[i].loc = h.loc;
748 kbd->sc_var[i].mask = 1 << (i % 8);
749 kbd->sc_var[i].key = HID_GET_USAGE(h.usage);
750 i++;
751 }
752 } else {
753 /* keys array should be in bytes, on a byte boundary */
754 if (h.loc.size != 8) {
755 DPRINTF((": key code size != 8\n"));
756 continue;
757 }
758 if (h.loc.pos % 8 != 0) {
759 DPRINTF((": array not on byte boundary"));
760 continue;
761 }
762 if (kbd->sc_nkeycode != 0) {
763 DPRINTF((": ignoring multiple arrays\n"));
764 continue;
765 }
766 kbd->sc_keycodeloc = h.loc;
767 if (h.loc.count > MAXKEYCODE) {
768 DPRINTF((": ignoring extra key codes"));
769 kbd->sc_nkeycode = MAXKEYCODE;
770 } else
771 kbd->sc_nkeycode = h.loc.count;
772 }
773 DPRINTF(("\n"));
774 }
775 hid_end_parse(d);
776
777 /* don't attach if no keys... */
778 if (kbd->sc_nkeycode == 0 && ivar == 0)
779 return "no usable key codes array";
780
781 hid_locate(desc, dlen, HID_USAGE2(HUP_LEDS, HUD_LED_NUM_LOCK),
782 id, hid_output, &kbd->sc_numloc, NULL);
783 hid_locate(desc, dlen, HID_USAGE2(HUP_LEDS, HUD_LED_CAPS_LOCK),
784 id, hid_output, &kbd->sc_capsloc, NULL);
785 hid_locate(desc, dlen, HID_USAGE2(HUP_LEDS, HUD_LED_SCROLL_LOCK),
786 id, hid_output, &kbd->sc_scroloc, NULL);
787 hid_locate(desc, dlen, HID_USAGE2(HUP_LEDS, HUD_LED_COMPOSE),
788 id, hid_output, &kbd->sc_compose, NULL);
789
790 return (NULL);
791 }
792