From 3d56b3bab38dcdd97e9541e2df85677ee1e12e49 Mon Sep 17 00:00:00 2001 From: dsainty Date: Sun, 22 Oct 2006 05:09:14 +0000 Subject: [PATCH] Add support for addressing multiple items with the same name (for buggy devices). The syntax and patch were discussed with Lennart about 1.5 years ago :) Knock the BUG entry out of the man page. --- usr.bin/usbhidctl/usbhid.c | 81 +++++++++++++++++++++++++++++------ usr.bin/usbhidctl/usbhidctl.1 | 32 +++++++++++--- 2 files changed, 93 insertions(+), 20 deletions(-) diff --git a/usr.bin/usbhidctl/usbhid.c b/usr.bin/usbhidctl/usbhid.c index 2825630d6af9..0a3b39c7489c 100644 --- a/usr.bin/usbhidctl/usbhid.c +++ b/usr.bin/usbhidctl/usbhid.c @@ -1,4 +1,4 @@ -/* $NetBSD: usbhid.c,v 1.30 2006/05/10 21:53:48 mrg Exp $ */ +/* $NetBSD: usbhid.c,v 1.31 2006/10/22 05:09:14 dsainty Exp $ */ /* * Copyright (c) 2001 The NetBSD Foundation, Inc. @@ -38,7 +38,7 @@ #include #ifndef lint -__RCSID("$NetBSD: usbhid.c,v 1.30 2006/05/10 21:53:48 mrg Exp $"); +__RCSID("$NetBSD: usbhid.c,v 1.31 2006/10/22 05:09:14 dsainty Exp $"); #endif #include @@ -67,6 +67,7 @@ unsigned int verbose; #define DELIM_USAGE '.' #define DELIM_PAGE ':' #define DELIM_SET '=' +#define DELIM_INSTANCE '#' static int reportid; @@ -88,8 +89,16 @@ struct Susbvar { #define MATCH_SHOWVALUES (1 << 8) unsigned int mflags; + /* + * An instance number can be used to identify an item by + * position as well as by name. This allows us to manipulate + * devices that don't assign unique names to all usage items. + */ + int usageinstance; + /* Workspace for hidmatch() */ ssize_t matchindex; + int matchcount; int (*opfunc)(struct hid_item *item, struct Susbvar *var, u_int32_t const *collist, size_t collen, u_char *buf); @@ -198,7 +207,7 @@ hidtestrule(struct Susbvar *var, struct usagedata *cache) if (pagesplit >= 0) { /* * Page name was specified, determine whether it was - * symbolic or numeric. + * symbolic or numeric. */ char const *strstart; int numpage; @@ -283,6 +292,17 @@ hidtestrule(struct Susbvar *var, struct usagedata *cache) return 0; } +/* + * Clear state in HID variable records used by hidmatch(). + */ +static void +resethidvars(struct Susbvar *varlist, size_t vlsize) +{ + size_t vlind; + for (vlind = 0; vlind < vlsize; vlind++) + varlist[vlind].matchcount = 0; +} + /* * hidmatch() determines whether the item specified in 'item', and * nested within a hierarchy of collections specified in 'collist' @@ -293,6 +313,7 @@ static struct Susbvar* hidmatch(u_int32_t const *collist, size_t collen, struct hid_item *item, struct Susbvar *varlist, size_t vlsize) { + struct Susbvar *result; size_t colind, vlactive, vlind; int iscollection; @@ -348,6 +369,7 @@ hidmatch(u_int32_t const *collist, size_t collen, struct hid_item *item, * test which variables named in the rule list are still * applicable - if any. */ + result = NULL; for (colind = 0; vlactive > 0 && colind <= collen; colind++) { struct usagedata cache; @@ -382,19 +404,29 @@ hidmatch(u_int32_t const *collist, size_t collen, struct hid_item *item, matchres = hidtestrule(var, &cache); - if (matchres < 0) { - /* Bad match */ - var->matchindex = -1; - vlactive--; + if (matchres == 0) + /* Partial match */ continue; - } else if (matchres > 0) { + + if (matchres > 0) { /* Complete match */ - return var; + if (var->usageinstance < 0 || + var->matchcount == var->usageinstance) + result = var; + var->matchcount++; } + + /* + * We either matched completely, or not at + * all. Either way, this variable is no + * longer active. + */ + var->matchindex = -1; + vlactive--; } } - return NULL; + return result; } static void @@ -619,6 +651,8 @@ devloop(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize) (unsigned long)readlen, (unsigned long)dlen); collind = 0; + resethidvars(varlist, vlsize); + hdata = hid_start_parse(rd, 1 << hid_input, reportid); if (hdata == NULL) errx(1, "Failed to start parser"); @@ -679,6 +713,8 @@ devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize, } collind = 0; + resethidvars(varlist, vlsize); + hdata = hid_start_parse(rd, kindset, reportid); if (hdata == NULL) errx(1, "Failed to start parser"); @@ -839,8 +875,9 @@ main(int argc, char **argv) } for (varnum = 0; varnum < (size_t)argc; varnum++) { - char const *name, *valuesep; + char const *name, *valuesep, *varinst; struct Susbvar *svar; + size_t namelen; svar = &variables[varnum]; name = argv[varnum]; @@ -848,13 +885,14 @@ main(int argc, char **argv) svar->variable = name; svar->mflags = 0; + svar->usageinstance = 0; if (valuesep == NULL) { /* Read variable */ if (wflag) errx(1, "Must not specify -w to read variables"); svar->value = NULL; - svar->varlen = strlen(name); + namelen = strlen(name); if (nflag) { /* Display value of variable only */ @@ -880,10 +918,27 @@ main(int argc, char **argv) * don't bother documenting it. */ svar->mflags |= MATCH_SHOWVALUES; - svar->varlen = valuesep - name; + namelen = valuesep - name; svar->value = valuesep + 1; svar->opfunc = varop_modify; } + + varinst = memchr(name, DELIM_INSTANCE, namelen); + + if (varinst != NULL && ++varinst != &name[namelen]) { + char *endptr; + + svar->usageinstance = strtol(varinst, &endptr, 0); + + if (&name[namelen] != (char const*)endptr) + errx(1, "%s%c%s", "Error parsing item " + "instance number after '", + DELIM_INSTANCE, "'"); + + namelen = varinst - 1 - name; + } + + svar->varlen = namelen; } if (aflag || rflag) { diff --git a/usr.bin/usbhidctl/usbhidctl.1 b/usr.bin/usbhidctl/usbhidctl.1 index 70651d599082..4faef4f554ef 100644 --- a/usr.bin/usbhidctl/usbhidctl.1 +++ b/usr.bin/usbhidctl/usbhidctl.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: usbhidctl.1,v 1.17 2003/07/01 10:26:27 wiz Exp $ +.\" $NetBSD: usbhidctl.1,v 1.18 2006/10/22 05:09:14 dsainty Exp $ .\" .\" Copyright (c) 2001 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -34,7 +34,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd August 27, 2000 +.Dd October 22, 2006 .Dt USBHIDCTL 1 .Os .Sh NAME @@ -144,6 +144,12 @@ 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. +.Pp +Some devices give the same name to more than one item. +.Nm +supports isolating each item by appending a +.Sq Cm \&# . +character and a decimal item instance number, starting at zero. .Sh EXAMPLES On a standard USB mouse the item .Dl Generic_Desktop:Mouse.Generic_Desktop:Pointer.Button:Button_2 @@ -193,6 +199,23 @@ control as usage 2 under page 0xffff, in the collection. The following can be used to switch this LED off: .Dl usbhidctl -f /dev/mouse -w Mouse.0xffff:2=0 +.Pp +The output below is from a device that uses the same name repeatedly. +.Bd -literal -offset indent +% usbhidctl -f /dev/uhid0 -a +Consumer_Control.Volume_Up=0 +Consumer_Control.Volume_Down=0 +Consumer_Control.Mute=0 +Consumer_Control.Unassigned=0 +Consumer_Control.Unassigned=0 +.Ed +.Pp +The +.Qq Consumer_Control.Unassigned +name is used twice. +Each can be individually accessed by providing an instance number. +For example, to set the value for the first item: +.Dl usbhidctl -f /dev/uhid0 -w 'Consumer_Control.Unassigned#0=1' .Sh SEE ALSO .Xr usbhidaction 1 , .Xr usbhid 3 , @@ -205,8 +228,3 @@ command first appeared in .Nx 1.4 . .Sh AUTHORS .An David Sainty Aq David.Sainty@dtsp.co.nz -.Sh BUGS -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.