diff options
author | Nathan Binkert <nate@cvs.openbsd.org> | 2002-05-10 00:09:18 +0000 |
---|---|---|
committer | Nathan Binkert <nate@cvs.openbsd.org> | 2002-05-10 00:09:18 +0000 |
commit | a82d07519e6786b844ed49d488f67672db78daa2 (patch) | |
tree | 9ed579bb5297f1a9a77613ce6171eb968d7bf508 | |
parent | 0dd51d96675add74c11132637e3499fceb185340 (diff) |
Update usb userland stuff to reflect hid changes in the kernel.
This adds the new program usbhidaction which can be used to assign actions
to events that occur on a uhid device. For example, you can now make the
volume buttons on some newer keyboards actually do something.
-rw-r--r-- | lib/libusbhid/data.c | 4 | ||||
-rw-r--r-- | lib/libusbhid/descr.c | 4 | ||||
-rw-r--r-- | lib/libusbhid/parse.c | 147 | ||||
-rw-r--r-- | lib/libusbhid/usage.c | 13 | ||||
-rw-r--r-- | lib/libusbhid/usbhid.3 | 36 | ||||
-rw-r--r-- | lib/libusbhid/usbhid.h | 16 | ||||
-rw-r--r-- | lib/libusbhid/usbvar.h | 4 | ||||
-rw-r--r-- | usr.bin/usbhidaction/Makefile | 10 | ||||
-rw-r--r-- | usr.bin/usbhidaction/usbhidaction.1 | 137 | ||||
-rw-r--r-- | usr.bin/usbhidaction/usbhidaction.c | 439 | ||||
-rw-r--r-- | usr.bin/usbhidctl/usbhid.c | 447 | ||||
-rw-r--r-- | usr.bin/usbhidctl/usbhidctl.1 | 99 | ||||
-rw-r--r-- | usr.sbin/usbdevs/usbdevs.8 | 13 | ||||
-rw-r--r-- | usr.sbin/usbdevs/usbdevs.c | 45 |
14 files changed, 1148 insertions, 266 deletions
diff --git a/lib/libusbhid/data.c b/lib/libusbhid/data.c index 57571967fe4..371fd99dfee 100644 --- a/lib/libusbhid/data.c +++ b/lib/libusbhid/data.c @@ -1,5 +1,5 @@ -/* $OpenBSD: data.c,v 1.1 2001/12/30 07:04:38 pvalchev Exp $ */ -/* $NetBSD: data.c,v 1.8 2000/04/02 11:10:53 augustss Exp $ */ +/* $OpenBSD: data.c,v 1.2 2002/05/10 00:09:17 nate Exp $ */ +/* $NetBSD: data.c,v 1.1 2001/12/28 17:45:26 augustss Exp $ */ /* * Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org> diff --git a/lib/libusbhid/descr.c b/lib/libusbhid/descr.c index 7366784338b..35095a406c6 100644 --- a/lib/libusbhid/descr.c +++ b/lib/libusbhid/descr.c @@ -1,5 +1,5 @@ -/* $OpenBSD: descr.c,v 1.2 2002/05/02 20:12:07 nate Exp $ */ -/* $NetBSD: descr.c,v 1.9 2000/09/24 02:13:24 augustss Exp $ */ +/* $OpenBSD: descr.c,v 1.3 2002/05/10 00:09:17 nate Exp $ */ +/* $NetBSD: descr.c,v 1.2 2002/02/20 20:31:07 christos Exp $ */ /* * Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org> diff --git a/lib/libusbhid/parse.c b/lib/libusbhid/parse.c index 737812254b1..4bc72344a5a 100644 --- a/lib/libusbhid/parse.c +++ b/lib/libusbhid/parse.c @@ -1,8 +1,8 @@ -/* $OpenBSD: parse.c,v 1.1 2001/12/30 07:04:38 pvalchev Exp $ */ -/* $NetBSD: parse.c,v 1.11 2000/09/24 02:19:54 augustss Exp $ */ +/* $OpenBSD: parse.c,v 1.2 2002/05/10 00:09:17 nate Exp $ */ +/* $NetBSD: parse.c,v 1.2 2001/12/29 20:44:22 augustss Exp $ */ /* - * Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org> + * Copyright (c) 1999, 2001 Lennart Augustsson <augustss@netbsd.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,18 +46,30 @@ struct hid_data { unsigned int usages[MAXUSAGE]; int nusage; int minset; + int logminsize; int multi; int multimax; int kindset; + int reportid; - /* 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]; + /* + * 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) { @@ -72,17 +84,21 @@ hid_clear_local(hid_item_t *c) c->string_minimum = 0; c->string_maximum = 0; c->set_delimiter = 0; + c->report_size = 0; } hid_data_t -hid_start_parse(report_desc_t d, int kindset) +hid_start_parse(report_desc_t d, int kindset, int id) { - struct hid_data *s = malloc(sizeof *s); + struct hid_data *s; + s = malloc(sizeof *s); memset(s, 0, sizeof *s); s->start = s->p = d->data; s->end = d->data + d->size; s->kindset = kindset; + s->reportid = id; + s->hassavedcoll = 0; return (s); } @@ -101,12 +117,38 @@ hid_end_parse(hid_data_t s) int hid_get_item(hid_data_t s, hid_item_t *h) { + int r; + + for (;;) { + r = hid_get_item_raw(s, h); + if (r <= 0) + break; + if (h->report_ID == s->reportid || s->reportid == -1) + break; + } + return (r); +} + +#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; 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; @@ -114,13 +156,21 @@ hid_get_item(hid_data_t s, hid_item_t *h) 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; + } if (s->multi < s->multimax) { c->usage = s->usages[min(s->multi, s->nusage-1)]; s->multi++; *h = *c; - - /* 'multimax' is only non-zero if the current - item kind is input/output/feature */ + /* + * '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; @@ -163,12 +213,12 @@ hid_get_item(hid_data_t s, hid_item_t *h) dval = 0; break; case 1: - dval = (int8_t)*data++; + dval = /*(int8_t)*/*data++; break; case 2: dval = *data++; dval |= *data++ << 8; - dval = (int16_t)dval; + dval = /*(int16_t)*/dval; break; case 4: dval = *data++; @@ -205,6 +255,8 @@ hid_get_item(hid_data_t s, hid_item_t *h) if (s->nusage < MAXUSAGE-1) s->nusage++; } + c->usage_minimum = 0; + c->usage_maximum = 0; s->minset = 0; } goto top; @@ -214,7 +266,8 @@ hid_get_item(hid_data_t s, hid_item_t *h) *h = *c; h->next = 0; h->pos = s->kindpos[c->kind]; - s->kindpos[c->kind] += c->report_size * c->report_count; + s->kindpos[c->kind] += + c->report_size * c->report_count; hid_clear_local(c); s->minset = 0; return (1); @@ -226,15 +279,25 @@ hid_get_item(hid_data_t s, hid_item_t *h) c->kind = hid_collection; c->collection = dval; c->collevel++; - *h = *c; + nc = *c; hid_clear_local(c); - c->report_ID = NO_REPORT_ID; + /*c->report_ID = NO_REPORT_ID;*/ s->nusage = 0; - return (1); + 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 */ retkind = hid_feature; goto ret; case 12: /* End collection */ + REPORT_SAVED_COLL; c->kind = hid_endcollection; c->collevel--; *h = *c; @@ -244,6 +307,7 @@ hid_get_item(hid_data_t s, hid_item_t *h) default: return (-2); } + break; case 1: /* Global */ switch (bTag) { @@ -252,6 +316,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; @@ -273,6 +338,9 @@ hid_get_item(hid_data_t s, hid_item_t *h) break; case 8: c->report_ID = dval; + s->kindpos[hid_input] = + s->kindpos[hid_output] = + s->kindpos[hid_feature] = 0; break; case 9: c->report_count = dval; @@ -294,29 +362,17 @@ hid_get_item(hid_data_t s, hid_item_t *h) case 2: /* Local */ switch (bTag) { case 0: - if (bSize == 1) - dval = c->_usage_page | (dval&0xff); - else if (bSize == 2) - dval = c->_usage_page | (dval&0xffff); - c->usage = dval; + c->usage = c->_usage_page | dval; if (s->nusage < MAXUSAGE) - s->usages[s->nusage++] = dval; + s->usages[s->nusage++] = c->usage; /* else XXX */ break; case 1: s->minset = 1; - if (bSize == 1) - dval = c->_usage_page | (dval&0xff); - else if (bSize == 2) - dval = c->_usage_page | (dval&0xffff); - c->usage_minimum = dval; + c->usage_minimum = c->_usage_page | dval; break; case 2: - if (bSize == 1) - dval = c->_usage_page | (dval&0xff); - else if (bSize == 2) - dval = c->_usage_page | (dval&0xffff); - c->usage_maximum = dval; + c->usage_maximum = c->_usage_page | dval; break; case 3: c->designator_index = dval; @@ -350,35 +406,30 @@ hid_get_item(hid_data_t s, hid_item_t *h) } int -hid_report_size(report_desc_t r, enum hid_kind k, int *idp) +hid_report_size(report_desc_t r, enum hid_kind k, int id) { struct hid_data *d; hid_item_t h; - int size, id; + int size; - id = 0; - if (idp) - *idp = 0; memset(&h, 0, sizeof h); - for (d = hid_start_parse(r, 1<<k); hid_get_item(d, &h); ) { - if (h.report_ID != NO_REPORT_ID) { - if (idp) - *idp = h.report_ID; - id = 8; + size = 0; + for (d = hid_start_parse(r, 1<<k, id); hid_get_item(d, &h); ) { + if (h.report_ID == id && h.kind == k) { + size = d->kindpos[k]; } } - - size = d->kindpos[k] + id; hid_end_parse(d); return ((size + 7) / 8); } int -hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k, hid_item_t *h) +hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k, + hid_item_t *h, int id) { hid_data_t d; - for (d = hid_start_parse(desc, 1<<k); hid_get_item(d, h); ) { + for (d = hid_start_parse(desc, 1<<k, id); hid_get_item(d, h); ) { if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) { hid_end_parse(d); return (1); diff --git a/lib/libusbhid/usage.c b/lib/libusbhid/usage.c index fb36769d243..e5122c1745e 100644 --- a/lib/libusbhid/usage.c +++ b/lib/libusbhid/usage.c @@ -1,5 +1,5 @@ -/* $OpenBSD: usage.c,v 1.1 2001/12/30 07:04:38 pvalchev Exp $ */ -/* $NetBSD: usage.c,v 1.11 2001/01/09 15:59:47 augustss Exp $ */ +/* $OpenBSD: usage.c,v 1.2 2002/05/10 00:09:17 nate Exp $ */ +/* $NetBSD: usage.c,v 1.1 2001/12/28 17:45:27 augustss Exp $ */ /* * Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org> @@ -163,7 +163,7 @@ hid_usage_page(int i) for (k = 0; k < npages; k++) if (pages[k].usage == i) return pages[k].name; - snprintf(b, sizeof b, "0x%04x", i); + sprintf(b, "0x%04x", i); return b; } @@ -183,7 +183,7 @@ hid_usage_in_page(unsigned int u) for (j = 0; j < pages[k].pagesize; j++) { us = pages[k].page_contents[j].usage; if (us == -1) { - snprintf(b, sizeof b, "%s %d", + snprintf(b, sizeof b, "%s %d", pages[k].page_contents[j].name, i); return b; } @@ -191,7 +191,7 @@ hid_usage_in_page(unsigned int u) return pages[k].page_contents[j].name; } bad: - snprintf(b, sizeof b, "0x%04x", i); + sprintf(b, "0x%04x", i); return b; } @@ -213,10 +213,11 @@ hid_parse_usage_page(const char *name) int hid_parse_usage_in_page(const char *name) { - const char *sep = strchr(name, ':'); + const char *sep; int k, j; unsigned int l; + sep = strchr(name, ':'); if (sep == NULL) return -1; l = sep - name; diff --git a/lib/libusbhid/usbhid.3 b/lib/libusbhid/usbhid.3 index 066d3a8da34..5d2c042a1dd 100644 --- a/lib/libusbhid/usbhid.3 +++ b/lib/libusbhid/usbhid.3 @@ -1,7 +1,7 @@ -.\" $OpenBSD: usbhid.3,v 1.3 2002/05/01 08:03:30 mpech Exp $ -.\" $NetBSD: usb.3,v 1.14 2001/04/09 12:09:49 wiz Exp $ +.\" $OpenBSD: usbhid.3,v 1.4 2002/05/10 00:09:17 nate Exp $ +.\" $NetBSD: usbhid.3,v 1.5 2002/02/07 07:00:52 ross Exp $ .\" -.\" Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org> +.\" Copyright (c) 1999, 2001 Lennart Augustsson <augustss@netbsd.org> .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 11, 1999 +.Dd December 29, 2001 .Dt USBHID 3 .Os .Sh NAME @@ -45,7 +45,7 @@ .Nm hid_set_data .Nd USB HID access routines .Sh SYNOPSIS -.Fd #include <usbhid.h> +.Fd #include \*[Lt]usbhid.h\*[Gt] .Ft report_desc_t .Fn hid_get_report_desc "int file" .Ft report_desc_t @@ -53,15 +53,15 @@ .Ft void .Fn hid_dispose_report_desc "report_desc_t d" .Ft hid_data_t -.Fn hid_start_parse "report_desc_t d" "int kindset" +.Fn hid_start_parse "report_desc_t d" "int kindset" "int id" .Ft void .Fn hid_end_parse "hid_data_t s" .Ft int .Fn hid_get_item "hid_data_t s" "hid_item_t *h" .Ft int -.Fn hid_report_size "report_desc_t d" "hid_kind_t k" "int *idp" +.Fn hid_report_size "report_desc_t d" "hid_kind_t k" "int id" .Ft int -.Fn hid_locate "report_desc_t d" "u_int usage" "hid_kind_t k" "hid_item_t *h" +.Fn hid_locate "report_desc_t d" "u_int usage" "hid_kind_t k" "hid_item_t *h" "int id" .Ft char * .Fn hid_usage_page "int i" .Ft char * @@ -115,11 +115,13 @@ To parse the report descriptor the function should be called with a report descriptor and a set that describes which items that are interesting. The set is obtained by or-ing together values -.Fa "(1 << k)" +.Fa "(1 \*[Lt]\*[Lt] k)" where .Fa k is an item of type .Fa hid_kind_t . +The report id (if present) is given by +.Fa id . The function returns .Fa NULL if the initialization fails, otherwise an opaque value to be used @@ -140,7 +142,7 @@ will be filled with the relevant data for the item. The definition of .Fa hid_item_t can be found in -.Pa <usbhid.h> +.Pa \*[Lt]usbhid.h\*[Gt] and the meaning of the components in the USB HID documentation. .Pp Data should be read/written to the device in the size of @@ -148,9 +150,8 @@ the report. The size of a report (of a certain kind) can be computed by the .Fn hid_report_size function. -If the report is prefixed by an ID byte it is stored at -.Fa idp , -otherwise it will contain 0. +If the report is prefixed by an ID byte it is given by +.Fa id . .Pp To locate a single item the .Fn hid_locate @@ -192,18 +193,15 @@ function extracts the value of the item. Conversely .Fn hid_set_data can be used to put data into a report (which must be zeroed first). -.Sh EXAMPLES -Not yet. .Sh FILES .Pa /usr/share/misc/usb_hid_usages The default HID usage table. -.Sh BUGS -This man page is woefully incomplete. +.\" .Sh EXAMPLES .Sh SEE ALSO The .Tn USB specifications can be found at: -.Dv http://www.usb.org/developers/docs.html +.Dv http://www.usb.org/developers/docs.html . .Pp .Xr uhid 4 , .Xr usb 4 @@ -212,3 +210,5 @@ The .Nm library first appeared in .Ox 3.0 . +.Sh BUGS +This man page is woefully incomplete. diff --git a/lib/libusbhid/usbhid.h b/lib/libusbhid/usbhid.h index 57c0b83bde9..7172ca5fac8 100644 --- a/lib/libusbhid/usbhid.h +++ b/lib/libusbhid/usbhid.h @@ -1,4 +1,4 @@ -/* $OpenBSD: usbhid.h,v 1.1 2001/12/30 07:04:38 pvalchev Exp $ */ +/* $OpenBSD: usbhid.h,v 1.2 2002/05/10 00:09:17 nate Exp $ */ /* $NetBSD: usbhid.h,v 1.1 2001/12/28 17:45:27 augustss Exp $ */ /* @@ -32,8 +32,12 @@ typedef struct report_desc *report_desc_t; typedef struct hid_data *hid_data_t; typedef enum hid_kind { - hid_input, hid_output, hid_feature, hid_collection, hid_endcollection -}hid_kind_t; + hid_input = 0, + hid_output = 1, + hid_feature = 2, + hid_collection, + hid_endcollection +} hid_kind_t; typedef struct hid_item { /* Global */ @@ -79,11 +83,11 @@ report_desc_t hid_use_report_desc(unsigned char *data, unsigned int size); void hid_dispose_report_desc(report_desc_t); /* Parsing of a HID report descriptor, parse.c: */ -hid_data_t hid_start_parse(report_desc_t d, int kindset); +hid_data_t hid_start_parse(report_desc_t d, int kindset, int id); 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 *idp); -int hid_locate(report_desc_t d, unsigned int usage, enum hid_kind k, 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); /* Conversion to/from usage names, usage.c: */ const char *hid_usage_page(int i); diff --git a/lib/libusbhid/usbvar.h b/lib/libusbhid/usbvar.h index ef23a3d3bfa..224423ea99f 100644 --- a/lib/libusbhid/usbvar.h +++ b/lib/libusbhid/usbvar.h @@ -1,5 +1,5 @@ -/* $OpenBSD: usbvar.h,v 1.1 2001/12/30 07:04:38 pvalchev Exp $ */ -/* $NetBSD: usbvar.h,v 1.2 1999/05/11 21:15:46 augustss Exp $ */ +/* $OpenBSD: usbvar.h,v 1.2 2002/05/10 00:09:17 nate Exp $ */ +/* $NetBSD: usbvar.h,v 1.1 2001/12/28 17:45:27 augustss Exp $ */ /* * Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org> diff --git a/usr.bin/usbhidaction/Makefile b/usr.bin/usbhidaction/Makefile new file mode 100644 index 00000000000..1ce2fe8b7ad --- /dev/null +++ b/usr.bin/usbhidaction/Makefile @@ -0,0 +1,10 @@ +# $OpenBSD: Makefile,v 1.1 2002/05/10 00:09:17 nate Exp $ +# $NetBSD: Makefile,v 1.4 2002/02/02 16:54:26 veego Exp $ + +PROG= usbhidaction +SRCS= usbhidaction.c + +LDADD+= -lusbhid -lutil +DPADD+= ${LIBUSBHID} ${LIBUTIL} + +.include <bsd.prog.mk> diff --git a/usr.bin/usbhidaction/usbhidaction.1 b/usr.bin/usbhidaction/usbhidaction.1 new file mode 100644 index 00000000000..9f075b80b13 --- /dev/null +++ b/usr.bin/usbhidaction/usbhidaction.1 @@ -0,0 +1,137 @@ +.\" $OpenBSD: usbhidaction.1,v 1.1 2002/05/10 00:09:17 nate Exp $ +.\" $NetBSD: usbhidaction.1,v 1.6 2002/01/18 14:38:59 augustss Exp $ +.\" +.\" Copyright (c) 2000 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Lennart Augustsson (lennart@augustsson.net). +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the NetBSD +.\" Foundation, Inc. and its contributors. +.\" 4. Neither the name of The NetBSD Foundation nor the names of its +.\" contributors may be used to endorse or promote products derived +.\" from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd December 29, 2000 +.Dt USBHIDACTION 1 +.Os +.Sh NAME +.Nm usbhidaction +.Nd perform actions according to USB HID controls +.Sh SYNOPSIS +.Nm +.Fl c Ar config-file +.Op Fl d +.Op Fl i +.Fl f Ar device +.Op Fl v +.Ar arg ... +.Sh DESCRIPTION +.Nm +can be used to execute commands when certain values appear on HID controls. +The normal operation for this program is to read the configuration file +and then become a daemon and execute commands as the HID items specify. +If a read from the HID device fails the program dies; this will make it +die when the USB device is unplugged. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl c Ar config-file +Specify a path name for the config file. +.It Fl d +Toggle the daemon flag. +.It Fl i +Ignore HID items in the config file that does not exist in the device. +.It Fl f Ar device +Specify a path name for the device to operate on. If +.Ar device +is numeric, it is taken to be the USB HID device number. If it is a relative +path, it is taken to be the name of the device under +.Pa /dev . +An absolute path is taken to be the literal device pathname. +.It Fl v +Be verbose, and do not become a daemon. +.El +.Pp +The config file will be re-read if the process gets a HUP signal. +.Sh CONFIGURATION +The configuration file has a very simple format. Each line describes an +action; if a line begins with a whitespace it is considered a continuation +of the previous line. Lines beginning with `#' are considered as comments. +.Pp +Each line has three parts: a name of a USB HID item, a value for that item, +and an action. There must be whitespace between the parts. +.Pp +The item names are similar to those used by +.Xr usbhidctl 1 , +but each part must be prefixed by its page name. +.Pp +The value is simply a numeric value. When the item reports this value +the action will be performed. +If the value is `*' it will match any value. +.Pp +The action is a normal command that is executed with +.Xr system 3 . +Before it is executed some substitution will occur: +`$n' will be replaced by the nth argument on the +command line, `$V' will be replaced by the numeric value +of the HID item, `$N' will be replaced by the name +of the control, and `$H' will be replaced by the name +of the HID device. +.Sh FILES +.Pa /usr/share/misc/usb_hid_usages +The HID usage table. +.Sh EXAMPLES +The following configuration file can be used to control a pair +of Philips USB speakers with the HID controls on the speakers. +.Bd -literal -offset indent +# Configuration for various Philips USB speakers +Consumer:Consumer_Control.Consumer:Volume_Up 1 + mixerctl -f $1 -n -w fea8-i7-master++ +Consumer:Consumer_Control.Consumer:Volume_Down 1 + mixerctl -f $1 -n -w fea8-i7-master-- +Consumer:Consumer_Control.Consumer:Mute 1 + mixerctl -f $1 -n -w fea8-i7-mute++ +Consumer:Consumer_Control.Consumer:Channel_Top.Microsoft:Base_Up 1 + mixerctl -f $1 -n -w fea8-i7-bass++ +Consumer:Consumer_Control.Consumer:Channel_Top.Microsoft:Base_Down 1 + mixerctl -f $1 -n -w fea8-i7-bass-- +.Ed +.Pp +A sample invocation using this configuration would be +.Bd -literal -offset indent +usbhidaction -f /dev/uhid1 -c conf /dev/mixer1 +.Ed +.Sh SEE ALSO +.Xr usbhidctl 1 , +.Xr usbhid 3 , +.Xr uhid 4 , +.Xr usb 4 +.Sh HISTORY +The +.Nm +command first appeared in +.Ox 3.2 . diff --git a/usr.bin/usbhidaction/usbhidaction.c b/usr.bin/usbhidaction/usbhidaction.c new file mode 100644 index 00000000000..7b218f5e49f --- /dev/null +++ b/usr.bin/usbhidaction/usbhidaction.c @@ -0,0 +1,439 @@ +/* $OpenBSD: usbhidaction.c,v 1.1 2002/05/10 00:09:17 nate Exp $ */ +/* $NetBSD: usbhidaction.c,v 1.7 2002/01/18 14:38:59 augustss Exp $ */ + +/* + * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson <lennart@augustsson.net>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <limits.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> +#include <usbhid.h> +#include <util.h> +#include <syslog.h> +#include <signal.h> + +int verbose = 0; +int isdemon = 0; +int reparse = 1; + +struct command { + struct command *next; + int line; + + struct hid_item item; + int value; + char anyvalue; + char *name; + char *action; +}; +struct command *commands; + +#define SIZE 4000 + +void usage(void); +struct command *parse_conf(const char *, report_desc_t, int, int); +void docmd(struct command *, int, const char *, int, char **); +void freecommands(struct command *); + +static void +sighup(int sig) +{ + reparse = 1; +} + +int +main(int argc, char **argv) +{ + const char *conf = NULL; + const char *dev = NULL; + int fd, ch, sz, n, val, i; + int demon, ignore; + report_desc_t repd; + char buf[100]; + char devnamebuf[PATH_MAX]; + struct command *cmd; + int reportid; + + demon = 1; + ignore = 0; + while ((ch = getopt(argc, argv, "c:df:iv")) != -1) { + switch(ch) { + case 'c': + conf = optarg; + break; + case 'd': + demon ^= 1; + break; + case 'i': + ignore++; + break; + case 'f': + dev = optarg; + break; + case 'v': + demon = 0; + verbose++; + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (conf == NULL || dev == NULL) + usage(); + + hid_init(NULL); + + if (dev[0] != '/') { + snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s", + isdigit(dev[0]) ? "uhid" : "", dev); + dev = devnamebuf; + } + + fd = open(dev, O_RDWR); + if (fd < 0) + err(1, "%s", dev); + if (ioctl(fd, USB_GET_REPORT_ID, &reportid) < 0) + reportid = -1; + repd = hid_get_report_desc(fd); + if (repd == NULL) + err(1, "hid_get_report_desc() failed\n"); + + commands = parse_conf(conf, repd, reportid, ignore); + + sz = hid_report_size(repd, hid_input, reportid); + + if (verbose) + printf("report size %d\n", sz); + if (sz > sizeof buf) + errx(1, "report too large"); + + (void)signal(SIGHUP, sighup); + + if (demon) { + if (daemon(0, 0) < 0) + err(1, "daemon()"); + pidfile(NULL); + isdemon = 1; + } + + for(;;) { + n = read(fd, buf, sz); + if (verbose > 2) { + printf("read %d bytes:", n); + for (i = 0; i < n; i++) + printf(" %02x", buf[i]); + printf("\n"); + } + if (n < 0) { + if (verbose) + err(1, "read"); + else + exit(1); + } +#if 0 + if (n != sz) { + err(2, "read size"); + } +#endif + for (cmd = commands; cmd; cmd = cmd->next) { + val = hid_get_data(buf, &cmd->item); + if (cmd->value == val || cmd->anyvalue) + docmd(cmd, val, dev, argc, argv); + } + if (reparse) { + struct command *cmds = + parse_conf(conf, repd, reportid, ignore); + if (cmds) { + freecommands(commands); + commands = cmds; + } + reparse = 0; + } + } + + exit(0); +} + +void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "Usage: %s -c config_file [-d] -f hid_dev " + "[-i] [-v]\n", __progname); + exit(1); +} + +static int +peek(FILE *f) +{ + int c; + + c = getc(f); + if (c != EOF) + ungetc(c, f); + return c; +} + +struct command * +parse_conf(const char *conf, report_desc_t repd, int reportid, int ignore) +{ + FILE *f; + char *p; + int line; + char buf[SIZE], name[SIZE], value[SIZE], action[SIZE]; + char usage[SIZE], coll[SIZE]; + struct command *cmd, *cmds; + struct hid_data *d; + struct hid_item h; + int u, lo, hi, range; + + + f = fopen(conf, "r"); + if (f == NULL) + err(1, "%s", conf); + + cmds = NULL; + for (line = 1; ; line++) { + if (fgets(buf, sizeof buf, f) == NULL) + break; + if (buf[0] == '#' || buf[0] == '\n') + continue; + p = strchr(buf, '\n'); + while (p && isspace(peek(f))) { + if (fgets(p, sizeof buf - strlen(buf), f) == NULL) + break; + p = strchr(buf, '\n'); + } + if (p) + *p = 0; + if (sscanf(buf, "%s %s %[^\n]", name, value, action) != 3) { + if (isdemon) { + syslog(LOG_WARNING, "config file `%s', line %d" + ", syntax error: %s", conf, line, buf); + freecommands(cmds); + return (NULL); + } else { + errx(1, "config file `%s', line %d," + ", syntax error: %s", conf, line, buf); + } + } + + cmd = malloc(sizeof *cmd); + if (cmd == NULL) + err(1, "malloc failed"); + cmd->next = cmds; + cmds = cmd; + cmd->line = line; + + if (strcmp(value, "*") == 0) { + cmd->anyvalue = 1; + } else { + cmd->anyvalue = 0; + if (sscanf(value, "%d", &cmd->value) != 1) { + if (isdemon) { + syslog(LOG_WARNING, + "config file `%s', line %d, " + "bad value: %s\n", + conf, line, value); + freecommands(cmds); + return (NULL); + } else { + errx(1, "config file `%s', line %d, " + "bad value: %s\n", + conf, line, value); + } + } + } + + coll[0] = 0; + for (d = hid_start_parse(repd, 1 << hid_input, reportid); + hid_get_item(d, &h); ) { + if (verbose > 2) + printf("kind=%d usage=%x\n", h.kind, h.usage); + if (h.flags & HIO_CONST) + continue; + switch (h.kind) { + case hid_input: + if (h.usage_minimum != 0 || + h.usage_maximum != 0) { + lo = h.usage_minimum; + hi = h.usage_maximum; + range = 1; + } else { + lo = h.usage; + hi = h.usage; + range = 0; + } + for (u = lo; u <= hi; u++) { + snprintf(usage, sizeof usage, "%s:%s", + hid_usage_page(HID_PAGE(u)), + hid_usage_in_page(u)); + if (verbose > 2) + printf("usage %s\n", usage); + if (!strcasecmp(usage, name)) + goto foundhid; + if (coll[0]) { + snprintf(usage, sizeof usage, + "%s.%s:%s", coll+1, + hid_usage_page(HID_PAGE(u)), + hid_usage_in_page(u)); + if (verbose > 2) + printf("usage %s\n", + usage); + if (!strcasecmp(usage, name)) + goto foundhid; + } + } + break; + case hid_collection: + snprintf(coll + strlen(coll), + sizeof coll - strlen(coll), ".%s:%s", + hid_usage_page(HID_PAGE(h.usage)), + hid_usage_in_page(h.usage)); + break; + case hid_endcollection: + if (coll[0]) + *strrchr(coll, '.') = 0; + break; + default: + break; + } + } + if (ignore) { + if (verbose) + warnx("ignore item '%s'\n", name); + continue; + } + if (isdemon) { + syslog(LOG_WARNING, "config file `%s', line %d, HID " + "item not found: `%s'\n", conf, line, name); + freecommands(cmds); + return (NULL); + } else { + errx(1, "config file `%s', line %d, HID item " + "not found: `%s'\n", conf, line, name); + } + + foundhid: + hid_end_parse(d); + cmd->item = h; + cmd->name = strdup(name); + cmd->action = strdup(action); + if (range) { + if (cmd->value == 1) + cmd->value = u - lo; + else + cmd->value = -1; + } + + if (verbose) + printf("PARSE:%d %s, %d, '%s'\n", cmd->line, name, + cmd->value, cmd->action); + } + fclose(f); + return (cmds); +} + +void +docmd(struct command *cmd, int value, const char *hid, int argc, char **argv) +{ + char cmdbuf[SIZE], *p, *q; + size_t len; + int n, r; + + for (p = cmd->action, q = cmdbuf; *p && q < &cmdbuf[SIZE-1]; ) { + if (*p == '$') { + p++; + len = &cmdbuf[SIZE-1] - q; + if (isdigit(*p)) { + n = strtol(p, &p, 10) - 1; + if (n >= 0 && n < argc) { + strncpy(q, argv[n], len); + q += strlen(q); + } + } else if (*p == 'V') { + p++; + snprintf(q, len, "%d", value); + q += strlen(q); + } else if (*p == 'N') { + p++; + strncpy(q, cmd->name, len); + q += strlen(q); + } else if (*p == 'H') { + p++; + strncpy(q, hid, len); + q += strlen(q); + } else if (*p) { + *q++ = *p++; + } + } else { + *q++ = *p++; + } + } + *q = 0; + + if (verbose) + printf("system '%s'\n", cmdbuf); + r = system(cmdbuf); + if (verbose > 1 && r) + printf("return code = 0x%x\n", r); +} + +void +freecommands(struct command *cmd) +{ + struct command *next; + + while (cmd) { + next = cmd->next; + free(cmd); + cmd = next; + } +} diff --git a/usr.bin/usbhidctl/usbhid.c b/usr.bin/usbhidctl/usbhid.c index 1751eb8d1ea..83dd7f7abf7 100644 --- a/usr.bin/usbhidctl/usbhid.c +++ b/usr.bin/usbhidctl/usbhid.c @@ -1,8 +1,8 @@ -/* $OpenBSD: usbhid.c,v 1.3 2002/05/02 20:12:07 nate Exp $ */ -/* $NetBSD: usbhid.c,v 1.17 2001/03/28 03:17:42 simonb Exp $ */ +/* $OpenBSD: usbhid.c,v 1.4 2002/05/10 00:09:17 nate Exp $ */ +/* $NetBSD: usbhid.c,v 1.22 2002/02/20 20:30:42 christos Exp $ */ /* - * Copyright (c) 2000 The NetBSD Foundation, Inc. + * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -53,14 +53,18 @@ #include <unistd.h> #include <usbhid.h> +/* + * Zero if not in a verbose mode. Greater levels of verbosity + * are indicated by values larger than one. + */ +unsigned int verbose; + /* Parser tokens */ #define DELIM_USAGE '.' #define DELIM_PAGE ':' #define DELIM_SET '=' -/* Zero if not in a verbose mode. Greater levels of verbosity are - indicated by values larger than one. */ -static unsigned int verbose; +static int reportid; struct Susbvar { /* Variable name, not NUL terminated */ @@ -77,6 +81,7 @@ struct Susbvar { #define MATCH_SHOWPAGENAME (1 << 5) #define MATCH_SHOWNUMERIC (1 << 6) #define MATCH_WRITABLE (1 << 7) +#define MATCH_SHOWVALUES (1 << 8) unsigned int mflags; /* Workspace for hidmatch() */ @@ -108,11 +113,183 @@ static struct { #define REPORT_MAXVAL 2 }; +/* + * Extract 16-bit unsigned usage ID from a numeric string. Returns -1 + * if string failed to parse correctly. + */ +static int +strtousage(const char *nptr, size_t nlen) +{ + char *endptr; + long result; + char numstr[16]; + + if (nlen >= sizeof(numstr) || !isdigit((unsigned char)*nptr)) + return -1; + + /* + * We use strtol() here, but unfortunately strtol() requires a + * NUL terminated string - which we don't have - at least not + * officially. + */ + memcpy(numstr, nptr, nlen); + numstr[nlen] = '\0'; + + result = strtol(numstr, &endptr, 0); + + if (result < 0 || result > 0xffff || endptr != &numstr[nlen]) + return -1; + + return result; +} + +struct usagedata { + char const *page_name; + char const *usage_name; + size_t page_len; + size_t usage_len; + int isfinal; + u_int32_t usage_id; +}; + +/* + * Test a rule against the current usage data. Returns -1 on no + * match, 0 on partial match and 1 on complete match. + */ +static int +hidtestrule(struct Susbvar *var, struct usagedata *cache) +{ + char const *varname; + ssize_t matchindex, pagesplit; + size_t strind, varlen; + int numusage; + u_int32_t usage_id; + + matchindex = var->matchindex; + varname = var->variable; + varlen = var->varlen; + + usage_id = cache->usage_id; + + /* + * Parse the current variable name, locating the end of the + * current 'usage', and possibly where the usage page name + * ends. + */ + pagesplit = -1; + for (strind = matchindex; strind < varlen; strind++) { + if (varname[strind] == DELIM_USAGE) + break; + if (varname[strind] == DELIM_PAGE) + pagesplit = strind; + } + + if (cache->isfinal && strind != varlen) + /* + * Variable name is too long (hit delimiter instead of + * end-of-variable). + */ + return -1; + + if (pagesplit >= 0) { + /* + * Page name was specified, determine whether it was + * symbolic or numeric. + */ + char const *strstart; + int numpage; + + strstart = &varname[matchindex]; + + numpage = strtousage(strstart, pagesplit - matchindex); + + if (numpage >= 0) { + /* Valid numeric */ + + if (numpage != HID_PAGE(usage_id)) + /* Numeric didn't match page ID */ + return -1; + } else { + /* Not a valid numeric */ + + /* + * Load and cache the page name if and only if + * it hasn't already been loaded (it's a + * fairly expensive operation). + */ + if (cache->page_name == NULL) { + cache->page_name = hid_usage_page(HID_PAGE(usage_id)); + cache->page_len = strlen(cache->page_name); + } + + /* + * Compare specified page name to actual page + * name. + */ + if (cache->page_len != + (size_t)(pagesplit - matchindex) || + memcmp(cache->page_name, + &varname[matchindex], + cache->page_len) != 0) + /* Mismatch, page name wrong */ + return -1; + } + + /* Page matches, discard page name */ + matchindex = pagesplit + 1; + } + + numusage = strtousage(&varname[matchindex], strind - matchindex); + + if (numusage >= 0) { + /* Valid numeric */ + + if (numusage != HID_USAGE(usage_id)) + /* Numeric didn't match usage ID */ + return -1; + } else { + /* Not a valid numeric */ + + /* Load and cache the usage name */ + if (cache->usage_name == NULL) { + cache->usage_name = hid_usage_in_page(usage_id); + cache->usage_len = strlen(cache->usage_name); + } + + /* + * Compare specified usage name to actual usage name + */ + if (cache->usage_len != (size_t)(strind - matchindex) || + memcmp(cache->usage_name, &varname[matchindex], + cache->usage_len) != 0) + /* Mismatch, usage name wrong */ + return -1; + } + + if (cache->isfinal) + /* Match */ + return 1; + + /* + * Partial match: Move index past this usage string + + * delimiter + */ + var->matchindex = strind + 1; + + return 0; +} + +/* + * hidmatch() determines whether the item specified in 'item', and + * nested within a heirarchy of collections specified in 'collist' + * matches any of the rules in the list 'varlist'. Returns the + * matching rule on success, or NULL on no match. + */ static struct Susbvar* hidmatch(u_int32_t const *collist, size_t collen, struct hid_item *item, struct Susbvar *varlist, size_t vlsize) { - size_t vlind, colind, vlactive; + size_t colind, vlactive, vlind; int iscollection; /* @@ -161,96 +338,52 @@ hidmatch(u_int32_t const *collist, size_t collen, struct hid_item *item, } } + /* + * Loop through each usage in the collection list, including + * the 'item' itself on the final iteration. For each usage, + * test which variables named in the rule list are still + * applicable - if any. + */ for (colind = 0; vlactive > 0 && colind <= collen; colind++) { - char const *usage_name, *page_name; - size_t usage_len, page_len; - int final; - u_int32_t usage_id; - - final = (colind == collen); + struct usagedata cache; - if (final) - usage_id = item->usage; + cache.isfinal = (colind == collen); + if (cache.isfinal) + cache.usage_id = item->usage; else - usage_id = collist[colind]; + cache.usage_id = collist[colind]; - usage_name = hid_usage_in_page(usage_id); - usage_len = strlen(usage_name); - - page_name = NULL; + cache.usage_name = NULL; + cache.page_name = NULL; + /* + * Loop through each rule, testing whether the rule is + * still applicable or not. For each rule, + * 'matchindex' retains the current match state as an + * index into the variable name string, or -1 if this + * rule has been proven not to match. + */ for (vlind = 0; vlind < vlsize; vlind++) { - ssize_t matchindex, pagesplit; - size_t varlen, strind; - char const *varname; struct Susbvar *var; + int matchres; var = &varlist[vlind]; - matchindex = var->matchindex; - varname = var->variable; - varlen = var->varlen; - - if (matchindex < 0) + if (var->matchindex < 0) /* Mismatch at a previous level */ continue; - pagesplit = -1; - for (strind = matchindex; strind < varlen; strind++) { - if (varname[strind] == DELIM_USAGE) - break; - if (varname[strind] == DELIM_PAGE) - pagesplit = strind; - } + matchres = hidtestrule(var, &cache); - if (final && strind != varlen) { - /* - * Variable name is too long (hit - * delimiter instead of - * end-of-variable) - */ - var->matchindex = -1; - vlactive--; - continue; - } - - if (pagesplit >= 0) { - if (page_name == NULL) { - page_name = hid_usage_page(HID_PAGE(usage_id)); - page_len = strlen(page_name); - } - if (page_len != - (size_t)(pagesplit - matchindex) || - memcmp(page_name, &varname[matchindex], - page_len) != 0) { - /* Mismatch, page name wrong */ - var->matchindex = -1; - vlactive--; - continue; - } - - /* Page matches, discard page name */ - matchindex = pagesplit + 1; - } - - if (usage_len != strind - matchindex || - memcmp(usage_name, &varname[matchindex], - usage_len) != 0) { - /* Mismatch, usage name wrong */ + if (matchres < 0) { + /* Bad match */ var->matchindex = -1; vlactive--; continue; - } - - if (final) - /* Match */ + } else if (matchres > 0) { + /* Complete match */ return var; - - /* - * Partial match: Move index past this usage - * string + delimiter - */ - var->matchindex = matchindex + usage_len + 1; + } } } @@ -262,8 +395,7 @@ allocreport(struct Sreport *report, report_desc_t rd, int repindex) { int reptsize; - reptsize = hid_report_size(rd, reptoparam[repindex].hid_kind, - &report->report_id); + reptsize = hid_report_size(rd, reptoparam[repindex].hid_kind, reportid); if (reptsize < 0) errx(1, "Negative report size"); report->size = reptsize; @@ -302,7 +434,8 @@ getreport(struct Sreport *report, int hidfd, report_desc_t rd, int repindex) report->buffer->ucr_report = reptoparam[repindex].uhid_report; if (ioctl(hidfd, USB_GET_REPORT, report->buffer) < 0) - err(1, "USB_GET_REPORT"); + err(1, "USB_GET_REPORT (probably not supported by " + "device)"); } } @@ -335,19 +468,30 @@ varop_display(struct hid_item *item, struct Susbvar *var, u_int32_t const *collist, size_t collen, u_char *buf) { size_t colitem; - - for (colitem = 0; colitem < collen; colitem++) { + int val, i; + + for (i = 0; i < item->report_count; i++) { + for (colitem = 0; colitem < collen; colitem++) { + if (var->mflags & MATCH_SHOWPAGENAME) + printf("%s:", + hid_usage_page(HID_PAGE(collist[colitem]))); + printf("%s.", hid_usage_in_page(collist[colitem])); + } if (var->mflags & MATCH_SHOWPAGENAME) - printf("%s:", - hid_usage_page(HID_PAGE(collist[colitem]))); - printf("%s.", hid_usage_in_page(collist[colitem])); + printf("%s:", hid_usage_page(HID_PAGE(item->usage))); + val = hid_get_data(buf, item); + item->pos += item->report_size; + if (item->usage_minimum != 0 || item->usage_maximum != 0) { + val += item->usage_minimum; + printf("%s=1", hid_usage_in_page(val)); + } else { + printf("%s=%d%s", hid_usage_in_page(item->usage), + val, item->flags & HIO_CONST ? " (const)" : ""); + } + if (item->report_count > 1) + printf(" [%d]", i); + printf("\n"); } - - if (var->mflags & MATCH_SHOWPAGENAME) - printf("%s:", hid_usage_page(HID_PAGE(item->usage))); - printf("%s=%d%s\n", hid_usage_in_page(item->usage), - hid_get_data(buf, item), - (item->flags & HIO_CONST) ? " (const)" : ""); return 0; } @@ -362,12 +506,8 @@ varop_modify(struct hid_item *item, struct Susbvar *var, hid_set_data(buf, item, dataval); - if (verbose >= 1) - /* - * Allow displaying of set value in verbose mode. - * This isn't particularly useful though, so don't - * bother documenting it. - */ + if (var->mflags & MATCH_SHOWVALUES) + /* Display set value */ varop_display(item, var, collist, collen, buf); return 1; @@ -376,14 +516,28 @@ varop_modify(struct hid_item *item, struct Susbvar *var, static void reportitem(char const *label, struct hid_item const *item, unsigned int mflags) { - printf("%s size=%d count=%d page=%s usage=%s%s", label, + int isconst = item->flags & HIO_CONST, + isvar = item->flags & HIO_VARIABLE; + printf("%s size=%d count=%d%s%s page=%s", label, item->report_size, item->report_count, - hid_usage_page(HID_PAGE(item->usage)), - hid_usage_in_page(item->usage), - item->flags & HIO_CONST ? " Const" : ""); - if (mflags & MATCH_SHOWNUMERIC) - printf(" (%u/0x%x)", - HID_PAGE(item->usage), HID_USAGE(item->usage)); + isconst ? " Const" : "", + !isvar && !isconst ? " Array" : "", + hid_usage_page(HID_PAGE(item->usage))); + if (item->usage_minimum != 0 || item->usage_maximum != 0) { + printf(" usage=%s..%s", hid_usage_in_page(item->usage_minimum), + hid_usage_in_page(item->usage_maximum)); + if (mflags & MATCH_SHOWNUMERIC) + printf(" (%u:0x%x..%u:0x%x)", + HID_PAGE(item->usage_minimum), + HID_USAGE(item->usage_minimum), + HID_PAGE(item->usage_maximum), + HID_USAGE(item->usage_maximum)); + } else { + printf(" usage=%s", hid_usage_in_page(item->usage)); + if (mflags & MATCH_SHOWNUMERIC) + printf(" (%u:0x%x)", + HID_PAGE(item->usage), HID_USAGE(item->usage)); + } printf(", logical range %d..%d", item->logical_minimum, item->logical_maximum); if (item->physical_minimum != item->physical_maximum) @@ -402,9 +556,14 @@ varop_report(struct hid_item *item, struct Susbvar *var, { switch (item->kind) { case hid_collection: - printf("Collection page=%s usage=%s\n", + printf("Collection page=%s usage=%s", hid_usage_page(HID_PAGE(item->usage)), hid_usage_in_page(item->usage)); + if (var->mflags & MATCH_SHOWNUMERIC) + printf(" (%u:0x%x)\n", + HID_PAGE(item->usage), HID_USAGE(item->usage)); + else + printf("\n"); break; case hid_endcollection: printf("End collection\n"); @@ -426,13 +585,12 @@ varop_report(struct hid_item *item, struct Susbvar *var, static void devloop(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize) { + u_char *dbuf; struct hid_data *hdata; + size_t collind, dlen; struct hid_item hitem; u_int32_t colls[128]; struct Sreport inreport; - size_t dlen; - u_char *dbuf; - size_t collind; allocreport(&inreport, rd, REPORT_INPUT); @@ -446,12 +604,14 @@ devloop(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize) ssize_t readlen; readlen = read(hidfd, dbuf, dlen); - if (readlen < 0 || dlen != (size_t)readlen) - err(1, "bad read %ld != %ld", - (long)readlen, (long)dlen); + if (readlen < 0) + err(1, "Device read error"); + if (dlen != (size_t)readlen) + errx(1, "Unexpected response length: %lu != %lu", + (unsigned long)readlen, (unsigned long)dlen); collind = 0; - hdata = hid_start_parse(rd, 1 << hid_input); + hdata = hid_start_parse(rd, 1 << hid_input, reportid); if (hdata == NULL) errx(1, "Failed to start parser"); @@ -476,6 +636,9 @@ devloop(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize) errx(1, "Unexpected non-input item returned"); } + if (reportid != -1 && hitem.report_ID != reportid) + continue; + matchvar = hidmatch(colls, collind, &hitem, varlist, vlsize); @@ -495,10 +658,9 @@ devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize, int kindset) { struct hid_data *hdata; + size_t collind, repind, vlind; struct hid_item hitem; u_int32_t colls[128]; - size_t collind, repind, vlind; - struct Sreport reports[REPORT_MAXVAL + 1]; @@ -509,9 +671,7 @@ devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize, } collind = 0; - hdata = hid_start_parse(rd, kindset | - (1 << hid_collection) | - (1 << hid_endcollection)); + hdata = hid_start_parse(rd, kindset, reportid); if (hdata == NULL) errx(1, "Failed to start parser"); @@ -519,6 +679,9 @@ devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize, struct Susbvar *matchvar; int repindex; + if (verbose > 3) + printf("item: kind=%d repid=%d usage=0x%x\n", + hitem.kind, hitem.report_ID, hitem.usage); repindex = -1; switch (hitem.kind) { case hid_collection: @@ -542,6 +705,9 @@ devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize, break; } + if (reportid != -1 && hitem.report_ID != reportid) + continue; + matchvar = hidmatch(colls, collind, &hitem, varlist, vlsize); if (matchvar != NULL) { @@ -609,13 +775,13 @@ usage(void) int main(int argc, char **argv) { - int hidfd; - report_desc_t repdesc; - char devnamebuf[PATH_MAX]; char const *dev; - int ch, wflag, aflag, nflag, rflag, lflag; - size_t varnum; char const *table; + size_t varnum; + int aflag, lflag, nflag, rflag, wflag; + int ch, hidfd; + report_desc_t repdesc; + char devnamebuf[PATH_MAX]; struct Susbvar variables[128]; wflag = aflag = nflag = verbose = rflag = lflag = 0; @@ -698,6 +864,14 @@ main(int argc, char **argv) if (!wflag) errx(2, "Must specify -w to set variables"); svar->mflags |= MATCH_WRITABLE; + if (verbose >= 1) + /* + * Allow displaying of set value in + * verbose mode. This isn't + * particularly useful though, so + * don't bother documenting it. + */ + svar->mflags |= MATCH_SHOWVALUES; svar->varlen = valuesep - name; svar->value = valuesep + 1; svar->opfunc = varop_modify; @@ -770,6 +944,10 @@ main(int argc, char **argv) if (hidfd < 0) err(1, "%s", dev); + if (ioctl(hidfd, USB_GET_REPORT_ID, &reportid) < 0) + reportid = -1; + if (verbose > 1) + printf("report ID=%d\n", reportid); repdesc = hid_get_report_desc(hidfd); if (repdesc == 0) errx(1, "USB_GET_REPORT_DESC"); @@ -788,31 +966,18 @@ main(int argc, char **argv) 1 << hid_output | 1 << hid_feature); -#if 0 - { - size_t repindex; - for (repindex = 0; - repindex < (sizeof(reptoparam) / sizeof(*reptoparam)); - repindex++) - devshow(hidfd, repdesc, variables, varnum, - 1 << reptoparam[repindex].hid_kind); - } -#endif - if (rflag) { /* Report mode trailer */ size_t repindex; for (repindex = 0; repindex < (sizeof(reptoparam) / sizeof(*reptoparam)); repindex++) { - int report_id, size; + int size; size = hid_report_size(repdesc, reptoparam[repindex].hid_kind, - &report_id); - size -= report_id != 0; - printf("Total %7s size %s%d bytes\n", - reptoparam[repindex].name, - report_id && size ? "1+" : "", size); + reportid); + printf("Total %7s size %d bytes\n", + reptoparam[repindex].name, size); } } diff --git a/usr.bin/usbhidctl/usbhidctl.1 b/usr.bin/usbhidctl/usbhidctl.1 index b7b2ec48efa..87f92e51a1a 100644 --- a/usr.bin/usbhidctl/usbhidctl.1 +++ b/usr.bin/usbhidctl/usbhidctl.1 @@ -1,7 +1,7 @@ -.\" $OpenBSD: usbhidctl.1,v 1.3 2001/12/30 07:24:07 pvalchev Exp $ -.\" $NetBSD: usbhidctl.1,v 1.10 2000/09/24 02:27:12 augustss Exp $ +.\" $OpenBSD: usbhidctl.1,v 1.4 2002/05/10 00:09:17 nate Exp $ +.\" $NetBSD: usbhidctl.1,v 1.14 2001/12/28 17:49:32 augustss Exp $ .\" -.\" Copyright (c) 2000 The NetBSD Foundation, Inc. +.\" Copyright (c) 2001 The NetBSD Foundation, Inc. .\" All rights reserved. .\" .\" This code is derived from software contributed to The NetBSD Foundation @@ -70,8 +70,8 @@ .Op Ar item=value ... .Sh DESCRIPTION .Nm -can be used to dump or modify the state of a USB HID (Human Interface Device). -If a list of items is present on the command line, then +can be used to output or modify the state of a USB HID (Human Interface +Device). If a list of items is present on the command line, then .Nm prints the current value of those items for the specified device. If the .Fl w @@ -83,6 +83,7 @@ The options are as follows: .Bl -tag -width Ds .It Fl a Show all items and their current values. +This option fails if the device does not support the GET_REPORT command. .It Fl f Ar device Specify a path name for the device to operate on. If .Ar device @@ -94,9 +95,10 @@ An absolute path is taken to be the literal device pathname. Loop and dump the device data every time it changes. Only 'input' items are displayed in this mode. .It Fl n -Suppress printing of the item name when querying specific item values. +Suppress printing of the item name when querying specific items. Only output +the current value. .It Fl r -Dump the report descriptor. +Dump the USB HID report descriptor. .It Fl t Ar table Specify a path name for the HID usage table file. .It Fl v @@ -108,18 +110,91 @@ option. .Sh FILES .Pa /usr/share/misc/usb_hid_usages The default HID usage table. +.Sh SYNTAX +.Nm +parses the names of items specified on the command line against the human +interface items reported by the USB device. Each human interface item is +mapped from its native form to a human readable name, using the HID usage +table file. Command line items are compared with the generated item names, +and the USB HID device is operated on when a match is found. +.Pp +Each human interface item is named by the +.Qq page +it appears in, the +.Qq usage +within that page, and the list of +.Qq collections +containing the item. Each collection in turn is also identified by page, and +the usage within that page. +.Pp +On the +.Nm +command line the page name is separated from the usage name with the character +.Cm So : Sc . +The collections are separated by the character +.Cm So . Sc . +.Pp +As an alternative notation in items on the command line, the native numeric +value for the page name or usage can be used instead of the full human +readable page name or usage name. Numeric values can be specified in decimal, +octal or hexadecimal. +.Sh EXAMPLES +On a standard USB mouse the item +.Dl Generic_Desktop:Mouse.Generic_Desktop:Pointer.Button:Button_2 +reflects the current status of button 2. The +.Qq button 2 +item is encapsulated within two collections, the +.Qq Mouse +collection in the +.Qq Generic Desktop +page, and the +.Qq Pointer +collection in the +.Qq Generic Desktop +page. The item itself is the usage +.Qq Button_2 +in the +.Qq Button +page. +.Pp +An item can generally be named by omitting one or more of the page names. For +example the +.Qq button 2 +item would usually just be referred to on the command line as: +.Dl usbhidctl -f /dev/mouse Mouse.Pointer.Button_2 +.Pp +Items can also be named by referring to parts of the item name with the +numeric representation of the native HID usage identifiers. This is most +useful when items are missing from the HID usage table. The page identifier +for the +.Qq Generic Desktop +page is 1, and the usage identifier for the usage +.Qq Button_2 +is 2, so the following can be used to refer to the +.Qq button 2 +item: +.Dl usbhidctl -f /dev/mouse 1:Mouse.1:Pointer.Button:2 +.Pp +Devices with human interface outputs can be manipulated with the +.Fl w +option. For example, some USB mice have a Light Emitting Diode under software +control as usage 2 under page 0xffff, in the +.Qq Mouse +collection. The following can be used to switch this LED off: +.Dl usbhidctl -f /dev/mouse -w Mouse.0xffff:2=0 .Sh SEE ALSO +.Xr usbhidaction 1 , .Xr usbhid 3 , .Xr uhid 4 , .Xr usb 4 -.Sh AUTHOR -David Sainty <David.Sainty@dtsp.co.nz> .Sh HISTORY The .Nm command first appeared in .Ox 3.0 . +.Sh AUTHORS +.An David Sainty Aq David.Sainty@dtsp.co.nz .Sh BUGS -Some USB HID devices report multiple items with exactly the same description. -The current naming scheme does not provide the means to specify which of the -identically named items you are referring to. +Some USB HID devices report multiple items with exactly the same usage +identifiers. The current naming scheme does not provide the means to specify +which of a set of identically named items you are referring to. diff --git a/usr.sbin/usbdevs/usbdevs.8 b/usr.sbin/usbdevs/usbdevs.8 index a9397e35704..b36206ff508 100644 --- a/usr.sbin/usbdevs/usbdevs.8 +++ b/usr.sbin/usbdevs/usbdevs.8 @@ -1,5 +1,5 @@ -.\" $OpenBSD: usbdevs.8,v 1.5 2001/09/17 17:29:56 mickey Exp $ -.\" $NetBSD: usbdevs.8,v 1.4 1999/04/13 20:50:49 augustss Exp $ +.\" $OpenBSD: usbdevs.8,v 1.6 2002/05/10 00:09:17 nate Exp $ +.\" $NetBSD: usbdevs.8,v 1.5 2000/10/15 12:44:11 bjh21 Exp $ .\" .\" Copyright (c) 1999 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -35,17 +35,18 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd August 14, 2001 +.Dd October 15, 2000 .Dt USBDEVS 8 .Os .Sh NAME .Nm usbdevs .Nd show USB devices connected to the system .Sh SYNOPSIS -.Nm usbdevs -.Op Fl dv +.Nm .Op Fl a Ar addr +.Op Fl d .Op Fl f Ar dev +.Op Fl v .Sh DESCRIPTION .Nm prints a listing of all USB devices connected to the system @@ -53,7 +54,7 @@ with some information about each device. The indentation of each line indicates its distance from the root. .Pp The options are as follows: -.Bl -tag -width Ds +.Bl -tag -width Fl .It Fl a Ar addr Only print information about the device at the given address. .It Fl d diff --git a/usr.sbin/usbdevs/usbdevs.c b/usr.sbin/usbdevs/usbdevs.c index 8a07c829b4f..a22f1576e4f 100644 --- a/usr.sbin/usbdevs/usbdevs.c +++ b/usr.sbin/usbdevs/usbdevs.c @@ -1,5 +1,5 @@ -/* $OpenBSD: usbdevs.c,v 1.4 2002/05/02 20:12:07 nate Exp $ */ -/* $NetBSD: usbdevs.c,v 1.11 1999/09/08 02:39:36 augustss Exp $ */ +/* $OpenBSD: usbdevs.c,v 1.5 2002/05/10 00:09:17 nate Exp $ */ +/* $NetBSD: usbdevs.c,v 1.19 2002/02/21 00:34:31 christos Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -49,8 +49,8 @@ #define USBDEV "/dev/usb" -int verbose; -int showdevs; +int verbose = 0; +int showdevs = 0; void usage(void); void usbdev(int f, int a, int rec); @@ -72,10 +72,7 @@ char done[USB_MAX_DEVICES]; int indent; void -usbdev(f, a, rec) - int f; - int a; - int rec; +usbdev(int f, int a, int rec) { struct usb_device_info di; int e, p, i; @@ -90,8 +87,17 @@ usbdev(f, a, rec) printf("addr %d: ", a); done[a] = 1; if (verbose) { +#ifdef notyet + switch (di.udi_speed) { + case USB_SPEED_LOW: printf("low speed, "); break; + case USB_SPEED_FULL: printf("full speed, "); break; + case USB_SPEED_HIGH: printf("high speed, "); break; + default: break; + } +#endif if (di.udi_lowspeed) printf("low speed, "); + if (di.udi_power) printf("power %d mA, ", di.udi_power); else @@ -109,10 +115,10 @@ usbdev(f, a, rec) printf("%s, %s", di.udi_product, di.udi_vendor); printf("\n"); if (showdevs) { - for (i = 0; i< USB_MAX_DEVNAMES; i++) + for (i = 0; i < USB_MAX_DEVNAMES; i++) if (di.udi_devnames[i][0]) - printf("%*s %s\n", indent, "", - di.udi_devnames[i]); + printf("%*s %s\n", indent, "", + di.udi_devnames[i]); } if (!rec) return; @@ -143,8 +149,7 @@ usbdev(f, a, rec) } void -usbdump(f) - int f; +usbdump(int f) { int a; @@ -155,10 +160,7 @@ usbdump(f) } void -dumpone(name, f, addr) - char *name; - int f; - int addr; +dumpone(char *name, int f, int addr) { if (verbose) printf("Controller %s:\n", name); @@ -171,14 +173,10 @@ dumpone(name, f, addr) } int -main(argc, argv) - int argc; - char **argv; +main(int argc, char **argv) { int ch, i, f; char buf[50]; - extern int optind; - extern char *optarg; char *dev = 0; int addr = 0; int ncont; @@ -220,7 +218,8 @@ main(argc, argv) ncont++; } if (verbose && ncont == 0) - printf("%s: no USB controllers found\n", __progname); + printf("%s: no USB controllers found\n", + __progname); } else { f = open(dev, O_RDONLY); if (f >= 0) |