Support more varieties of USB keyboard reports.

The previous code asssumed reports would closely match the Bootstrap
Keyboard Protocol.  This is no longer always the case, particularly
with higher-end keyboards.
This commit is contained in:
jakllsch 2017-08-13 22:19:56 +00:00
parent 8780d8c54a
commit 0d0313ba43

View File

@ -1,4 +1,4 @@
/* $NetBSD: ukbd.c,v 1.137 2017/08/13 21:11:45 jakllsch Exp $ */
/* $NetBSD: ukbd.c,v 1.138 2017/08/13 22:19:56 jakllsch Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ukbd.c,v 1.137 2017/08/13 21:11:45 jakllsch Exp $");
__KERNEL_RCSID(0, "$NetBSD: ukbd.c,v 1.138 2017/08/13 22:19:56 jakllsch Exp $");
#ifdef _KERNEL_OPT
#include "opt_ddb.h"
@ -83,12 +83,11 @@ int ukbddebug = 0;
#define DPRINTFN(n,x)
#endif
#define MAXKEYCODE 6
#define MAXMOD 8 /* max 32 */
#define MAXKEYCODE 32
#define MAXKEYS 256
struct ukbd_data {
uint32_t modifiers;
uint8_t keycode[MAXKEYCODE];
uint8_t keys[MAXKEYS/NBBY];
};
#define PRESS 0x000
@ -234,19 +233,14 @@ Static const uint8_t ukbd_trtab[256] = {
#define KEY_ERROR 0x01
#define MAXKEYS (MAXMOD+2*MAXKEYCODE)
struct ukbd_softc {
struct uhidev sc_hdev;
struct ukbd_data sc_ndata;
struct ukbd_data sc_odata;
struct hid_location sc_modloc[MAXMOD];
u_int sc_nmod;
struct {
uint32_t mask;
uint8_t key;
} sc_mods[MAXMOD];
struct hid_location sc_keyloc[MAXKEYS];
uint8_t sc_keyuse[MAXKEYS];
u_int sc_nkeyloc;
struct hid_location sc_keycodeloc;
u_int sc_nkeycode;
@ -307,15 +301,17 @@ void ukbdtracedump(void);
void
ukbdtracedump(void)
{
int i;
size_t i, j;
for (i = 0; i < UKBDTRACESIZE; i++) {
struct ukbdtraceinfo *p =
&ukbdtracedata[(i+ukbdtraceindex)%UKBDTRACESIZE];
printf("%"PRIu64".%06"PRIu64": mod=0x%02x key0=0x%02x key1=0x%02x "
"key2=0x%02x key3=0x%02x\n",
p->tv.tv_sec, (uint64_t)p->tv.tv_usec,
p->ud.modifiers, p->ud.keycode[0], p->ud.keycode[1],
p->ud.keycode[2], p->ud.keycode[3]);
printf("%"PRIu64".%06"PRIu64":", p->tv.tv_sec,
(uint64_t)p->tv.tv_usec);
for (j = 0; j < MAXKEYS; j++) {
if (isset(p->ud.keys, j))
printf(" %zu", j);
}
printf(".\n");
}
}
#endif
@ -438,7 +434,7 @@ ukbd_attach(device_t parent, device_t self, void *aux)
#endif
#ifdef DIAGNOSTIC
aprint_normal(": %d modifier keys, %d key codes", sc->sc_nmod,
aprint_normal(": %d Variable keys, %d Array codes", sc->sc_nkeyloc,
sc->sc_nkeycode);
if (sc->sc_flags & FLAG_APPLE_FN)
aprint_normal(", apple fn key");
@ -595,21 +591,22 @@ ukbd_translate_keycodes(struct ukbd_softc *sc, struct ukbd_data *ud,
const struct ukbd_keycodetrans *tab)
{
const struct ukbd_keycodetrans *tp;
struct ukbd_data oud;
int i;
uint8_t key;
for (i = 0; i < sc->sc_nkeycode; i++) {
key = ud->keycode[i];
if (key)
oud = *ud;
for (i = 4; i < MAXKEYS; i++) {
if (isset(oud.keys, i))
for (tp = tab; tp->from; tp++)
if (tp->from == key) {
if (tp->from == i) {
if (tp->to & IS_PMF) {
pmf_event_inject(
sc->sc_hdev.sc_dev,
tp->to & 0xff);
ud->keycode[i] = 0;
} else
ud->keycode[i] = tp->to;
setbit(ud->keys, tp->to);
clrbit(ud->keys, i);
break;
}
}
@ -652,12 +649,18 @@ ukbd_intr(struct uhidev *addr, void *ibuf, u_int len)
}
#endif
ud->modifiers = 0;
for (i = 0; i < sc->sc_nmod; i++)
if (hid_get_data(ibuf, &sc->sc_modloc[i]))
ud->modifiers |= sc->sc_mods[i].mask;
memcpy(ud->keycode, (char *)ibuf + sc->sc_keycodeloc.pos / 8,
sc->sc_nkeycode);
memset(ud->keys, 0, sizeof(ud->keys));
for (i = 0; i < sc->sc_nkeyloc; i++)
if (hid_get_data(ibuf, &sc->sc_keyloc[i]))
setbit(ud->keys, sc->sc_keyuse[i]);
const uint8_t * const scancode = (char *)ibuf + sc->sc_keycodeloc.pos / 8;
const uint16_t Keyboard_NoEvent = 0x0000;
for (i = 0; i < sc->sc_nkeycode; i++) {
if (scancode[i] != Keyboard_NoEvent)
setbit(ud->keys, scancode[i]);
}
if (sc->sc_flags & FLAG_APPLE_FN) {
if (hid_get_data(ibuf, &sc->sc_apple_fn)) {
@ -714,10 +717,12 @@ ukbd_delayed_decode(void *addr)
void
ukbd_decode(struct ukbd_softc *sc, struct ukbd_data *ud)
{
int mod, omod;
uint16_t ibuf[MAXKEYS]; /* chars events */
int s;
int nkeys, i, j;
int nkeys, i;
#ifdef WSDISPLAY_COMPAT_RAWKBD
int j;
#endif
int key;
#define ADDKEY(c) do { \
KASSERT(nkeys < MAXKEYS); \
@ -740,15 +745,17 @@ ukbd_decode(struct ukbd_softc *sc, struct ukbd_data *ud)
if (ukbddebug > 5) {
struct timeval tv;
microtime(&tv);
DPRINTF((" at %"PRIu64".%06"PRIu64" mod=0x%02x key0=0x%02x key1=0x%02x "
"key2=0x%02x key3=0x%02x\n",
tv.tv_sec, (uint64_t)tv.tv_usec,
ud->modifiers, ud->keycode[0], ud->keycode[1],
ud->keycode[2], ud->keycode[3]));
DPRINTF((" at %"PRIu64".%06"PRIu64":", tv.tv_sec,
(uint64_t)tv.tv_usec));
for (size_t k = 0; k < MAXKEYS; k++) {
if (isset(ud->keys, k))
DPRINTF((" %zu", k));
}
DPRINTF((".\n"));
}
#endif
if (ud->keycode[0] == KEY_ERROR) {
if (isset(ud->keys, KEY_ERROR)) {
DPRINTF(("ukbd_intr: KEY_ERROR\n"));
return; /* ignore */
}
@ -757,61 +764,19 @@ ukbd_decode(struct ukbd_softc *sc, struct ukbd_data *ud)
ukbd_translate_keycodes(sc, ud, trtab_apple_iso);
nkeys = 0;
mod = ud->modifiers;
omod = sc->sc_odata.modifiers;
if (mod != omod)
for (i = 0; i < sc->sc_nmod; i++)
if (( mod & sc->sc_mods[i].mask) !=
(omod & sc->sc_mods[i].mask)) {
key = sc->sc_mods[i].key |
((mod & sc->sc_mods[i].mask) ?
PRESS : RELEASE);
for (i = 0; i < MAXKEYS; i++) {
#ifdef GDIUM_KEYBOARD_HACK
if (sc->sc_flags & FLAG_GDIUM_FN && i == 0x82) {
if (isset(ud->keys, i))
sc->sc_flags |= FLAG_FN_PRESSED;
else
sc->sc_flags &= ~FLAG_FN_PRESSED;
}
#endif
if (isset(ud->keys, i) != isset(sc->sc_odata.keys, i)) {
key = i | ((isset(ud->keys, i) ? PRESS : RELEASE));
ADDKEY(ukbd_translate_modifier(sc, key));
}
if (memcmp(ud->keycode, sc->sc_odata.keycode, sc->sc_nkeycode) != 0) {
/* Check for released keys. */
for (i = 0; i < sc->sc_nkeycode; i++) {
key = sc->sc_odata.keycode[i];
if (key == 0)
continue;
for (j = 0; j < sc->sc_nkeycode; j++)
if (key == ud->keycode[j])
goto rfound;
DPRINTFN(3,("ukbd_intr: relse key=0x%02x\n", key));
#ifdef GDIUM_KEYBOARD_HACK
if (sc->sc_flags & FLAG_GDIUM_FN) {
if (key == 0x82) {
sc->sc_flags &= ~FLAG_FN_PRESSED;
goto rfound;
}
}
#endif
ADDKEY(key | RELEASE);
rfound:
;
}
/* Check for pressed keys. */
for (i = 0; i < sc->sc_nkeycode; i++) {
key = ud->keycode[i];
if (key == 0)
continue;
for (j = 0; j < sc->sc_nkeycode; j++)
if (key == sc->sc_odata.keycode[j])
goto pfound;
DPRINTFN(2,("ukbd_intr: press key=0x%02x\n", key));
#ifdef GDIUM_KEYBOARD_HACK
if (sc->sc_flags & FLAG_GDIUM_FN) {
if (key == 0x82) {
sc->sc_flags |= FLAG_FN_PRESSED;
goto pfound;
}
}
#endif
ADDKEY(key | PRESS);
pfound:
;
}
}
sc->sc_odata = *ud;
@ -1052,10 +1017,10 @@ ukbd_parse_desc(struct ukbd_softc *sc)
struct hid_item h;
int size;
void *desc;
int imod;
int ikey;
uhidev_get_report_desc(sc->sc_hdev.sc_parent, &desc, &size);
imod = 0;
ikey = 0;
sc->sc_nkeycode = 0;
d = hid_start_parse(desc, size, hid_input);
while (hid_get_item(d, &h)) {
@ -1074,8 +1039,8 @@ ukbd_parse_desc(struct ukbd_softc *sc)
HID_GET_USAGE_PAGE(h.usage) != HUP_KEYBOARD ||
h.report_ID != sc->sc_hdev.sc_report_id)
continue;
DPRINTF(("ukbd: imod=%d usage=0x%x flags=0x%x pos=%d size=%d "
"cnt=%d\n", imod,
DPRINTF(("ukbd: ikey=%d usage=0x%x flags=0x%x pos=%d size=%d "
"cnt=%d\n", ikey,
h.usage, h.flags, h.loc.pos, h.loc.size, h.loc.count));
if (h.flags & HIO_VARIABLE) {
if (h.loc.size != 1) {
@ -1083,14 +1048,13 @@ ukbd_parse_desc(struct ukbd_softc *sc)
return "bad modifier size";
}
/* Single item */
if (imod < MAXMOD) {
sc->sc_modloc[imod] = h.loc;
sc->sc_mods[imod].mask = 1 << imod;
sc->sc_mods[imod].key = HID_GET_USAGE(h.usage);
imod++;
if (ikey < MAXKEYS) {
sc->sc_keyloc[ikey] = h.loc;
sc->sc_keyuse[ikey] = HID_GET_USAGE(h.usage);
ikey++;
} else {
hid_end_parse(d);
return "too many modifier keys";
return "too many Variable keys";
}
} else {
/* Array */
@ -1112,7 +1076,7 @@ ukbd_parse_desc(struct ukbd_softc *sc)
sc->sc_nkeycode = h.loc.count;
}
}
sc->sc_nmod = imod;
sc->sc_nkeyloc = ikey;
hid_end_parse(d);
hid_locate(desc, size, HID_USAGE2(HUP_LEDS, HUD_LED_NUM_LOCK),