aboutsummaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/usb_hid.c
diff options
context:
space:
mode:
authorAndrew Thompson <thompsa@FreeBSD.org>2009-02-24 03:34:05 +0000
committerAndrew Thompson <thompsa@FreeBSD.org>2009-02-24 03:34:05 +0000
commit3e5e312a28c8d2ea9ccb6b47ec39b616582c3142 (patch)
tree9040787a9ebd2efae390395c1027b740aeeec158 /sys/dev/usb/usb_hid.c
parent811b1001aaeba45d1987307d4a396d6ff9185d2d (diff)
downloadsrc-3e5e312a28c8d2ea9ccb6b47ec39b616582c3142.tar.gz
src-3e5e312a28c8d2ea9ccb6b47ec39b616582c3142.zip
MFp4 //depot/projects/usb; 157814, 157863, 157868
- The software computed HID size is not always correct, because the algoritm does not handle unsorted HID descriptors. - Change the way we obtain the report ID. - Use the X/Y/Z+button locations instead for report ID source for ums. - Add more range checks. - Remove Microsoft Mouse quirks. If the positions are moduloed the report length multiplied by 8, the values seem correct. - Some minor style changes. Submitted by: Hans Petter Selasky
Notes
Notes: svn path=/head/; revision=188981
Diffstat (limited to 'sys/dev/usb/usb_hid.c')
-rw-r--r--sys/dev/usb/usb_hid.c103
1 files changed, 70 insertions, 33 deletions
diff --git a/sys/dev/usb/usb_hid.c b/sys/dev/usb/usb_hid.c
index 2adf208c0a71..75da2495ddd1 100644
--- a/sys/dev/usb/usb_hid.c
+++ b/sys/dev/usb/usb_hid.c
@@ -155,7 +155,7 @@ top:
}
for (;;) {
p = s->p;
- if (p >= s->end)
+ if ((p >= s->end) || (p < s->start))
return (0);
bSize = *p++;
@@ -388,32 +388,53 @@ top:
* hid_report_size
*------------------------------------------------------------------------*/
int
-hid_report_size(const void *buf, int len, enum hid_kind k, uint8_t *idp)
+hid_report_size(const void *buf, int len, enum hid_kind k, uint8_t *id)
{
struct hid_data *d;
struct hid_item h;
- int hi, lo, size, id;
+ uint32_t temp;
+ uint32_t hpos;
+ uint32_t lpos;
+ uint8_t any_id;
- id = 0;
- hi = lo = -1;
- for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);)
+ any_id = 0;
+ hpos = 0;
+ lpos = 0xFFFFFFFF;
+
+ for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) {
if (h.kind == k) {
- if (h.report_ID != 0 && !id)
- id = h.report_ID;
- if (h.report_ID == id) {
- if (lo < 0)
- lo = h.loc.pos;
- hi = h.loc.pos + h.loc.size * h.loc.count;
+ /* check for ID-byte presense */
+ if ((h.report_ID != 0) && !any_id) {
+ if (id != NULL)
+ *id = h.report_ID;
+ any_id = 1;
}
+ /* compute minimum */
+ if (lpos > h.loc.pos)
+ lpos = h.loc.pos;
+ /* compute end position */
+ temp = h.loc.pos + (h.loc.size * h.loc.count);
+ /* compute maximum */
+ if (hpos < temp)
+ hpos = temp;
}
+ }
hid_end_parse(d);
- size = hi - lo;
- if (id != 0) {
- size += 8;
- *idp = id; /* XXX wrong */
- } else
- *idp = 0;
- return ((size + 7) / 8);
+
+ /* safety check - can happen in case of currupt descriptors */
+ if (lpos > hpos)
+ temp = 0;
+ else
+ temp = hpos - lpos;
+
+ /* check for ID byte */
+ if (any_id)
+ temp += 8;
+ else if (id != NULL)
+ *id = 0;
+
+ /* return length in bytes rounded up */
+ return ((temp + 7) / 8);
}
/*------------------------------------------------------------------------*
@@ -421,7 +442,7 @@ hid_report_size(const void *buf, int len, enum hid_kind k, uint8_t *idp)
*------------------------------------------------------------------------*/
int
hid_locate(const void *desc, int size, uint32_t u, enum hid_kind k,
- struct hid_location *loc, uint32_t *flags)
+ struct hid_location *loc, uint32_t *flags, uint8_t *id)
{
struct hid_data *d;
struct hid_item h;
@@ -432,12 +453,19 @@ hid_locate(const void *desc, int size, uint32_t u, enum hid_kind k,
*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)
+ loc->size = 0;
+ if (flags != NULL)
+ *flags = 0;
+ if (id != NULL)
+ *id = 0;
hid_end_parse(d);
- loc->size = 0;
return (0);
}
@@ -450,26 +478,35 @@ hid_get_data(const uint8_t *buf, uint32_t len, struct hid_location *loc)
uint32_t hpos = loc->pos;
uint32_t hsize = loc->size;
uint32_t data;
- int i, s, t;
+ uint32_t rpos;
+ uint8_t n;
DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize);
+ /* Range check and limit */
if (hsize == 0)
return (0);
+ if (hsize > 32)
+ hsize = 32;
+ /* Get data in a safe way */
data = 0;
- s = hpos / 8;
- for (i = hpos; i < (hpos + hsize); i += 8) {
- t = (i / 8);
- if (t < len) {
- data |= buf[t] << ((t - s) * 8);
- }
+ rpos = (hpos / 8);
+ n = (hsize + 7) / 8;
+ rpos += n;
+ while (n--) {
+ rpos--;
+ if (rpos < len)
+ data |= buf[rpos] << (8 * n);
}
- data >>= hpos % 8;
- data &= (1 << hsize) - 1;
- hsize = 32 - hsize;
- /* Sign extend */
- data = ((int32_t)data << hsize) >> hsize;
+
+ /* Correctly shift down data */
+ data = (data >> (hpos % 8));
+
+ /* Mask and sign extend in one */
+ n = 32 - hsize;
+ data = ((int32_t)data << n) >> n;
+
DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n",
loc->pos, loc->size, (long)data);
return (data);