Change usbhidctl to take numeric usage names. Add examples in the man

page.  From Dave Sainty <dave@dtsp.co.nz>.
This commit is contained in:
augustss 2001-10-22 22:03:49 +00:00
parent 8523ec52e8
commit dc41f2f427
2 changed files with 319 additions and 107 deletions

View File

@ -1,7 +1,7 @@
/* $NetBSD: usbhid.c,v 1.17 2001/03/28 03:17:42 simonb Exp $ */
/* $NetBSD: usbhid.c,v 1.18 2001/10/22 22:03:49 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
@ -57,10 +57,6 @@
#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;
struct Susbvar {
/* Variable name, not NUL terminated */
char const *variable;
@ -76,6 +72,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() */
@ -107,11 +104,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;
/*
@ -160,96 +329,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;
struct usagedata cache;
final = (colind == collen);
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)
*/
if (matchres < 0) {
/* Bad match */
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 */
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;
}
}
}
@ -361,12 +486,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;
@ -381,7 +502,7 @@ reportitem(char const *label, struct hid_item const *item, unsigned int mflags)
hid_usage_in_page(item->usage),
item->flags & HIO_CONST ? " Const" : "");
if (mflags & MATCH_SHOWNUMERIC)
printf(" (%u/0x%x)",
printf(" (%u:0x%x)",
HID_PAGE(item->usage), HID_USAGE(item->usage));
printf(", logical range %d..%d",
item->logical_minimum, item->logical_maximum);
@ -401,9 +522,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");
@ -425,13 +551,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);
@ -494,10 +619,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];
@ -608,15 +732,21 @@ usage(void)
int
main(int argc, char **argv)
{
int hidfd;
char const *dev;
char const *table;
size_t varnum;
int aflag, lflag, nflag, rflag, wflag;
int ch, 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;
struct Susbvar variables[128];
/*
* Zero if not in a verbose mode. Greater levels of verbosity
* are indicated by values larger than one.
*/
unsigned int verbose;
wflag = aflag = nflag = verbose = rflag = lflag = 0;
dev = NULL;
table = NULL;
@ -697,6 +827,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;

View File

@ -1,6 +1,6 @@
.\" $NetBSD: usbhidctl.1,v 1.10 2000/09/24 02:27:12 augustss Exp $
.\" $NetBSD: usbhidctl.1,v 1.11 2001/10/22 22:03:49 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
@ -66,8 +66,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
@ -90,9 +90,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
@ -104,10 +105,83 @@ 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 usb 3 ,
.Xr uhid 4 ,
.Xr usb 4
.Xr usb 4 ,
.Xr usbhidaction 1
.Sh AUTHOR
David Sainty <David.Sainty@dtsp.co.nz>
.Sh HISTORY
@ -116,6 +190,6 @@ The
command first appeared in
.Nx 1.4 .
.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.