MFprojects/hid:

Import the rest of HID improvements from the branch:
 - improve report descriptor parser in libusbhid to handle several kinds of
reports same time;
 - add to the libusbhid API two functions wrapping respective kernel IOCTLs
for reading and writing reports;
 - tune uhid IOCTL interface to allow reading and writing arbitrary report,
when multiple supported by the device;
 - teach usbhidctl to set output and feature reports;
 - make usbhidaction support all the same item names as bhidctl.

Sponsored by: iXsystems, inc.
This commit is contained in:
Alexander Motin 2011-09-28 14:52:25 +00:00
parent 005f609130
commit 7778ab7e0c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=225839
9 changed files with 449 additions and 156 deletions

View file

@ -32,7 +32,10 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <dev/usb/usb_ioctl.h>
#include "usbhid.h"
#include "usbvar.h"
int32_t
hid_get_data(const void *p, const hid_item_t *h)
@ -114,3 +117,27 @@ hid_set_data(void *p, const hid_item_t *h, int32_t data)
buf[offs + i] = (buf[offs + i] & (mask >> (i*8))) |
((data >> (i*8)) & 0xff);
}
int
hid_get_report(int fd, enum hid_kind k, unsigned char *data, unsigned int size)
{
struct usb_gen_descriptor ugd;
memset(&ugd, 0, sizeof(ugd));
ugd.ugd_data = hid_pass_ptr(data);
ugd.ugd_maxlen = size;
ugd.ugd_report_type = k + 1;
return (ioctl(fd, USB_GET_REPORT, &ugd));
}
int
hid_set_report(int fd, enum hid_kind k, unsigned char *data, unsigned int size)
{
struct usb_gen_descriptor ugd;
memset(&ugd, 0, sizeof(ugd));
ugd.ugd_data = hid_pass_ptr(data);
ugd.ugd_maxlen = size;
ugd.ugd_report_type = k + 1;
return (ioctl(fd, USB_SET_REPORT, &ugd));
}

View file

@ -43,10 +43,11 @@ __FBSDID("$FreeBSD$");
#define MAXUSAGE 100
#define MAXPUSH 4
#define MAXID 64
#define ITEMTYPES 3
struct hid_pos_data {
int32_t rid;
uint32_t pos;
uint32_t pos[ITEMTYPES];
};
struct hid_data {
@ -55,6 +56,7 @@ struct hid_data {
const uint8_t *p;
struct hid_item cur[MAXPUSH];
struct hid_pos_data last_pos[MAXID];
uint32_t pos[ITEMTYPES];
int32_t usages_min[MAXUSAGE];
int32_t usages_max[MAXUSAGE];
int32_t usage_last; /* last seen usage */
@ -92,7 +94,7 @@ hid_clear_local(hid_item_t *c)
static void
hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID)
{
uint8_t i;
uint8_t i, j;
/* check for same report ID - optimise */
@ -113,7 +115,8 @@ hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID)
}
if (i != MAXID) {
s->last_pos[i].rid = c->report_ID;
s->last_pos[i].pos = c->pos;
for (j = 0; j < ITEMTYPES; j++)
s->last_pos[i].pos[j] = s->pos[j];
}
/* store next report ID */
@ -134,9 +137,12 @@ hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID)
}
if (i != MAXID) {
s->last_pos[i].rid = next_rID;
c->pos = s->last_pos[i].pos;
} else
c->pos = 0; /* Out of RID entries. */
for (j = 0; j < ITEMTYPES; j++)
s->pos[j] = s->last_pos[i].pos[j];
} else {
for (j = 0; j < ITEMTYPES; j++)
s->pos[j] = 0; /* Out of RID entries. */
}
}
/*------------------------------------------------------------------------*
@ -206,7 +212,6 @@ hid_get_item(hid_data_t s, hid_item_t *h)
{
hid_item_t *c;
unsigned int bTag, bType, bSize;
uint32_t oldpos;
int32_t mask;
int32_t dval;
@ -240,7 +245,8 @@ hid_get_item(hid_data_t s, hid_item_t *h)
*/
if (s->kindset & (1 << c->kind)) {
*h = *c;
c->pos += c->report_size * c->report_count;
h->pos = s->pos[c->kind];
s->pos[c->kind] += c->report_size * c->report_count;
return (1);
}
}
@ -406,14 +412,10 @@ hid_get_item(hid_data_t s, hid_item_t *h)
case 11: /* Pop */
s->pushlevel --;
if (s->pushlevel < MAXPUSH) {
/* preserve position */
oldpos = c->pos;
c = &s->cur[s->pushlevel];
/* restore size and count */
s->loc_size = c->report_size;
s->loc_count = c->report_count;
/* set default item location */
c->pos = oldpos;
c->report_size = 0;
c->report_count = 0;
}

