diff options
-rw-r--r-- | lib/libusbhid/Makefile | 3 | ||||
-rw-r--r-- | lib/libusbhid/data.c | 49 | ||||
-rw-r--r-- | lib/libusbhid/descr.c | 46 | ||||
-rw-r--r-- | lib/libusbhid/parse.c | 468 | ||||
-rw-r--r-- | lib/libusbhid/shlib_version | 2 | ||||
-rw-r--r-- | lib/libusbhid/usbhid.3 | 9 | ||||
-rw-r--r-- | lib/libusbhid/usbhid.h | 3 |
7 files changed, 210 insertions, 370 deletions
diff --git a/lib/libusbhid/Makefile b/lib/libusbhid/Makefile index d9bd659f7b3..4ae744756c7 100644 --- a/lib/libusbhid/Makefile +++ b/lib/libusbhid/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.5 2012/07/11 13:43:54 yuo Exp $ +# $OpenBSD: Makefile,v 1.6 2012/07/16 19:57:17 jasper Exp $ # $NetBSD: Makefile,v 1.5 1999/07/23 09:44:38 mrg Exp $ LIB= usbhid @@ -10,7 +10,6 @@ MLINKS= usbhid.3 hid_dispose_report_desc.3 \ usbhid.3 hid_get_data.3 \ usbhid.3 hid_get_item.3 \ usbhid.3 hid_get_report_desc.3 \ - usbhid.3 hid_get_report_id.3 \ usbhid.3 hid_init.3 \ usbhid.3 hid_start.3 \ usbhid.3 hid_locate.3 \ diff --git a/lib/libusbhid/data.c b/lib/libusbhid/data.c index 3fcf719e2ce..4f35e68227a 100644 --- a/lib/libusbhid/data.c +++ b/lib/libusbhid/data.c @@ -1,4 +1,4 @@ -/* $OpenBSD: data.c,v 1.5 2012/07/12 10:17:43 yuo Exp $ */ +/* $OpenBSD: data.c,v 1.6 2012/07/16 19:57:17 jasper Exp $ */ /* $NetBSD: data.c,v 1.1 2001/12/28 17:45:26 augustss Exp $ */ /* @@ -33,59 +33,34 @@ int hid_get_data(const void *p, const hid_item_t *h) { - const uint8_t *buf; - uint32_t hpos, hsize, data; - int i, end, offs; - - buf = p; - - /* skip report ID byte */ - if (h->report_ID > 0) - buf++; - - hpos = h->pos; /* bit position of data */ - hsize = h->report_size; /* bit length of data */ + const unsigned char *buf = p; + unsigned int hpos = h->pos, hsize = h->report_size; + int data, i, end, offs; if (hsize == 0) return (0); - if (hsize > 32) - hsize = 32; - offs = hpos / 8; end = (hpos + hsize) / 8 - offs; data = 0; for (i = 0; i <= end; i++) data |= buf[offs + i] << (i*8); - - /* Correctly shift down data */ data >>= hpos % 8; - hsize = 32 - hsize; - - /* Mask and sign extend in one */ - if ((h->logical_minimum < 0) || (h->logical_maximum < 0)) - data = (int32_t)((int32_t)data << hsize) >> hsize; - else - data = (uint32_t)((uint32_t)data << hsize) >> hsize; - + data &= (1 << hsize) - 1; + if (h->logical_minimum < 0) { + /* Need to sign extend */ + hsize = sizeof data * 8 - hsize; + data = (data << hsize) >> hsize; + } return (data); } void hid_set_data(void *p, const hid_item_t *h, int data) { - uint32_t *buf; - uint32_t hpos, hsize; + unsigned char *buf = p; + unsigned int hpos = h->pos, hsize = h->report_size; int i, end, offs, mask; - buf = p; - - /* Set report ID byte */ - if (h->report_ID > 0) - *buf++ = h->report_ID & 0xff; - - hpos = h->pos; /* bit position of data */ - hsize = h->report_size; /* bit length of data */ - if (hsize != 32) { mask = (1 << hsize) - 1; data &= mask; diff --git a/lib/libusbhid/descr.c b/lib/libusbhid/descr.c index ebe34a379ff..11972c2259c 100644 --- a/lib/libusbhid/descr.c +++ b/lib/libusbhid/descr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: descr.c,v 1.5 2012/07/11 13:43:54 yuo Exp $ */ +/* $OpenBSD: descr.c,v 1.6 2012/07/16 19:57:17 jasper Exp $ */ /* $NetBSD: descr.c,v 1.2 2002/02/20 20:31:07 christos Exp $ */ /* @@ -40,57 +40,15 @@ #include "usbhid.h" #include "usbvar.h" -int -hid_get_report_id(int fd) -{ - report_desc_t rep; - hid_data_t d; - hid_item_t h; - int kindset; - int temp = -1; - int ret = -1; - - if ((rep = hid_get_report_desc(fd)) == NULL) - goto use_ioctl; - kindset = 1 << hid_input | 1 << hid_output | 1 << hid_feature; - for (d = hid_start_parse(rep, kindset, 0); hid_get_item(d, &h); ) { - /* Return the first report ID we met. */ - if (h.report_ID != 0) { - temp = h.report_ID; - break; - } - } - hid_end_parse(d); - hid_dispose_report_desc(rep); - - if (temp >0) - return (temp); - -use_ioctl: - if(ioctl(fd, USB_GET_REPORT_ID, &temp) < 0) - return 0; - else - ret = temp; - - - return (ret); -} - report_desc_t hid_get_report_desc(int fd) { struct usb_ctl_report_desc rep; - memset(&rep, 0, sizeof(rep)); - + rep.ucrd_size = 0; if (ioctl(fd, USB_GET_REPORT_DESC, &rep) < 0) return (NULL); - /* check END_COLLECTION */ - if (((unsigned char *)rep.ucrd_data)[rep.ucrd_size -1] != 0xc0) { - return (NULL); - } - return hid_use_report_desc(rep.ucrd_data, (unsigned int)rep.ucrd_size); } diff --git a/lib/libusbhid/parse.c b/lib/libusbhid/parse.c index 3a297dda4bb..08ac1864d9a 100644 --- a/lib/libusbhid/parse.c +++ b/lib/libusbhid/parse.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.c,v 1.6 2012/07/11 13:43:54 yuo Exp $ */ +/* $OpenBSD: parse.c,v 1.7 2012/07/16 19:57:17 jasper Exp $ */ /* $NetBSD: parse.c,v 1.2 2001/12/29 20:44:22 augustss Exp $ */ /* @@ -38,40 +38,42 @@ #include "usbvar.h" #define MAXUSAGE 100 -#define MAXPUSH 4 -#define MAXID 64 -#define ITEMTYPES 3 - -struct hid_pos_data { - int32_t rid; - uint32_t pos[ITEMTYPES]; -}; struct hid_data { - const uint8_t *start; - const uint8_t *end; - const uint8_t *p; - struct hid_item cur[MAXPUSH]; - struct hid_pos_data last_pos[MAXID]; - uint32_t pos[ITEMTYPES]; - int32_t usages_min[MAXUSAGE]; - int32_t usages_max[MAXUSAGE]; - int32_t usage_last; /* last seen usage */ - uint32_t loc_size; /* last seen size */ - uint32_t loc_count; /* last seen count */ - uint8_t kindset; /* we have 5 kinds so 8bits are enough */ - uint8_t pushlevel; /* current push level */ - uint8_t ncount; /* end usage item count */ - uint8_t icount; /* current usage item count */ - uint8_t nusage; /* end "usages_min/max" index */ - uint8_t iusage; /* current "usages_min/max" index */ - uint8_t ousage; /* current "usages_min/max" offset */ - uint8_t susage; /* usage set flags */ + u_char *start; + u_char *end; + u_char *p; + hid_item_t cur; + unsigned int usages[MAXUSAGE]; + int nusage; + int minset; + int logminsize; + int multi; + int multimax; + int kindset; + int reportid; + + /* + * The start of collection item has no report ID set, so save + * it until we know the ID. + */ + hid_item_t savedcoll; + u_char hassavedcoll; + /* + * Absolute data position (bits) for input/output/feature. + * Assumes that hid_input, hid_output and hid_feature have + * values 0, 1 and 2. + */ + unsigned int kindpos[3]; }; +static int min(int x, int y) { return x < y ? x : y; } + +static int hid_get_item_raw(hid_data_t s, hid_item_t *h); static void hid_clear_local(hid_item_t *c) { + c->usage = 0; c->usage_minimum = 0; c->usage_maximum = 0; @@ -84,58 +86,8 @@ hid_clear_local(hid_item_t *c) c->set_delimiter = 0; } -static void -hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) -{ - uint8_t i, j; - - /* check for same report ID - optimise */ - if (c->report_ID == next_rID) - return; - - /* save current position for current rID */ - if (c->report_ID == 0) { - i = 0; - } else { - for (i = 1; i != MAXID; i++) { - if (s->last_pos[i].rid == c->report_ID) - break; - if (s->last_pos[i].rid == 0) - break; - } - } - if (i != MAXID) { - s->last_pos[i].rid = c->report_ID; - for (j = 0; j < ITEMTYPES; j++) - s->last_pos[i].pos[j] = s->pos[j]; - } - - /* store next report ID */ - c->report_ID = next_rID; - - /* lookup last position for next rID */ - if (next_rID == 0) { - i = 0; - } else { - for (i = 1; i != MAXID; i++){ - if (s->last_pos[i].rid == next_rID) - break; - if (s->last_pos[i].rid == 0) - break; - } - } - if (i != MAXID) { - s->last_pos[i].rid = next_rID; - for (j = 0; j < ITEMTYPES; j++) - s->pos[j] = s->last_pos[i].pos[j]; - } else { - for (j = 0; j < ITEMTYPES; j++) - s->pos[j] = 0; /* Out of RID entries */ - } -} - hid_data_t -hid_start_parse(report_desc_t d, int kindset, int id __unused) +hid_start_parse(report_desc_t d, int kindset, int id) { struct hid_data *s; @@ -146,6 +98,8 @@ hid_start_parse(report_desc_t d, int kindset, int id __unused) s->start = s->p = d->data; s->end = d->data + d->size; s->kindset = kindset; + s->reportid = id; + s->hassavedcoll = 0; return (s); } @@ -153,191 +107,200 @@ void hid_end_parse(hid_data_t s) { - if (s == NULL) - return; - + while (s->cur.next) { + hid_item_t *hi = s->cur.next->next; + free(s->cur.next); + s->cur.next = hi; + } free(s); } -static uint8_t -hid_get_byte(struct hid_data *s, const uint16_t wSize) -{ - const uint8_t *ptr; - uint8_t retval; - - ptr = s->p; - - /* check if end is reached */ - if (ptr == s->end) - return (0); - - /* read out a byte */ - retval = *ptr; - - /* check if data pointer can be advanced by "wSize" bytes */ - if ((s->end - ptr) < wSize) - ptr = s->end; - else - ptr += wSize; - - /* update pointer */ - s->p = ptr; - - return (retval); -} - int hid_get_item(hid_data_t s, hid_item_t *h) { - hid_item_t *c; - unsigned int bTag, bType, bSize; - int32_t mask; - int32_t dval; + int r; - if (s == NULL) - return (0); + for (;;) { + r = hid_get_item_raw(s, h); + if (r <= 0) + break; + if (h->report_ID == s->reportid || s->reportid == -1) + break; + } + return (r); +} - c = &s->cur[s->pushlevel]; +#define REPORT_SAVED_COLL \ + do { \ + if (s->hassavedcoll) { \ + *h = s->savedcoll; \ + h->report_ID = c->report_ID; \ + s->hassavedcoll = 0; \ + return (1); \ + } \ + } while(/*LINTED*/ 0) + +static int +hid_get_item_raw(hid_data_t s, hid_item_t *h) +{ + hid_item_t *c = &s->cur, *hi, nc; + unsigned int bTag = 0, bType = 0, bSize; + unsigned char *data; + hid_kind_t retkind; + unsigned char *p; + int dval, i; top: - /* check if there is an array of items */ - if (s->icount < s-> ncount) { - /* get current usage */ - if (s->iusage < s->nusage) { - dval = s->usages_min[s->iusage] + s->ousage; - c->usage = dval; - s->usage_last = dval; - if (dval == s->usages_max[s->iusage]){ - s->iusage++; - s->ousage = 0; - } else - s->ousage++; - } else { - /* Using last usage */ - dval = s->usage_last; + if (s->multimax) { + REPORT_SAVED_COLL; + if (c->logical_minimum >= c->logical_maximum) { + if (s->logminsize == 1) + c->logical_minimum =(int8_t)c->logical_minimum; + else if (s->logminsize == 2) + c->logical_minimum =(int16_t)c->logical_minimum; } - s->icount++; - - /* - * Only copy HID item, increment position and return - * if correct kindset! - */ - if (s->kindset & (1 << c->kind)) { + if (s->multi < s->multimax) { + c->usage = s->usages[min(s->multi, s->nusage-1)]; + s->multi++; *h = *c; - h->pos = s->pos[c->kind]; - s->pos[c->kind] += c->report_size * c->report_count; + /* + * 'multimax' is only non-zero if the current + * item kind is input/output/feature + */ + h->pos = s->kindpos[c->kind]; + s->kindpos[c->kind] += c->report_size; + h->next = 0; return (1); + } else { + c->report_count = s->multimax; + s->multimax = 0; + s->nusage = 0; + hid_clear_local(c); } } + for (;;) { + p = s->p; + if (p >= s->end) + return (0); - /* reset state variables */ - s->icount = 0; - s->ncount = 0; - s->iusage = 0; - s->nusage = 0; - s->susage = 0; - s->ousage = 0; - hid_clear_local(c); - - /* get next item */ - while (s->p != s->end) { - bSize = hid_get_byte(s, 1); + bSize = *p++; if (bSize == 0xfe) { /* long item */ - bSize = hid_get_byte(s, 1); - bSize |= hid_get_byte(s, 1) << 8; - bTag = hid_get_byte(s, 1); - bType = 0xff; /* XXX what shoud it be */ + bSize = *p++; + bSize |= *p++ << 8; + bTag = *p++; + data = p; + p += bSize; } else { /* short item */ bTag = bSize >> 4; bType = (bSize >> 2) & 3; bSize &= 3; - if (bSize == 3) - bSize = 4; + if (bSize == 3) bSize = 4; + data = p; + p += bSize; } - + s->p = p; + /* + * The spec is unclear if the data is signed or unsigned. + */ switch(bSize) { case 0: dval = 0; - mask = 0; break; case 1: - dval = (int8_t)hid_get_byte(s, 1); - mask = 0xff; + dval = /*(int8_t)*/*data++; break; case 2: - dval = hid_get_byte(s, 1); - dval |= hid_get_byte(s, 1) << 8; - dval = (int16_t)dval; - mask = 0xffff; + dval = *data++; + dval |= *data++ << 8; + dval = /*(int16_t)*/dval; break; case 4: - dval = hid_get_byte(s, 1); - dval |= hid_get_byte(s, 1) << 8; - dval |= hid_get_byte(s, 1) << 16; - dval |= hid_get_byte(s, 1) << 24; - mask = 0xffffffff; + dval = *data++; + dval |= *data++ << 8; + dval |= *data++ << 16; + dval |= *data++ << 24; break; default: - dval = hid_get_byte(s, bSize); - continue; + return (-1); } switch (bType) { case 0: /* Main */ switch (bTag) { case 8: /* Input */ - c->kind = hid_input; - c->flags =dval; + retkind = hid_input; ret: - c->report_count = s->loc_count; - c->report_size = s->loc_size; - + if (!(s->kindset & (1 << retkind))) { + /* Drop the items of this kind */ + s->nusage = 0; + continue; + } + c->kind = retkind; + c->flags = dval; if (c->flags & HIO_VARIABLE) { - /* range check usage count */ - if (c->report_count > 255) { - s->ncount = 255; - } else - s->ncount = c->report_count; - - /* - * The "top" loop will return - * one and on item: - */ + s->multimax = c->report_count; + s->multi = 0; c->report_count = 1; - c->usage_minimum = 0; - c->usage_maximum = 0; + if (s->minset) { + for (i = c->usage_minimum; + i <= c->usage_maximum; i++) { + s->usages[s->nusage] = i; + if (s->nusage < MAXUSAGE-1) + s->nusage++; + } + c->usage_minimum = 0; + c->usage_maximum = 0; + s->minset = 0; + } + goto top; } else { - s->ncount = 1; + 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); } - goto top; case 9: /* Output */ - c->kind = hid_output; - c->flags = dval; + retkind = hid_output; goto ret; case 10: /* Collection */ c->kind = hid_collection; c->collection = dval; c->collevel++; - c->usage = s->usage_last; - *h = *c; - return (1); + nc = *c; + hid_clear_local(c); + /*c->report_ID = NO_REPORT_ID;*/ + s->nusage = 0; + if (s->hassavedcoll) { + *h = s->savedcoll; + h->report_ID = nc.report_ID; + s->savedcoll = nc; + return (1); + } else { + s->hassavedcoll = 1; + s->savedcoll = nc; + } + break; case 11: /* Feature */ - c->kind = hid_feature; - c->flags = dval; + retkind = hid_feature; goto ret; case 12: /* End collection */ + REPORT_SAVED_COLL; c->kind = hid_endcollection; - if (c->collevel == 0){ - /* invalid end collection */ - return (0); - } c->collevel--; *h = *c; + /*hid_clear_local(c);*/ + s->nusage = 0; return (1); default: - break; + return (-2); } break; @@ -348,6 +311,7 @@ hid_get_item(hid_data_t s, hid_item_t *h) break; case 1: c->logical_minimum = dval; + s->logminsize = bSize; break; case 2: c->logical_maximum = dval; @@ -359,102 +323,52 @@ hid_get_item(hid_data_t s, hid_item_t *h) c->physical_maximum = dval; break; case 5: - if (dval > 7 && dval < 0x10) - c->unit_exponent = -16 + dval; - else - c->unit_exponent = dval; + c->unit_exponent = dval; break; case 6: c->unit = dval; break; case 7: - /* mask because value is unsigned */ - s->loc_size = dval & mask; + c->report_size = dval; break; case 8: - hid_switch_rid(s, c, dval); + c->report_ID = dval; + s->kindpos[hid_input] = 0; + s->kindpos[hid_output] = 0; + s->kindpos[hid_feature] = 0; break; case 9: - /* mask because value is unsigned */ - s->loc_count = dval & mask; + c->report_count = dval; break; case 10: /* Push */ - s->pushlevel++; - if (s->pushlevel < MAXPUSH) { - s->cur[s->pushlevel] = *c; - /* store size and count */ - c->report_size = s->loc_size; - c->report_count = s->loc_count; - /* update current item pointer */ - c = &s->cur[s->pushlevel]; - } + hi = malloc(sizeof *hi); + /* XXX unchecked malloc */ + *hi = s->cur; + c->next = hi; break; case 11: /* Pop */ - s->pushlevel--; - if (s->pushlevel < MAXPUSH) { - c = &s->cur[s->pushlevel]; - /* restore size and count */ - s->loc_size = c->report_size; - s->loc_count = c->report_count; - c->report_size = 0; - c->report_count = 0; - } + hi = c->next; + s->cur = *hi; + free(hi); break; default: - break; + return (-3); } break; case 2: /* Local */ switch (bTag) { case 0: - if (bSize != 4) - dval = (dval & mask) | c->_usage_page; - - /* set last usage, in case of a collection */ - s->usage_last = dval; - - if (s->nusage < MAXUSAGE) { - s->usages_min[s->nusage] = dval; - s->usages_max[s->nusage] = dval; - s->nusage++; - } + c->usage = c->_usage_page | dval; + if (s->nusage < MAXUSAGE) + s->usages[s->nusage++] = c->usage; /* else XXX */ - - /* clear any pending usage sets */ - s->susage = 0; break; case 1: - s->susage |= 1; - - if (bSize != 4) - dval = (dval & mask) | c->_usage_page; - c->usage_minimum = dval; - - goto check_set; + s->minset = 1; + c->usage_minimum = c->_usage_page | dval; + break; case 2: - s->susage |= 2; - - if (bSize != 4) - dval = (dval & mask) | c->_usage_page; - c->usage_maximum = dval; - - check_set: - if (s->susage != 3) - break; - - /* sanity check */ - if ((s->nusage < MAXUSAGE) && - (c->usage_minimum <= c->usage_maximum)){ - /* add usage range */ - s->usages_min[s->nusage] = - c->usage_minimum; - s->usages_max[s->nusage] = - c->usage_maximum; - s->nusage++; - } - /* else XXX */ - - s->susage = 0; + c->usage_maximum = c->_usage_page | dval; break; case 3: c->designator_index = dval; @@ -525,7 +439,7 @@ hid_report_size(report_desc_t r, enum hid_kind k, int id) temp = hpos - lpos; /* return length in bytes rounded up */ - return ((temp + 7) / 8 + report_id); + return ((temp + 7) / 8); } int diff --git a/lib/libusbhid/shlib_version b/lib/libusbhid/shlib_version index d9961ea9fef..3066b9771e7 100644 --- a/lib/libusbhid/shlib_version +++ b/lib/libusbhid/shlib_version @@ -1,2 +1,2 @@ -major=4 +major=5 minor=0 diff --git a/lib/libusbhid/usbhid.3 b/lib/libusbhid/usbhid.3 index e8a812f8abe..09d2ffb4a9c 100644 --- a/lib/libusbhid/usbhid.3 +++ b/lib/libusbhid/usbhid.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: usbhid.3,v 1.12 2012/07/11 13:43:54 yuo Exp $ +.\" $OpenBSD: usbhid.3,v 1.13 2012/07/16 19:57:17 jasper Exp $ .\" $NetBSD: usbhid.3,v 1.5 2002/02/07 07:00:52 ross Exp $ .\" .\" Copyright (c) 1999, 2001 Lennart Augustsson <augustss@netbsd.org> @@ -25,13 +25,12 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd $Mdocdate: July 11 2012 $ +.Dd $Mdocdate: July 16 2012 $ .Dt USBHID 3 .Os .Sh NAME .Nm usbhid , .Nm hid_get_report_desc , -.Nm hid_get_report_id , .Nm hid_use_report_desc , .Nm hid_dispose_report_desc , .Nm hid_start_parse , @@ -52,8 +51,6 @@ .Fd #include \*[Lt]usbhid.h\*[Gt] .Ft report_desc_t .Fn hid_get_report_desc "int file" -.Ft int -.Fn hid_get_report_id "int file" .Ft report_desc_t .Fn hid_use_report_desc "unsigned char *data" "unsigned int size" .Ft void @@ -100,8 +97,6 @@ The routines can be divided into four parts: extraction of the descriptor, parsing of the descriptor, translating to/from symbolic names, and data manipulation. .Ss DESCRIPTOR FUNCTIONS -The report descripor ID can be obtained by calling -.Fn hid_get_report_id . A report descriptor can be obtained by calling .Fn hid_get_report_desc with a file descriptor obtained by opening a diff --git a/lib/libusbhid/usbhid.h b/lib/libusbhid/usbhid.h index 0ebe53dad77..dd0c98b73d6 100644 --- a/lib/libusbhid/usbhid.h +++ b/lib/libusbhid/usbhid.h @@ -1,4 +1,4 @@ -/* $OpenBSD: usbhid.h,v 1.4 2012/07/11 13:43:54 yuo Exp $ */ +/* $OpenBSD: usbhid.h,v 1.5 2012/07/16 19:57:17 jasper Exp $ */ /* $NetBSD: usbhid.h,v 1.1 2001/12/28 17:45:27 augustss Exp $ */ /* @@ -78,7 +78,6 @@ typedef struct hid_item { #define HID_USAGE(u) ((u) & 0xffff) /* Obtaining a report descriptor, descr.c: */ -int hid_get_report_id(int file); report_desc_t hid_get_report_desc(int file); report_desc_t hid_use_report_desc(unsigned char *data, unsigned int size); void hid_dispose_report_desc(report_desc_t); |