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"

Reply via email to