Index: descr.c =================================================================== --- descr.c (revision 205309) +++ descr.c (working copy) @@ -38,7 +38,6 @@ #include #include #include - #include #include "usbhid.h" @@ -59,9 +58,30 @@ 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; + 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: ret = ioctl(fd, USB_GET_REPORT_ID, &temp); #ifdef HID_COMPAT7 if (ret < 0) Index: usbhid.h =================================================================== --- usbhid.h (revision 205309) +++ usbhid.h (working copy) @@ -29,52 +29,48 @@ * */ -#include +#include typedef struct report_desc *report_desc_t; typedef struct hid_data *hid_data_t; typedef enum hid_kind { - hid_input = 0, - hid_output = 1, - hid_feature = 2, - hid_collection, - hid_endcollection + hid_input, hid_output, hid_feature, hid_collection, hid_endcollection } hid_kind_t; typedef struct hid_item { /* Global */ - unsigned int _usage_page; - int logical_minimum; - int logical_maximum; - int physical_minimum; - int physical_maximum; - int unit_exponent; - int unit; - int report_size; - int report_ID; + uint32_t _usage_page; + int32_t logical_minimum; + int32_t logical_maximum; + int32_t physical_minimum; + int32_t physical_maximum; + int32_t unit_exponent; + int32_t unit; + int32_t report_size; + int32_t report_ID; #define NO_REPORT_ID 0 - int report_count; + int32_t report_count; /* Local */ - unsigned int usage; - int usage_minimum; - int usage_maximum; - int designator_index; - int designator_minimum; - int designator_maximum; - int string_index; - int string_minimum; - int string_maximum; - int set_delimiter; + uint32_t usage; + int32_t usage_minimum; + int32_t usage_maximum; + int32_t designator_index; + int32_t designator_minimum; + int32_t designator_maximum; + int32_t string_index; + int32_t string_minimum; + int32_t string_maximum; + int32_t set_delimiter; /* Misc */ - int collection; - int collevel; + int32_t collection; + int collevel; enum hid_kind kind; - unsigned int flags; - /* Absolute data position (bits) */ - unsigned int pos; - /* */ + uint32_t flags; + /* Location */ + uint32_t pos; + /* unused */ struct hid_item *next; } hid_item_t; @@ -95,7 +91,8 @@ void hid_end_parse(hid_data_t s); int hid_get_item(hid_data_t s, hid_item_t *h); int hid_report_size(report_desc_t d, enum hid_kind k, int id); -int hid_locate(report_desc_t d, unsigned int usage, enum hid_kind k, hid_item_t *h, int id); +int hid_locate(report_desc_t d, unsigned int usage, enum hid_kind k, + hid_item_t *h, int id); /* Conversion to/from usage names, usage.c: */ const char *hid_usage_page(int i); Index: usage.c =================================================================== --- usage.c (revision 205309) +++ usage.c (working copy) @@ -29,6 +29,7 @@ #include __FBSDID("$FreeBSD$"); +#include #include #include #include Index: usbvar.h =================================================================== --- usbvar.h (revision 205309) +++ usbvar.h (working copy) @@ -30,8 +30,8 @@ */ struct report_desc { - unsigned int size; - unsigned char data[1]; + uint32_t size; + uint8_t data[1]; }; /* internal backwards compatibility functions */ Index: data.c =================================================================== --- data.c (revision 205309) +++ data.c (working copy) @@ -29,6 +29,7 @@ #include __FBSDID("$FreeBSD$"); +#include #include #include #include "usbhid.h" @@ -36,18 +37,27 @@ int hid_get_data(const void *p, const hid_item_t *h) { - const unsigned char *buf; - unsigned int hpos; - unsigned int hsize; - int data; + const uint8_t *buf; + uint32_t hpos; + uint32_t hsize; + uint32_t 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 */ + /* Range check and limit */ if (hsize == 0) return (0); + if (hsize > 32) + hsize = 32; + offs = hpos / 8; end = (hpos + hsize) / 8 - offs; data = 0; @@ -66,12 +76,17 @@ void hid_set_data(void *p, const hid_item_t *h, int data) { - unsigned char *buf; - unsigned int hpos; - unsigned int hsize; + uint8_t *buf; + uint32_t hpos; + uint32_t hsize; 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 */ @@ -90,5 +105,5 @@ for (i = 0; i <= end; i++) buf[offs + i] = (buf[offs + i] & (mask >> (i*8))) | - ((data >> (i*8)) & 0xff); + ((data >> (i*8)) & 0xff); } Index: parse.c =================================================================== --- parse.c (revision 205309) +++ parse.c (working copy) @@ -40,42 +40,43 @@ #include "usbhid.h" #include "usbvar.h" -#define MAXUSAGE 100 -struct hid_data { - 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; +#define MAXUSAGE 100 +#define MAXPUSH 4 +#define MAXID 64 - /* - * 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]; +struct hid_pos_data { + int32_t rid; + uint32_t pos; }; -static int min(int x, int y) { return x < y ? x : y; } +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]; + 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 8 bits are enough */ + uint8_t pushlevel; /* current pushlevel */ + 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 */ +}; -static int hid_get_item_raw(hid_data_t s, hid_item_t *h); - +/*------------------------------------------------------------------------* + * hid_clear_local + *------------------------------------------------------------------------*/ static void hid_clear_local(hid_item_t *c) { + c->usage = 0; c->usage_minimum = 0; c->usage_maximum = 0; @@ -88,8 +89,61 @@ c->set_delimiter = 0; } +static void +hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) +{ + uint8_t i; + + /* 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; + s->last_pos[i].pos = c->pos; + } + + /* 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; + c->pos = s->last_pos[i].pos; + } else + c->pos = 0; /* Out of RID entries. */ +} + +/*------------------------------------------------------------------------* + * hid_start_parse + *------------------------------------------------------------------------*/ hid_data_t -hid_start_parse(report_desc_t d, int kindset, int id) +hid_start_parse(report_desc_t d, int kindset, int id __unused) { struct hid_data *s; @@ -98,213 +152,207 @@ s->start = s->p = d->data; s->end = d->data + d->size; s->kindset = kindset; - s->reportid = id; - s->hassavedcoll = 0; return (s); } +/*------------------------------------------------------------------------* + * hid_end_parse + *------------------------------------------------------------------------*/ void hid_end_parse(hid_data_t s) { - while (s->cur.next) { - hid_item_t *hi = s->cur.next->next; - free(s->cur.next); - s->cur.next = hi; - } + + if (s == NULL) + return; + free(s); } -int -hid_get_item(hid_data_t s, hid_item_t *h) +/*------------------------------------------------------------------------* + * get byte from HID descriptor + *------------------------------------------------------------------------*/ +static uint8_t +hid_get_byte(struct hid_data *s, const uint16_t wSize) { - int r; + const uint8_t *ptr; + uint8_t retval; - for (;;) { - r = hid_get_item_raw(s, h); - if (r <= 0) - break; - if (h->report_ID == s->reportid || s->reportid == -1) - break; - } - return (r); + 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); } -#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_get_item + *------------------------------------------------------------------------*/ +int +hid_get_item(hid_data_t s, hid_item_t *h) { hid_item_t *c; - unsigned int bTag = 0, bType = 0, bSize; - unsigned char *data; - int dval; - unsigned char *p; - hid_item_t *hi; - hid_item_t nc; - int i; - hid_kind_t retkind; + unsigned int bTag, bType, bSize; + uint32_t oldpos; + int32_t mask; + int32_t dval; - c = &s->cur; + if (s == NULL) + return (0); + c = &s->cur[s->pushlevel]; + top: - 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; + /* 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->multi < s->multimax) { - c->usage = s->usages[min(s->multi, s->nusage-1)]; - s->multi++; + s->icount ++; + /* + * Only copy HID item, increment position and return + * if correct kindset! + */ + if (s->kindset & (1 << c->kind)) { *h = *c; - /* - * '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; + c->pos += c->report_size * c->report_count; 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); - bSize = *p++; + /* 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); if (bSize == 0xfe) { /* long item */ - bSize = *p++; - bSize |= *p++ << 8; - bTag = *p++; - data = p; - p += bSize; + bSize = hid_get_byte(s, 1); + bSize |= hid_get_byte(s, 1) << 8; + bTag = hid_get_byte(s, 1); + bType = 0xff; /* XXX what should it be */ } else { /* short item */ bTag = bSize >> 4; bType = (bSize >> 2) & 3; bSize &= 3; - if (bSize == 3) bSize = 4; - data = p; - p += bSize; + if (bSize == 3) + bSize = 4; } - 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 = *data++; + dval = (int8_t)hid_get_byte(s, 1); + mask = 0xFF; break; case 2: - dval = *data++; - dval |= *data++ << 8; + dval = hid_get_byte(s, 1); + dval |= hid_get_byte(s, 1) << 8; + dval = (int16_t)dval; + mask = 0xFFFF; break; case 4: - dval = *data++; - dval |= *data++ << 8; - dval |= *data++ << 16; - dval |= *data++ << 24; + 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; break; default: - return (-1); + dval = hid_get_byte(s, bSize); + continue; } switch (bType) { - case 0: /* Main */ + case 0: /* Main */ switch (bTag) { - case 8: /* Input */ - retkind = hid_input; - ret: - if (!(s->kindset & (1 << retkind))) { - /* Drop the items of this kind */ - s->nusage = 0; - continue; - } - c->kind = retkind; + case 8: /* Input */ + c->kind = hid_input; c->flags = dval; + ret: + c->report_count = s->loc_count; + c->report_size = s->loc_size; + if (c->flags & HIO_VARIABLE) { - s->multimax = c->report_count; - s->multi = 0; + /* 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 one item: + */ c->report_count = 1; - 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 { - 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->ncount = 1; } - case 9: /* Output */ - retkind = hid_output; + goto top; + + case 9: /* Output */ + c->kind = hid_output; + c->flags = dval; goto ret; case 10: /* Collection */ c->kind = hid_collection; c->collection = dval; c->collevel++; - 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; + c->usage = s->usage_last; + *h = *c; + return (1); case 11: /* Feature */ - retkind = hid_feature; + c->kind = hid_feature; + c->flags = dval; 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: - return (-2); + break; } break; @@ -315,13 +363,12 @@ break; case 1: c->logical_minimum = dval; - s->logminsize = bSize; break; case 2: c->logical_maximum = dval; break; case 3: - c->physical_maximum = dval; + c->physical_minimum = dval; break; case 4: c->physical_maximum = dval; @@ -333,45 +380,97 @@ c->unit = dval; break; case 7: - c->report_size = dval; + /* mask because value is unsigned */ + s->loc_size = dval & mask; break; case 8: - c->report_ID = dval; - s->kindpos[hid_input] = - s->kindpos[hid_output] = - s->kindpos[hid_feature] = 0; + hid_switch_rid(s, c, dval); break; case 9: - c->report_count = dval; + /* mask because value is unsigned */ + s->loc_count = dval & mask; break; - case 10: /* Push */ - hi = malloc(sizeof *hi); - *hi = s->cur; - c->next = hi; + 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]; + } break; - case 11: /* Pop */ - hi = c->next; - s->cur = *hi; - free(hi); + case 11: /* Pop */ + s->pushlevel --; + if (s->pushlevel < MAXPUSH) { + /* preserve position */ + oldpos = c->pos; + c = &s->cur[s->pushlevel]; + /* restore size and count */ + s->loc_size = c->report_size; + s->loc_count = c->report_count; + /* set default item location */ + c->pos = oldpos; + c->report_size = 0; + c->report_count = 0; + } break; default: - return (-3); + break; } break; case 2: /* Local */ switch (bTag) { case 0: - c->usage = c->_usage_page | dval; - if (s->nusage < MAXUSAGE) - s->usages[s->nusage++] = c->usage; + 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 ++; + } /* else XXX */ + + /* clear any pending usage sets */ + s->susage = 0; break; case 1: - s->minset = 1; - c->usage_minimum = c->_usage_page | dval; - break; + s->susage |= 1; + + if (bSize != 4) + dval = (dval & mask) | c->_usage_page; + c->usage_minimum = dval; + + goto check_set; case 2: - c->usage_maximum = c->_usage_page | dval; + 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; break; case 3: c->designator_index = dval; @@ -395,40 +494,63 @@ c->set_delimiter = dval; break; default: - return (-4); + break; } break; default: - return (-5); + break; } } + return (0); } int hid_report_size(report_desc_t r, enum hid_kind k, int id) { struct hid_data *d; - hid_item_t h; - int size; + struct hid_item h; + uint32_t temp; + uint32_t hpos; + uint32_t lpos; + hpos = 0; + lpos = 0xFFFFFFFF; + memset(&h, 0, sizeof h); - size = 0; - for (d = hid_start_parse(r, 1<kindpos[k]; + /* compute minimum */ + if (lpos > h.pos) + lpos = h.pos; + /* compute end position */ + temp = h.pos + (h.report_size * h.report_count); + /* compute maximum */ + if (hpos < temp) + hpos = temp; } } hid_end_parse(d); - return ((size + 7) / 8); + + /* safety check - can happen in case of currupt descriptors */ + if (lpos > hpos) + temp = 0; + else + temp = hpos - lpos; + + if (id) + temp += 8; + + /* return length in bytes rounded up */ + return ((temp + 7) / 8); } int hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k, hid_item_t *h, int id) { - hid_data_t d; + struct hid_data *d; - for (d = hid_start_parse(desc, 1<kind == k && !(h->flags & HIO_CONST) && h->usage == u) { hid_end_parse(d); return (1);