aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/libusbhid/data.c27
-rw-r--r--lib/libusbhid/parse.c26
-rw-r--r--lib/libusbhid/usbhid.316
-rw-r--r--lib/libusbhid/usbhid.h5
-rw-r--r--sys/dev/usb/input/uhid.c8
-rw-r--r--usr.bin/usbhidaction/usbhidaction.13
-rw-r--r--usr.bin/usbhidaction/usbhidaction.c43
-rw-r--r--usr.bin/usbhidctl/usbhid.c399
-rw-r--r--usr.bin/usbhidctl/usbhidctl.180
9 files changed, 450 insertions, 157 deletions
diff --git a/lib/libusbhid/data.c b/lib/libusbhid/data.c
index 3b90ac6f9560..f607737522d7 100644
--- a/lib/libusbhid/data.c
+++ b/lib/libusbhid/data.c
@@ -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));
+}
diff --git a/lib/libusbhid/parse.c b/lib/libusbhid/parse.c
index d2b49d40cc9b..f7c2cb195be1 100644
--- a/lib/libusbhid/parse.c
+++ b/lib/libusbhid/parse.c
@@ -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;
}
diff --git a/lib/libusbhid/usbhid.3 b/lib/libusbhid/usbhid.3
index b4951f2d11f2..c34a109d5865 100644
--- a/lib/libusbhid/usbhid.3
+++ b/lib/libusbhid/usbhid.3
@@ -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 .
diff --git a/lib/libusbhid/usbhid.h b/lib/libusbhid/usbhid.h
index 5e098eabad09..231ee1fe8a77 100644
--- a/lib/libusbhid/usbhid.h
+++ b/lib/libusbhid/usbhid.h
@@ -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
diff --git a/sys/dev/usb/input/uhid.c b/sys/dev/usb/input/uhid.c
index 1b810e321a0c..47231423c2e2 100644
--- a/sys/dev/usb/input/uhid.c
+++ b/sys/dev/usb/input/uhid.c
@@ -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:
diff --git a/usr.bin/usbhidaction/usbhidaction.1 b/usr.bin/usbhidaction/usbhidaction.1
index 148c6d886d19..633272e398e8 100644
--- a/usr.bin/usbhidaction/usbhidaction.1
+++ b/usr.bin/usbhidaction/usbhidaction.1
@@ -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,
diff --git a/usr.bin/usbhidaction/usbhidaction.c b/usr.bin/usbhidaction/usbhidaction.c
index c71a8fe7cec4..fd66b6433f22 100644
--- a/usr.bin/usbhidaction/usbhidaction.c
+++ b/usr.bin/usbhidaction/usbhidaction.c
@@ -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:
diff --git a/usr.bin/usbhidctl/usbhid.c b/usr.bin/usbhidctl/usbhid.c
index 281078d3de2f..bf3e348d526d 100644
--- a/usr.bin/usbhidctl/usbhid.c
+++ b/usr.bin/usbhidctl/usbhid.c
@@ -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;
-
-char **names;
-int nnames;
-
-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)
+int wflag = 0;
+int zflag = 0;
+
+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);
+
+static void
+parceargs(report_desc_t r, int all, int nnames, char **names)
{
- int i;
-
- for (i = 0; i < nnames; i++)
- if (strcmp(names[i], n) == 0)
- return 1;
- return 0;
-}
-
-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)]);
+ 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;
+
+ 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
+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;
-
- 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))
+ enum hid_kind kind;
+
+ kind = 0;
+ rid = -1;
+ use_rid = !!hid_get_report_id(f);
+ do {
+ 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;
+ }
+ dlen = hid_report_size(rd, kind < 3 ? kind : hid_input, rid);
+ if (dlen <= 0)
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");
+ 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;
}
- do {
- r = read(f, dbuf, dlen);
- if (r < 1) {
- err(1, "read error");
+ 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");
}
- for (n = hids; n; n = n->next) {
- if (n->report_ID != 0 && dbuf[0] != n->report_ID)
+ 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;
- 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);
+ 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);
diff --git a/usr.bin/usbhidctl/usbhidctl.1 b/usr.bin/usbhidctl/usbhidctl.1
index 37c7c7a2fdaf..56811890782a 100644
--- a/usr.bin/usbhidctl/usbhidctl.1
+++ b/usr.bin/usbhidctl/usbhidctl.1
@@ -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 t Ar table
+.Op Fl v
+.Op Fl x
+.Fl r
+.Nm
+.Fl f Ar device
+.Op Fl t Ar table
.Op Fl l
-.Op Fl n
-.Op Fl r
+.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
-.Op Ar item ...
+.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.