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:
parent
3383aff65a
commit
3a1a197150
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 .
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user