Allow HID report descriptor parser to return more then 1 usage per item

This handles parsing of following descriptor, containing array of
usages:

0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x80,        // Usage (Sys Control)
0xA1, 0x01,        // Collection (Application)
0x75, 0x02,        //   Report Size (2)
0x95, 0x01,        //   Report Count (1)
0x15, 0x01,        //   Logical Minimum (1)
0x25, 0x03,        //   Logical Maximum (3)
0x09, 0x82,        //   Usage (Sys Sleep)
0x09, 0x81,        //   Usage (Sys Power Down)
0x09, 0x83,        //   Usage (Sys Wake Up)
0x81, 0x60,        //   Input (Data,Array,Abs)
0x75, 0x06,        //   Report Size (6)
0x81, 0x03,        //   Input (Const,Var,Abs)
0xC0,              // End Collection

Our current parser returns only first usage (Sys Sleep) and loses next
two. Set HID_ITEM_MAXUSAGE limit relatively low as existing code
usually allocates hid_item on stack.

Also tweak hid_locate() to support hid items with multiple usages.

Reviewed by:	hselasky
Differential Revision:	https://reviews.freebsd.org/D27748
This commit is contained in:
Vladimir Kondratyev 2020-12-24 14:46:24 +03:00 committed by Vladimir Kondratyev
parent 3f27092854
commit 95e1f0d684
2 changed files with 34 additions and 13 deletions

View File

@ -111,7 +111,8 @@ hid_clear_local(struct hid_item *c)
c->loc.count = 0;
c->loc.size = 0;
c->usage = 0;
c->nusages = 0;
memset(c->usages, 0, sizeof(c->usages));
c->usage_minimum = 0;
c->usage_maximum = 0;
c->designator_index = 0;
@ -273,6 +274,16 @@ hid_get_item(struct hid_data *s, struct hid_item *h)
DPRINTFN(1, "Using last usage\n");
dval = s->usage_last;
}
c->nusages = 1;
/* array type HID item may have multiple usages */
while ((c->flags & HIO_VARIABLE) == 0 && s->ousage == 0 &&
s->iusage < s->nusage && c->nusages < HID_ITEM_MAXUSAGE)
c->usages[c->nusages++] = s->usages_min[s->iusage++];
if ((c->flags & HIO_VARIABLE) == 0 && s->ousage == 0 &&
s->iusage < s->nusage)
DPRINTFN(0, "HID_ITEM_MAXUSAGE should be increased "
"up to %hhu to parse the HID report descriptor\n",
s->nusage);
s->icount ++;
/*
* Only copy HID item, increment position and return
@ -381,6 +392,7 @@ hid_get_item(struct hid_data *s, struct hid_item *h)
c->collection = dval;
c->collevel++;
c->usage = s->usage_last;
c->nusages = 1;
*h = *c;
return (1);
case 11: /* Feature */
@ -620,19 +632,22 @@ hid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k,
{
struct hid_data *d;
struct hid_item h;
int i;
for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) {
if (h.kind == k && h.usage == u) {
if (index--)
continue;
if (loc != NULL)
*loc = h.loc;
if (flags != NULL)
*flags = h.flags;
if (id != NULL)
*id = h.report_ID;
hid_end_parse(d);
return (1);
for (i = 0; i < h.nusages; i++) {
if (h.kind == k && h.usages[i] == u) {
if (index--)
break;
if (loc != NULL)
*loc = h.loc;
if (flags != NULL)
*flags = h.flags;
if (id != NULL)
*id = h.report_ID;
hid_end_parse(d);
return (1);
}
}
}
if (loc != NULL)

View File

@ -208,6 +208,8 @@ struct usb_hid_descriptor {
#if defined(_KERNEL) || defined(_STANDALONE)
struct usb_config_descriptor;
#define HID_ITEM_MAXUSAGE 4
enum hid_kind {
hid_input, hid_output, hid_feature, hid_collection, hid_endcollection
};
@ -229,7 +231,11 @@ struct hid_item {
int32_t unit;
int32_t report_ID;
/* Local */
int32_t usage;
int nusages;
union {
int32_t usage;
int32_t usages[HID_ITEM_MAXUSAGE];
};
int32_t usage_minimum;
int32_t usage_maximum;
int32_t designator_index;