View file

@ -44,7 +44,9 @@
.Nm hid_usage_in_page ,
.Nm hid_init ,
.Nm hid_get_data ,
.Nm hid_set_data
.Nm hid_set_data ,
.Nm hid_get_report ,
.Nm hid_set_report
.Nd USB HID access routines
.Sh LIBRARY
.Lb libusbhid
@ -84,6 +86,10 @@
.Fn hid_get_data "const void *data" "const hid_item_t *h"
.Ft void
.Fn hid_set_data "void *buf" "const hid_item_t *h" "int data"
.Ft int
.Fn hid_get_report "int fd" "enum hid_kind k" "unsigned char *data" "unsigned int size"
.Ft int
.Fn hid_set_report "int fd" "enum hid_kind k" "unsigned char *data" "unsigned int size"
.Sh DESCRIPTION
The
.Nm
@ -105,6 +111,14 @@ Synchronous HID operation can be enabled or disabled by a call to
If the second argument is zero synchronous HID operation is disabled.
Else synchronous HID operation is enabled.
The function returns a negative value on failure.
.Pp
.Fn hid_get_report
and
.Fn hid_set_report
functions allow to synchronously get and set specific report if device
supports it.
For devices with multiple report IDs, wanted ID should be provided in the
first byte of the buffer for both get and set.
.Ss Descriptor Functions
The report descriptor ID can be obtained by calling
.Fn hid_get_report_id .

View file

@ -76,6 +76,7 @@ typedef struct hid_item {
#define HID_PAGE(u) (((u) >> 16) & 0xffff)
#define HID_USAGE(u) ((u) & 0xffff)
#define HID_HAS_GET_SET_REPORT 1
__BEGIN_DECLS
@ -104,5 +105,9 @@ int hid_parse_usage_page(const char *name);
/* Extracting/insertion of data, data.c: */
int32_t hid_get_data(const void *p, const hid_item_t *h);
void hid_set_data(void *p, const hid_item_t *h, int32_t data);
int hid_get_report(int fd, enum hid_kind k,
unsigned char *data, unsigned int size);
int hid_set_report(int fd, enum hid_kind k,
unsigned char *data, unsigned int size);
__END_DECLS

View file

@ -566,8 +566,10 @@ uhid_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr,
default:
return (EINVAL);
}
if (id != 0)
copyin(ugd->ugd_data, &id, 1);
error = uhid_get_report(sc, ugd->ugd_report_type, id,
NULL, ugd->ugd_data, size);
NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size));
break;
case USB_SET_REPORT:
@ -592,8 +594,10 @@ uhid_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr,
default:
return (EINVAL);
}
if (id != 0)
copyin(ugd->ugd_data, &id, 1);
error = uhid_set_report(sc, ugd->ugd_report_type, id,
NULL, ugd->ugd_data, size);
NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size));
break;
case USB_GET_REPORT_ID:

View file

@ -99,8 +99,7 @@ a debounce value, 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.
.Xr usbhidctl 1 .
.Pp
The value is simply a numeric value.
When the item reports this value,

View file

