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:
parent
8780d8c54a
commit
0d0313ba43
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user