libusbhid bug?
Stefan `Sec` Zehl
sec at 42.org
Mon May 14 23:54:26 UTC 2007
Hi,
I've been playing around with a Bluetooth HID device. Bluetooth uses the
same HID format as USB, so libusbhid is used to parse these messages.
While debugging, I've found what I think is a mistake in parsing
"Array"-type input messages:
In my HID descriptor, there is the following snippet:
| 95 06 | Report Count(6)
| 75 08 | Report Size(8)
| 15 00 | Logical Minimum(0)
| 26 a4 00| Logical Maximum(164)
| 05 07 | Usage Page(Key Codes)
| 19 00 | Usage Minimum(0x00)
| 29 a4 | Usage Maximum(0xa4)
| 81 00 | Input(Data,Array,Absolute)
The current usbhid only parses one item of Report_Size (8bit). According
to the spec it should parse Report_Count items:
| An array provides an alternate means for
| describing the data returned from a group of
| buttons. Arrays are more efficient, if less flexible
| than variable items. Rather than returning a single
| bit for each button in the group, an array returns an
| index in each field that corresponds to the pressed
| button (like keyboard scan codes). An out-of range
| value in and array field is considered no controls
| asserted. Buttons or keys in an array that are
| simultaneously pressed need to be reported in
| multiple fields. Therefore, the number of fields in
| an array input item (Report Count) dictates the
| maximum number of simultaneous controls that
| can be reported. A keyboard could report up to
| three simultaneous keys using an array with three
| 8-bit fields (Report Size = 8, Report Count = 3).
I have created a patch which changes that behaviour, and attached it
below. My sample code works fine with these changes, but as I don't have
an USB Keyboard, it would be great if someone could check that this
doesn't break anything.
--- parse.c.org Wed Feb 11 22:09:13 2004
+++ parse.c Tue May 15 01:26:08 2007
@@ -54,6 +54,7 @@
int multimax;
int kindset;
int reportid;
+ int isarray;
/*
* The start of collection item has no report ID set, so save
@@ -100,6 +101,7 @@
s->kindset = kindset;
s->reportid = id;
s->hassavedcoll = 0;
+ s->isarray = 0;
return (s);
}
@@ -182,6 +184,16 @@
hid_clear_local(c);
}
}
+ if(s->isarray) {
+ REPORT_SAVED_COLL;
+ *h = *c;
+ h->next = 0;
+ h->pos = s->kindpos[c->kind];
+ s->kindpos[c->kind] += c->report_size ;
+ if(--s->isarray ==0)
+ hid_clear_local(c);
+ return(1);
+ };
for (;;) {
p = s->p;
if (p >= s->end)
@@ -262,14 +274,9 @@
} else {
if (s->minset)
c->usage = c->usage_minimum;
- *h = *c;
- h->next = 0;
- h->pos = s->kindpos[c->kind];
- s->kindpos[c->kind] +=
- c->report_size * c->report_count;
- hid_clear_local(c);
s->minset = 0;
- return (1);
+ s->isarray=c->report_count;
+ goto top;
}
case 9: /* Output */
retkind = hid_output;
CU,
Sec
--
"The General who in a hundred battles is always victorious is not as
great as the one who achieves his objectives without fighting."
-- Sun Tzu
More information about the freebsd-usb
mailing list