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:
parent
8523ec52e8
commit
dc41f2f427
@ -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;
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user