@ -280,12 +280,11 @@ parse_conf(const char *conf, report_desc_t repd, int reportid, int ignore)
char *p;
int line;
char buf[SIZE], name[SIZE], value[SIZE], debounce[SIZE], action[SIZE];
char usbuf[SIZE], coll[SIZE];
char usbuf[SIZE], coll[SIZE], *tmp;
struct command *cmd, *cmds;
struct hid_data *d;
struct hid_item h;
int u, lo, hi, range;
int inst, cinst, u, lo, hi, range, t;
f = fopen(conf, "r");
if (f == NULL)
@ -317,6 +316,12 @@ parse_conf(const char *conf, report_desc_t repd, int reportid, int ignore)
", syntax error: %s", conf, line, buf);
}
}
tmp = strchr(name, '#');
if (tmp != NULL) {
*tmp = 0;
inst = atoi(tmp + 1);
} else
inst = 0;
cmd = malloc(sizeof *cmd);
if (cmd == NULL)
@ -361,6 +366,7 @@ parse_conf(const char *conf, report_desc_t repd, int reportid, int ignore)
}
coll[0] = 0;
cinst = 0;
for (d = hid_start_parse(repd, 1 << hid_input, reportid);
hid_get_item(d, &h); ) {
if (verbose > 2)
@ -380,24 +386,29 @@ parse_conf(const char *conf, report_desc_t repd, int reportid, int ignore)
range = 0;
}
for (u = lo; u <= hi; u++) {
snprintf(usbuf, sizeof usbuf, "%s:%s",
hid_usage_page(HID_PAGE(u)),
hid_usage_in_page(u));
if (verbose > 2)
printf("usage %s\n", usbuf);
if (!strcasecmp(usbuf, name))
goto foundhid;
if (coll[0]) {
snprintf(usbuf, sizeof usbuf,
"%s.%s:%s", coll+1,
hid_usage_page(HID_PAGE(u)),
hid_usage_page(HID_PAGE(u)),
hid_usage_in_page(u));
} else {
snprintf(usbuf, sizeof usbuf,
"%s:%s",
hid_usage_page(HID_PAGE(u)),
hid_usage_in_page(u));
if (verbose > 2)
printf("usage %s\n",
usbuf);
if (!strcasecmp(usbuf, name))
goto foundhid;
}
if (verbose > 2)
printf("usage %s\n", usbuf);
t = strlen(usbuf) - strlen(name);
if (t > 0) {
if (strcmp(usbuf + t, name))
continue;
if (usbuf[t - 1] != '.')
continue;
} else if (strcmp(usbuf, name))
continue;
if (inst == cinst++)
goto foundhid;
}
break;
case hid_collection:

View file

