diff options
author | mav <mav@FreeBSD.org> | 2011-09-28 14:52:25 +0000 |
---|---|---|
committer | mav <mav@FreeBSD.org> | 2011-09-28 14:52:25 +0000 |
commit | 3a1a19715027c5c03af47ff8d3ce06461e3b4d65 (patch) | |
tree | ed5b81a92c39c4c34a4ab0631b21d7f1764a740f /usr.bin/usbhidctl/usbhid.c | |
parent | 3383aff65a84084093f4575d6098fac891dcbab9 (diff) | |
download | FreeBSD-src-3a1a19715027c5c03af47ff8d3ce06461e3b4d65.zip FreeBSD-src-3a1a19715027c5c03af47ff8d3ce06461e3b4d65.tar.gz |
MFprojects/hid:
Import the rest of HID improvements from the branch:
- improve report descriptor parser in libusbhid to handle several kinds of
reports same time;
- add to the libusbhid API two functions wrapping respective kernel IOCTLs
for reading and writing reports;
- tune uhid IOCTL interface to allow reading and writing arbitrary report,
when multiple supported by the device;
- teach usbhidctl to set output and feature reports;
- make usbhidaction support all the same item names as bhidctl.
Sponsored by: iXsystems, inc.
Diffstat (limited to 'usr.bin/usbhidctl/usbhid.c')
-rw-r--r-- | usr.bin/usbhidctl/usbhid.c | 399 |
1 files changed, 286 insertions, 113 deletions
diff --git a/usr.bin/usbhidctl/usbhid.c b/usr.bin/usbhidctl/usbhid.c index 281078d..bf3e348 100644 --- a/usr.bin/usbhidctl/usbhid.c +++ b/usr.bin/usbhidctl/usbhid.c @@ -42,45 +42,141 @@ #include <usbhid.h> #include <dev/usb/usbhid.h> +struct variable { + char *name; + int instance; + int val; + struct hid_item h; + struct variable *next; +} *vars; + int verbose = 0; -int all = 0; int noname = 0; int hexdump = 0; - -char **names; -int nnames; - -void prbits(int bits, char **strs, int n); -void usage(void); -void dumpitem(const char *label, struct hid_item *h); -void dumpitems(report_desc_t r); -void rev(struct hid_item **p); -void prdata(u_char *buf, struct hid_item *h); -void dumpdata(int f, report_desc_t r, int loop); -int gotname(char *n); - -int -gotname(char *n) +int wflag = 0; +int zflag = 0; + +static void usage(void); +static void dumpitem(const char *label, struct hid_item *h); +static void dumpitems(report_desc_t r); +static void prdata(u_char *buf, struct hid_item *h); +static void dumpdata(int f, report_desc_t r, int loop); +static void writedata(int f, report_desc_t r); + +static void +parceargs(report_desc_t r, int all, int nnames, char **names) { - int i; - - for (i = 0; i < nnames; i++) - if (strcmp(names[i], n) == 0) - return 1; - return 0; -} - -void -prbits(int bits, char **strs, int n) -{ - int i; - - for(i = 0; i < n; i++, bits >>= 1) - if (strs[i*2]) - printf("%s%s", i == 0 ? "" : ", ", strs[i*2 + (bits&1)]); + struct hid_data *d; + struct hid_item h; + char colls[1000]; + char hname[1000], *tmp1, *tmp2; + struct variable *var, **pnext; + int i, instance, cp, t; + + pnext = &vars; + if (all) { + if (wflag) + errx(1, "Must not specify -w to read variables"); + cp = 0; + for (d = hid_start_parse(r, + 1<<hid_input | 1<<hid_output | 1<<hid_feature, -1); + hid_get_item(d, &h); ) { + if (h.kind == hid_collection) { + cp += sprintf(&colls[cp], "%s%s:%s", + cp != 0 ? "." : "", + hid_usage_page(HID_PAGE(h.usage)), + hid_usage_in_page(h.usage)); + } else if (h.kind == hid_endcollection) { + tmp1 = strrchr(colls, '.'); + if (tmp1 != NULL) { + cp -= strlen(tmp1); + tmp1[0] = 0; + } else { + cp = 0; + colls[0] = 0; + } + } + if ((h.kind != hid_input && h.kind != hid_output && + h.kind != hid_feature) || (h.flags & HIO_CONST)) + continue; + var = malloc(sizeof(*var)); + memset(var, 0, sizeof(*var)); + asprintf(&var->name, "%s%s%s:%s", + colls, colls[0] != 0 ? "." : "", + hid_usage_page(HID_PAGE(h.usage)), + hid_usage_in_page(h.usage)); + var->h = h; + *pnext = var; + pnext = &var->next; + } + hid_end_parse(d); + return; + } + for (i = 0; i < nnames; i++) { + var = malloc(sizeof(*var)); + memset(var, 0, sizeof(*var)); + tmp1 = tmp2 = strdup(names[i]); + strsep(&tmp2, "="); + var->name = strsep(&tmp1, "#"); + if (tmp1 != NULL) + var->instance = atoi(tmp1); + if (tmp2 != NULL) { + if (!wflag) + errx(1, "Must specify -w to write variables"); + var->val = atoi(tmp2); + } else + if (wflag) + errx(1, "Must not specify -w to read variables"); + *pnext = var; + pnext = &var->next; + + instance = 0; + cp = 0; + for (d = hid_start_parse(r, + 1<<hid_input | 1<<hid_output | 1<<hid_feature, -1); + hid_get_item(d, &h); ) { + if (h.kind == hid_collection) { + cp += sprintf(&colls[cp], "%s%s:%s", + cp != 0 ? "." : "", + hid_usage_page(HID_PAGE(h.usage)), + hid_usage_in_page(h.usage)); + } else if (h.kind == hid_endcollection) { + tmp1 = strrchr(colls, '.'); + if (tmp1 != NULL) { + cp -= strlen(tmp1); + tmp1[0] = 0; + } else { + cp = 0; + colls[0] = 0; + } + } + if ((h.kind != hid_input && h.kind != hid_output && + h.kind != hid_feature) || (h.flags & HIO_CONST)) + continue; + snprintf(hname, sizeof(hname), "%s%s%s:%s", + colls, colls[0] != 0 ? "." : "", + hid_usage_page(HID_PAGE(h.usage)), + hid_usage_in_page(h.usage)); + t = strlen(hname) - strlen(var->name); + if (t > 0) { + if (strcmp(hname + t, var->name) != 0) + continue; + if (hname[t - 1] != '.') + continue; + } else if (strcmp(hname, var->name) != 0) + continue; + if (var->instance != instance++) + continue; + var->h = h; + break; + } + hid_end_parse(d); + if (var->h.usage == 0) + errx(1, "Unknown item '%s'", var->name); + } } -void +static void usage(void) { @@ -92,10 +188,14 @@ usage(void) " %s -f device " "[-l] [-n] [-r] [-t tablefile] [-v] [-x] -a\n", getprogname()); + fprintf(stderr, + " %s -f device " + "[-t tablefile] [-v] [-z] -w name=value\n", + getprogname()); exit(1); } -void +static void dumpitem(const char *label, struct hid_item *h) { if ((h->flags & HIO_CONST) && !verbose) @@ -134,7 +234,7 @@ hid_collection_type(int32_t type) return (num); } -void +static void dumpitems(report_desc_t r) { struct hid_data *d; @@ -174,23 +274,7 @@ dumpitems(report_desc_t r) printf("Total feature size %d bytes\n", size); } -void -rev(struct hid_item **p) -{ - struct hid_item *cur, *prev, *next; - - prev = 0; - cur = *p; - while(cur != 0) { - next = cur->next; - cur->next = prev; - prev = cur; - cur = next; - } - *p = prev; -} - -void +static void prdata(u_char *buf, struct hid_item *h) { u_int data; @@ -212,82 +296,162 @@ prdata(u_char *buf, struct hid_item *h) h->pos = pos; } -void +static void dumpdata(int f, report_desc_t rd, int loop) { - struct hid_data *d; - struct hid_item h, *hids, *n; - int r, dlen; + struct variable *var; + int dlen, havedata, i, match, r, rid, use_rid; u_char *dbuf; - u_int32_t colls[100]; - int sp = 0; - char namebuf[10000], *namep; - - hids = 0; - for (d = hid_start_parse(rd, 1<<hid_input, -1); - hid_get_item(d, &h); ) { - if (h.kind == hid_collection) - colls[++sp] = h.usage; - else if (h.kind == hid_endcollection) - --sp; - if (h.kind != hid_input || (h.flags & HIO_CONST)) + enum hid_kind kind; + + kind = 0; + rid = -1; + use_rid = !!hid_get_report_id(f); + do { + if (kind < 3) { + if (++rid >= 256) { + rid = 0; + kind++; + } + if (kind >= 3) + rid = -1; + for (var = vars; var; var = var->next) { + if (rid == var->h.report_ID && + kind == var->h.kind) + break; + } + if (var == NULL) + continue; + } + dlen = hid_report_size(rd, kind < 3 ? kind : hid_input, rid); + if (dlen <= 0) continue; - h.next = hids; - h.collection = colls[sp]; - hids = malloc(sizeof *hids); - *hids = h; - } - hid_end_parse(d); - rev(&hids); - dlen = hid_report_size(rd, hid_input, -1); - dbuf = malloc(dlen); - if (!loop) - if (hid_set_immed(f, 1) < 0) { - if (errno == EOPNOTSUPP) - warnx("device does not support immediate mode, only changes reported."); - else - err(1, "USB_SET_IMMED"); + dbuf = malloc(dlen); + memset(dbuf, 0, dlen); + if (kind < 3) { + dbuf[0] = rid; + r = hid_get_report(f, kind, dbuf, dlen); + if (r < 0) + warn("hid_get_report(rid %d)", rid); + havedata = !r && (rid == 0 || dbuf[0] == rid); + if (rid != 0) + dbuf[0] = rid; + } else { + r = read(f, dbuf, dlen); + if (r < 1) + err(1, "read error"); + havedata = 1; } - do { - r = read(f, dbuf, dlen); - if (r < 1) { - err(1, "read error"); + if (verbose) { + printf("Got %s report %d (%d bytes):", + kind == hid_output ? "output" : + kind == hid_feature ? "feature" : "input", + use_rid ? dbuf[0] : 0, dlen); + if (havedata) { + for (i = 0; i < dlen; i++) + printf(" %02x", dbuf[i]); + } + printf("\n"); } - for (n = hids; n; n = n->next) { - if (n->report_ID != 0 && dbuf[0] != n->report_ID) + match = 0; + for (var = vars; var; var = var->next) { + if ((kind < 3 ? kind : hid_input) != var->h.kind) + continue; + if (var->h.report_ID != 0 && + dbuf[0] != var->h.report_ID) continue; - namep = namebuf; - namep += sprintf(namep, "%s:%s.", - hid_usage_page(HID_PAGE(n->collection)), - hid_usage_in_page(n->collection)); - namep += sprintf(namep, "%s:%s", - hid_usage_page(HID_PAGE(n->usage)), - hid_usage_in_page(n->usage)); - if (all || gotname(namebuf)) { - if (!noname) - printf("%s=", namebuf); - prdata(dbuf, n); + match = 1; + if (!noname) + printf("%s=", var->name); + if (havedata) + prdata(dbuf, &var->h); + printf("\n"); + } + if (match) + printf("\n"); + free(dbuf); + } while (loop || kind < 3); +} + +static void +writedata(int f, report_desc_t rd) +{ + struct variable *var; + int dlen, i, r, rid; + u_char *dbuf; + enum hid_kind kind; + + kind = 0; + rid = 0; + for (kind = 0; kind < 3; kind ++) { + for (rid = 0; rid < 256; rid ++) { + for (var = vars; var; var = var->next) { + if (rid == var->h.report_ID && kind == var->h.kind) + break; + } + if (var == NULL) + continue; + dlen = hid_report_size(rd, kind, rid); + if (dlen <= 0) + continue; + dbuf = malloc(dlen); + memset(dbuf, 0, dlen); + dbuf[0] = rid; + if (!zflag && hid_get_report(f, kind, dbuf, dlen) == 0) { + if (verbose) { + printf("Got %s report %d (%d bytes):", + kind == hid_input ? "input" : + kind == hid_output ? "output" : "feature", + rid, dlen); + for (i = 0; i < dlen; i++) + printf(" %02x", dbuf[i]); printf("\n"); } + } else if (!zflag) { + warn("hid_get_report(rid %d)", rid); + if (verbose) { + printf("Can't get %s report %d (%d bytes). " + "Will be initialized with zeros.\n", + kind == hid_input ? "input" : + kind == hid_output ? "output" : "feature", + rid, dlen); + } } - if (loop) + for (var = vars; var; var = var->next) { + if (rid != var->h.report_ID || kind != var->h.kind) + continue; + hid_set_data(dbuf, &var->h, var->val); + } + if (verbose) { + printf("Setting %s report %d (%d bytes):", + kind == hid_output ? "output" : + kind == hid_feature ? "feature" : "input", + rid, dlen); + for (i = 0; i < dlen; i++) + printf(" %02x", dbuf[i]); printf("\n"); - } while (loop); - free(dbuf); + } + r = hid_set_report(f, kind, dbuf, dlen); + if (r != 0) + warn("hid_set_report(rid %d)", rid); + free(dbuf); + } + } } int main(int argc, char **argv) { - int f; report_desc_t r; - char devnam[100], *dev = 0; + char *table = 0; + char devnam[100], *dev = NULL; + int f; + int all = 0; int ch; int repdump = 0; int loop = 0; - char *table = 0; - while ((ch = getopt(argc, argv, "af:lnrt:vx")) != -1) { + while ((ch = getopt(argc, argv, "af:lnrt:vwxz")) != -1) { switch(ch) { case 'a': all++; @@ -310,9 +474,15 @@ main(int argc, char **argv) case 'v': verbose++; break; + case 'w': + wflag = 1; + break; case 'x': hexdump = 1; break; + case 'z': + zflag = 1; + break; case '?': default: usage(); @@ -320,12 +490,10 @@ main(int argc, char **argv) } argc -= optind; argv += optind; - if (dev == 0) + if (dev == NULL) usage(); - names = argv; - nnames = argc; - if (nnames == 0 && !all && !repdump) + if (argc == 0 && !all && !repdump) usage(); if (dev[0] != '/') { @@ -350,8 +518,13 @@ main(int argc, char **argv) printf("Report descriptor:\n"); dumpitems(r); } - if (nnames != 0 || all) - dumpdata(f, r, loop); + if (argc != 0 || all) { + parceargs(r, all, argc, argv); + if (wflag) + writedata(f, r); + else + dumpdata(f, r, loop); + } hid_dispose_report_desc(r); exit(0); |