On Tue, Jul 03, 2012 at 03:23:16PM -0700, Mike Larkin wrote: > Many low-cost USB keyboards have a limit of either 3 or 6 simultaneous > keypresses before they wedge and stop supplying any more keypress events > (at least until you release one of the pressed keys). > > Some newer (usually called "gaming") keyboards use a different way of > reporting keypress events in order to support an arbitrary number of > simultaneous keypresses (likely under the assumption that in certain > games, you might need more than 3 or 6 keys pressed at the same time). > > Our USB HID keyboard code had some bad assumptions in it - namely, that > every key reported in this fashion was a 'modifier' key like shift, > alt, or control, and that there would only be 8 such keys maximum. > > The diff below reworks the HID keyboard code a bit to remove these > assumptions and support these newer style USB keyboards. It also > changes the term 'modifier' to 'variable' which is a more proper > description of these keys (as per the HID spec), in various > comments and printfs. > > If you have a USB keyboard (whether or not it is a "gaming" variety), > please test this diff to ensure that it does not break you. I've tested > on i386 and amd64 on a variety of keyboards and haven't seen any > problems, so it's time to turn it loose to a wider audience. > > -ml This keyboard is still working fine:
uhidev2 at uhub2 port 1 configuration 1 interface 0 "Microsoft Natural\M-. Ergonomic Keyboard 4000" rev 2.00/1.73 addr 2 uhidev2: iclass 3/1 ukbd1 at uhidev2: 8 variable keys, 6 key codes wskbd2 at ukbd1 mux 1 wskbd2: connecting to wsdisplay0 uhidev3 at uhub2 port 1 configuration 1 interface 1 "Microsoft Natural\M-. Ergonomic Keyboard 4000" rev 2.00/1.73 addr 2 uhidev3: iclass 3/0, 1 report id uhid0 at uhidev3 reportid 1: input=7, output=0, feature=0 Although it's still suffering from the fact I need to detach/attach the cables upon every boot/suspend otherwise it just sits there playing dead. But that's unrelated to this diff. > Index: hidkbd.c > =================================================================== > RCS file: /cvs/src/sys/dev/usb/hidkbd.c,v > retrieving revision 1.5 > diff -a -u -r1.5 hidkbd.c > --- hidkbd.c 9 Nov 2011 14:22:38 -0000 1.5 > +++ hidkbd.c 29 Jun 2012 06:45:59 -0000 > @@ -41,6 +41,7 @@ > #include <sys/kernel.h> > #include <sys/device.h> > #include <sys/ioctl.h> > +#include <sys/malloc.h> > > #include <dev/usb/usb.h> > #include <dev/usb/usbhid.h> > @@ -164,6 +165,8 @@ > { > const char *parserr; > > + kbd->sc_var = NULL; > + > parserr = hidkbd_parse_desc(kbd, id, desc, dlen); > if (parserr != NULL) { > printf(": %s\n", parserr); > @@ -171,8 +174,8 @@ > } > > #ifdef DIAGNOSTIC > - printf(": %d modifier keys, %d key codes", > - kbd->sc_nmod, kbd->sc_nkeycode); > + printf(": %d variable keys, %d key codes", > + kbd->sc_nvar, kbd->sc_nkeycode); > #endif > > kbd->sc_device = self; > @@ -245,6 +248,9 @@ > if (kbd->sc_wskbddev != NULL) > rv = config_detach(kbd->sc_wskbddev, flags); > > + if (kbd->sc_var != NULL) > + free(kbd->sc_var, M_DEVBUF); > + > return (rv); > } > > @@ -263,11 +269,9 @@ > } > #endif > > - /* extract key modifiers */ > - ud->modifiers = 0; > - for (i = 0; i < kbd->sc_nmod; i++) > - if (hid_get_data(data, &kbd->sc_modloc[i])) > - ud->modifiers |= kbd->sc_mods[i].mask; > + /* extract variable keys */ > + for (i = 0; i < kbd->sc_nvar; i++) > + ud->var[i] = (u_int8_t)hid_get_data(data, &kbd->sc_var[i].loc); > > /* extract keycodes */ > memcpy(ud->keycode, data + kbd->sc_keycodeloc.pos / 8, > @@ -311,7 +315,6 @@ > void > hidkbd_decode(struct hidkbd *kbd, struct hidkbd_data *ud) > { > - uint32_t mod, omod; > u_int16_t ibuf[MAXKEYS]; /* chars events */ > int s; > int nkeys, i, j; > @@ -347,15 +350,15 @@ > return; /* ignore */ > } > nkeys = 0; > - mod = ud->modifiers; > - omod = kbd->sc_odata.modifiers; > - if (mod != omod) > - for (i = 0; i < kbd->sc_nmod; i++) > - if (( mod & kbd->sc_mods[i].mask) != > - (omod & kbd->sc_mods[i].mask)) > - ADDKEY(kbd->sc_mods[i].key | > - (mod & kbd->sc_mods[i].mask > - ? PRESS : RELEASE)); > + > + for (i = 0; i < kbd->sc_nvar; i++) > + if ((kbd->sc_odata.var[i] & kbd->sc_var[i].mask) != > + (ud->var[i] & kbd->sc_var[i].mask)) { > + ADDKEY(kbd->sc_var[i].key | > + ((ud->var[i] & kbd->sc_var[i].mask) ? > + PRESS : RELEASE)); > + } > + > if (memcmp(ud->keycode, kbd->sc_odata.keycode, kbd->sc_nkeycode) != 0) { > /* Check for released keys. */ > for (i = 0; i < kbd->sc_nkeycode; i++) { > @@ -547,10 +550,36 @@ > { > struct hid_data *d; > struct hid_item h; > - int imod; > + int i, ivar = 0; > > - imod = 0; > kbd->sc_nkeycode = 0; > + > + d = hid_start_parse(desc, dlen, hid_input); > + while (hid_get_item(d, &h)) { > + if (h.kind != hid_input || (h.flags & HIO_CONST) || > + HID_GET_USAGE_PAGE(h.usage) != HUP_KEYBOARD || > + h.report_ID != id) > + continue; > + if (h.flags & HIO_VARIABLE) > + ivar++; > + } > + hid_end_parse(d); > + > + if (ivar > MAXVARS) { > + DPRINTF((": too many variable keys\n")); > + ivar = MAXVARS; > + } > + > + kbd->sc_nvar = ivar; > + kbd->sc_var = (struct hidkbd_variable *)malloc( > + sizeof(struct hidkbd_variable) * ivar, M_DEVBUF, > + M_NOWAIT); > + > + if (!kbd->sc_var) > + return NULL; > + > + i = 0; > + > d = hid_start_parse(desc, dlen, hid_input); > while (hid_get_item(d, &h)) { > if (h.kind != hid_input || (h.flags & HIO_CONST) || > @@ -562,20 +591,18 @@ > "cnt=%d", imod, > h.usage, h.flags, h.loc.pos, h.loc.size, h.loc.count)); > if (h.flags & HIO_VARIABLE) { > - /* modifier reports should be one bit each */ > + /* variable reports should be one bit each */ > if (h.loc.size != 1) { > - DPRINTF((": bad modifier size\n")); > + DPRINTF((": bad variable size\n")); > continue; > } > - /* single item */ > - if (imod < MAXMOD) { > - kbd->sc_modloc[imod] = h.loc; > - kbd->sc_mods[imod].mask = 1 << imod; > - kbd->sc_mods[imod].key = HID_GET_USAGE(h.usage); > - imod++; > - } else { > - /* ignore extra modifiers */ > - DPRINTF((": too many modifier keys\n")); > + > + /* variable report */ > + if (ivar < MAXVARS) { > + kbd->sc_var[i].loc = h.loc; > + kbd->sc_var[i].mask = 1 << (i % 8); > + kbd->sc_var[i].key = HID_GET_USAGE(h.usage); > + i++; > } > } else { > /* keys array should be in bytes, on a byte boundary */ > @@ -600,11 +627,10 @@ > } > DPRINTF(("\n")); > } > - kbd->sc_nmod = imod; > hid_end_parse(d); > > /* don't attach if no keys... */ > - if (kbd->sc_nkeycode == 0) > + if (kbd->sc_nkeycode == 0 && ivar == 0) > return "no usable key codes array"; > > hid_locate(desc, dlen, HID_USAGE2(HUP_LEDS, HUD_LED_NUM_LOCK), > Index: hidkbdsc.h > =================================================================== > RCS file: /cvs/src/sys/dev/usb/hidkbdsc.h,v > retrieving revision 1.2 > diff -a -u -r1.2 hidkbdsc.h > --- hidkbdsc.h 9 Nov 2011 14:22:38 -0000 1.2 > +++ hidkbdsc.h 29 Jun 2012 06:39:37 -0000 > @@ -32,13 +32,19 @@ > */ > > #define MAXKEYCODE 6 > -#define MAXMOD 8 /* max 32 */ > +#define MAXVARS 128 > > -#define MAXKEYS (MAXMOD+2*MAXKEYCODE) > +#define MAXKEYS (MAXVARS+2*MAXKEYCODE) > + > +struct hidkbd_variable { > + struct hid_location loc; > + u_int8_t mask; > + u_int8_t key; > +}; > > struct hidkbd_data { > - u_int32_t modifiers; > u_int8_t keycode[MAXKEYCODE]; > + u_int8_t var[MAXVARS]; > }; > > struct hidkbd { > @@ -47,12 +53,8 @@ > struct hidkbd_data sc_odata; > > /* input reports */ > - struct hid_location sc_modloc[MAXMOD]; > - u_int sc_nmod; > - struct { > - u_int32_t mask; > - u_int8_t key; > - } sc_mods[MAXMOD]; > + u_int sc_nvar; > + struct hidkbd_variable *sc_var; > > struct hid_location sc_keycodeloc; > u_int sc_nkeycode; > -- Cheers, Jasper "Stay Hungry. Stay Foolish"