@ -42,45 +42,141 @@
#include <usbhid.h>
#include <dev/usb/usbhid.h>
struct variable {
char *name;
int instance;
int val;
struct hid_item h;
struct variable *next;
} *vars;
int verbose = 0;
int all = 0;
int noname = 0;
int hexdump = 0;
int wflag = 0;
int zflag = 0;
char **names;
int nnames;
static void usage(void);
static void dumpitem(const char *label, struct hid_item *h);
static void dumpitems(report_desc_t r);
static void prdata(u_char *buf, struct hid_item *h);
static void dumpdata(int f, report_desc_t r, int loop);
static void writedata(int f, report_desc_t r);
void prbits(int bits, char **strs, int n);
void usage(void);
void dumpitem(const char *label, struct hid_item *h);
void dumpitems(report_desc_t r);
void rev(struct hid_item **p);
void prdata(u_char *buf, struct hid_item *h);
void dumpdata(int f, report_desc_t r, int loop);
int gotname(char *n);
int
gotname(char *n)
static void
parceargs(report_desc_t r, int all, int nnames, char **names)
{
int i;
struct hid_data *d;
struct hid_item h;
char colls[1000];
char hname[1000], *tmp1, *tmp2;
struct variable *var, **pnext;
int i, instance, cp, t;
for (i = 0; i < nnames; i++)
if (strcmp(names[i], n) == 0)
return 1;
return 0;
pnext = &vars;
if (all) {
if (wflag)
errx(1, "Must not specify -w to read variables");
cp = 0;
for (d = hid_start_parse(r,
1<<hid_input | 1<<hid_output | 1<<hid_feature, -1);
hid_get_item(d, &h); ) {
if (h.kind == hid_collection) {
cp += sprintf(&colls[cp], "%s%s:%s",
cp != 0 ? "." : "",
hid_usage_page(HID_PAGE(h.usage)),
hid_usage_in_page(h.usage));
} else if (h.kind == hid_endcollection) {
tmp1 = strrchr(colls, '.');
if (tmp1 != NULL) {
cp -= strlen(tmp1);
tmp1[0] = 0;
} else {
cp = 0;
colls[0] = 0;
}
}
if ((h.kind != hid_input && h.kind != hid_output &&
h.kind != hid_feature) || (h.flags & HIO_CONST))
continue;
var = malloc(sizeof(*var));
memset(var, 0, sizeof(*var));
asprintf(&var->name, "%s%s%s:%s",
colls, colls[0] != 0 ? "." : "",
hid_usage_page(HID_PAGE(h.usage)),
hid_usage_in_page(h.usage));
var->h = h;
*pnext = var;
pnext = &var->next;
}
hid_end_parse(d);
return;
}
for (i = 0; i < nnames; i++) {
var = malloc(sizeof(*var));
memset(var, 0, sizeof(*var));
tmp1 = tmp2 = strdup(names[i]);
strsep(&tmp2, "=");
var->name = strsep(&tmp1, "#");
if (tmp1 != NULL)
var->instance = atoi(tmp1);
if (tmp2 != NULL) {
if (!wflag)
errx(1, "Must specify -w to write variables");
var->val = atoi(tmp2);
} else
if (wflag)
errx(1, "Must not specify -w to read variables");
*pnext = var;
pnext = &var->next;
instance = 0;
cp = 0;
for (d = hid_start_parse(r,
1<<hid_input | 1<<hid_output | 1<<hid_feature, -1);
hid_get_item(d, &h); ) {
if (h.kind == hid_collection) {
cp += sprintf(&colls[cp], "%s%s:%s",
cp != 0 ? "." : "",
hid_usage_page(HID_PAGE(h.usage)),
hid_usage_in_page(h.usage));
} else if (h.kind == hid_endcollection) {
tmp1 = strrchr(colls, '.');
if (tmp1 != NULL) {
cp -= strlen(tmp1);
tmp1[0] = 0;
} else {
cp = 0;
colls[0] = 0;
}
}
if ((h.kind != hid_input && h.kind != hid_output &&
h.kind != hid_feature) || (h.flags & HIO_CONST))
continue;
snprintf(hname, sizeof(hname), "%s%s%s:%s",
colls, colls[0] != 0 ? "." : "",
hid_usage_page(HID_PAGE(h.usage)),
hid_usage_in_page(h.usage));
t = strlen(hname) - strlen(var->name);
if (t > 0) {
if (strcmp(hname + t, var->name) != 0)
continue;
if (hname[t - 1] != '.')
continue;
} else if (strcmp(hname, var->name) != 0)
continue;
if (var->instance != instance++)
continue;
var->h = h;
break;
}
hid_end_parse(d);
if (var->h.usage == 0)
errx(1, "Unknown item '%s'", var->name);
}
}
void
prbits(int bits, char **strs, int n)
{
int i;
for(i = 0; i < n; i++, bits >>= 1)
if (strs[i*2])
printf("%s%s", i == 0 ? "" : ", ", strs[i*2 + (bits&1)]);
}
void
static void
usage(void)
{
@ -92,10 +188,14 @@ usage(void)
" %s -f device "
"[-l] [-n] [-r] [-t tablefile] [-v] [-x] -a\n",
getprogname());
fprintf(stderr,
" %s -f device "
"[-t tablefile] [-v] [-z] -w name=value\n",
getprogname());
exit(1);
}
void
static void
dumpitem(const char *label, struct hid_item *h)
{
if ((h->flags & HIO_CONST) && !verbose)
@ -134,7 +234,7 @@ hid_collection_type(int32_t type)
return (num);
}
void
static void
dumpitems(report_desc_t r)
{
struct hid_data *d;
@ -174,23 +274,7 @@ dumpitems(report_desc_t r)
printf("Total feature size %d bytes\n", size);
}
void
rev(struct hid_item **p)
{
struct hid_item *cur, *prev, *next;
prev = 0;
cur = *p;
while(cur != 0) {
next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}
*p = prev;
}
void
static void
prdata(u_char *buf, struct hid_item *h)
{
u_int data;
@ -212,82 +296,162 @@ prdata(u_char *buf, struct hid_item *h)
h->pos = pos;
}
void
static void
dumpdata(int f, report_desc_t rd, int loop)
{
struct hid_data *d;
struct hid_item h, *hids, *n;
int r, dlen;
struct variable *var;
int dlen, havedata, i, match, r, rid, use_rid;
u_char *dbuf;
u_int32_t colls[100];
int sp = 0;
char namebuf[10000], *namep;
enum hid_kind kind;
hids = 0;
for (d = hid_start_parse(rd, 1<<hid_input, -1);
hid_get_item(d, &h); ) {
if (h.kind == hid_collection)
colls[++sp] = h.usage;
else if (h.kind == hid_endcollection)
--sp;
if (h.kind != hid_input || (h.flags & HIO_CONST))
continue;
h.next = hids;
h.collection = colls[sp];
hids = malloc(sizeof *hids);
*hids = h;
}
hid_end_parse(d);
rev(&hids);
dlen = hid_report_size(rd, hid_input, -1);
dbuf = malloc(dlen);
if (!loop)
if (hid_set_immed(f, 1) < 0) {
if (errno == EOPNOTSUPP)
warnx("device does not support immediate mode, only changes reported.");
else
err(1, "USB_SET_IMMED");
}
kind = 0;
rid = -1;
use_rid = !!hid_get_report_id(f);
do {
r = read(f, dbuf, dlen);
if (r < 1) {
err(1, "read error");
}
for (n = hids; n; n = n->next) {
if (n->report_ID != 0 && dbuf[0] != n->report_ID)
if (kind < 3) {
if (++rid >= 256) {
rid = 0;
kind++;
}
if (kind >= 3)
rid = -1;
for (var = vars; var; var = var->next) {
if (rid == var->h.report_ID &&
kind == var->h.kind)
break;
}
if (var == NULL)
continue;
namep = namebuf;
namep += sprintf(namep, "%s:%s.",
hid_usage_page(HID_PAGE(n->collection)),
hid_usage_in_page(n->collection));
namep += sprintf(namep, "%s:%s",
hid_usage_page(HID_PAGE(n->usage)),
hid_usage_in_page(n->usage));
if (all || gotname(namebuf)) {
if (!noname)
printf("%s=", namebuf);
prdata(dbuf, n);
}
dlen = hid_report_size(rd, kind < 3 ? kind : hid_input, rid);
if (dlen <= 0)
continue;
dbuf = malloc(dlen);
memset(dbuf, 0, dlen);
if (kind < 3) {
dbuf[0] = rid;
r = hid_get_report(f, kind, dbuf, dlen);
if (r < 0)
warn("hid_get_report(rid %d)", rid);
havedata = !r && (rid == 0 || dbuf[0] == rid);
if (rid != 0)
dbuf[0] = rid;
} else {
r = read(f, dbuf, dlen);
if (r < 1)
err(1, "read error");
havedata = 1;
}
if (verbose) {
printf("Got %s report %d (%d bytes):",
kind == hid_output ? "output" :
kind == hid_feature ? "feature" : "input",
use_rid ? dbuf[0] : 0, dlen);
if (havedata) {
for (i = 0; i < dlen; i++)
printf(" %02x", dbuf[i]);
}
printf("\n");
}
match = 0;
for (var = vars; var; var = var->next) {
if ((kind < 3 ? kind : hid_input) != var->h.kind)
continue;
if (var->h.report_ID != 0 &&
dbuf[0] != var->h.report_ID)
continue;
match = 1;
if (!noname)
printf("%s=", var->name);
if (havedata)
prdata(dbuf, &var->h);
printf("\n");
}
if (match)
printf("\n");
free(dbuf);
} while (loop || kind < 3);
}
static void
writedata(int f, report_desc_t rd)
{
struct variable *var;
int dlen, i, r, rid;
u_char *dbuf;
enum hid_kind kind;
kind = 0;
rid = 0;
for (kind = 0; kind < 3; kind ++) {
for (rid = 0; rid < 256; rid ++) {
for (var = vars; var; var = var->next) {
if (rid == var->h.report_ID && kind == var->h.kind)
break;
}
if (var == NULL)
continue;
dlen = hid_report_size(rd, kind, rid);
if (dlen <= 0)
continue;
dbuf = malloc(dlen);
memset(dbuf, 0, dlen);
dbuf[0] = rid;
if (!zflag && hid_get_report(f, kind, dbuf, dlen) == 0) {
if (verbose) {
printf("Got %s report %d (%d bytes):",
kind == hid_input ? "input" :
kind == hid_output ? "output" : "feature",
rid, dlen);
for (i = 0; i < dlen; i++)
printf(" %02x", dbuf[i]);
printf("\n");
}
} else if (!zflag) {
warn("hid_get_report(rid %d)", rid);
if (verbose) {
printf("Can't get %s report %d (%d bytes). "
"Will be initialized with zeros.\n",
kind == hid_input ? "input" :
kind == hid_output ? "output" : "feature",
rid, dlen);
}
}
if (loop)
for (var = vars; var; var = var->next) {
if (rid != var->h.report_ID || kind != var->h.kind)
continue;
hid_set_data(dbuf, &var->h, var->val);
}
if (verbose) {
printf("Setting %s report %d (%d bytes):",
kind == hid_output ? "output" :
kind == hid_feature ? "feature" : "input",
rid, dlen);
for (i = 0; i < dlen; i++)
printf(" %02x", dbuf[i]);
printf("\n");
} while (loop);
free(dbuf);
}
r = hid_set_report(f, kind, dbuf, dlen);
if (r != 0)
warn("hid_set_report(rid %d)", rid);
free(dbuf);
}
}
}
int
main(int argc, char **argv)
{
int f;
report_desc_t r;
char devnam[100], *dev = 0;
char *table = 0;
char devnam[100], *dev = NULL;
int f;
int all = 0;
int ch;
int repdump = 0;
int loop = 0;
char *table = 0;
while ((ch = getopt(argc, argv, "af:lnrt:vx")) != -1) {
while ((ch = getopt(argc, argv, "af:lnrt:vwxz")) != -1) {
switch(ch) {
case 'a':
all++;
@ -310,9 +474,15 @@ main(int argc, char **argv)
case 'v':
verbose++;
break;
case 'w':
wflag = 1;
break;
case 'x':
hexdump = 1;
break;
case 'z':
zflag = 1;
break;
case '?':
default:
usage();
@ -320,12 +490,10 @@ main(int argc, char **argv)
}
argc -= optind;
argv += optind;
if (dev == 0)
if (dev == NULL)
usage();
names = argv;
nnames = argc;
if (nnames == 0 && !all && !repdump)
if (argc == 0 && !all && !repdump)
usage();
if (dev[0] != '/') {
@ -350,8 +518,13 @@ main(int argc, char **argv)
printf("Report descriptor:\n");
dumpitems(r);
}
if (nnames != 0 || all)
dumpdata(f, r, loop);
if (argc != 0 || all) {
parceargs(r, all, argc, argv);
if (wflag)
writedata(f, r);
else
dumpdata(f, r, loop);
}
hid_dispose_report_desc(r);
exit(0);

View file

@ -28,7 +28,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd November 23, 2006
.Dd August 01, 2011
.Dt USBHIDCTL 1
.Os
.Sh NAME
@ -36,27 +36,51 @@
.Nd manipulate USB HID devices
.Sh SYNOPSIS
.Nm
.Op Fl a
.Fl f Ar device
.Op Fl l
.Op Fl n
.Op Fl r
.Op Fl t Ar table
.Op Fl v
.Op Fl x
.Op Ar item ...
.Fl r
.Nm
.Fl f Ar device
.Op Fl t Ar table
.Op Fl l
.Op Fl v
.Op Fl x
.Fl a
.Nm
.Fl f Ar device
.Op Fl t Ar table
.Op Fl l
.Op Fl n
.Op Fl v
.Op Fl x
.Ar item ...
.Nm
.Fl f Ar device
.Op Fl t Ar table
.Op Fl v
.Op Fl z
.Fl w
.Ar item=value ...
.Sh DESCRIPTION
The
.Nm
utility can be used to dump the state of a USB HID (Human Interface Device).
utility can be used to dump and modify the state of a USB HID (Human
Interface Device).
Each named
.Ar item
is printed.
If the
.Fl w
flag is specified
.Nm
attempts to set the specified items to the given values.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl a
Show all items.
Show all items and their current values if device returns.
.It Fl f Ar device
Specify a path name for the device to operate on.
.It Fl l
@ -69,9 +93,47 @@ Dump the report descriptor.
Specify a path name for the HID usage table file.
.It Fl v
Be verbose.
.It Fl w
Change item values.
Only 'output' and 'feature' kinds can be set with this option.
.It Fl x
Dump data in hexadecimal as well as decimal.
.It Fl z
Reset reports to zero before processing
.Fl w
arguments. If not specified, current values will be requested from device.
.El
.Sh SYNTAX
.Nm
compares 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
.Sq Cm \&: .
The collections are separated by the character
.Sq Cm \&. .
.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 FILES
.Pa /usr/share/misc/usb_hid_usages
The default HID usage table.
@ -84,7 +146,3 @@ The
.Nm
command appeared in
.Nx 1.4 .
.Sh BUGS
The
.Nm
utility cannot show nor set output and feature items.