aboutsummaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorMatthew N. Dodd <mdodd@FreeBSD.org>2003-04-09 11:16:34 +0000
committerMatthew N. Dodd <mdodd@FreeBSD.org>2003-04-09 11:16:34 +0000
commitce5b934709be8dc766eac9401f19a012315cd53f (patch)
treebfaac44acbbd4e9aade1a87b48e2bcacc3e0c8b4 /usr.bin
parenta362eb6374fe3d4515a75adc9a83f52f9dee2788 (diff)
downloadsrc-ce5b934709be8dc766eac9401f19a012315cd53f.tar.gz
src-ce5b934709be8dc766eac9401f19a012315cd53f.zip
Add usbhidaction(1).
This allows actions to be bound to HID events. Obtained from: NetBSD
Notes
Notes: svn path=/head/; revision=113288
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/Makefile1
-rw-r--r--usr.bin/usbhidaction/Makefile10
-rw-r--r--usr.bin/usbhidaction/usbhidaction.1154
-rw-r--r--usr.bin/usbhidaction/usbhidaction.c449
4 files changed, 614 insertions, 0 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index 8a4b5ce9703a..ea43e5505663 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -186,6 +186,7 @@ SUBDIR= alias \
uniq \
units \
unvis \
+ usbhidaction \
usbhidctl \
users \
uudecode \
diff --git a/usr.bin/usbhidaction/Makefile b/usr.bin/usbhidaction/Makefile
new file mode 100644
index 000000000000..8f5a6b58c2a3
--- /dev/null
+++ b/usr.bin/usbhidaction/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+# $NetBSD: Makefile,v 1.4 2002/02/02 16:54:26 veego Exp $
+
+PROG= usbhidaction
+SRCS= usbhidaction.c
+
+LDADD+= -lusbhid
+DPADD+= ${LIBUSBHID}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/usbhidaction/usbhidaction.1 b/usr.bin/usbhidaction/usbhidaction.1
new file mode 100644
index 000000000000..b0b491e3d32a
--- /dev/null
+++ b/usr.bin/usbhidaction/usbhidaction.1
@@ -0,0 +1,154 @@
+.\" $FreeBSD$
+.\" $NetBSD: usbhidaction.1,v 1.8 2003/02/25 10:35:59 wiz Exp $
+.\"
+.\" Copyright (c) 2000 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Lennart Augustsson (lennart@augustsson.net).
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd April 9, 2003
+.Dt USBHIDACTION 1
+.Os
+.Sh NAME
+.Nm usbhidaction
+.Nd perform actions according to USB HID controls
+.Sh SYNOPSIS
+.Nm
+.Fl c Ar config-file
+.Op Fl d
+.Op Fl i
+.Fl f Ar device
+.Op Fl p Ar pidfile
+.Op Fl v
+.Ar arg ...
+.Sh DESCRIPTION
+.Nm
+can be used to execute commands when certain values appear on HID controls.
+The normal operation for this program is to read the configuration file
+and then become a daemon and execute commands as the HID items specify.
+If a read from the HID device fails the program dies; this will make it
+die when the USB device is unplugged.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c Ar config-file
+Specify a path name for the config file.
+.It Fl d
+Toggle the daemon flag.
+.It Fl i
+Ignore HID items in the config file that does not exist in the device.
+.It Fl f Ar device
+Specify a path name for the device to operate on.
+If
+.Ar device
+is numeric, it is taken to be the USB HID device number.
+If it is a relative
+path, it is taken to be the name of the device under
+.Pa /dev .
+An absolute path is taken to be the literal device pathname.
+.It Fl p Ar pidfile
+Specify an alternate file in which to store the process ID.
+.It Fl v
+Be verbose, and do not become a daemon.
+.El
+.Pp
+The config file will be re-read if the process gets a HUP signal.
+.Sh CONFIGURATION
+The configuration file has a very simple format.
+Each line describes an
+action; if a line begins with a whitespace it is considered a continuation
+of the previous line.
+Lines beginning with `#' are considered as comments.
+.Pp
+Each line has three parts: a name of a USB HID item, a value for that item,
+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.
+.Pp
+The value is simply a numeric value.
+When the item reports this value
+the action will be performed.
+If the value is `*' it will match any value.
+.Pp
+The action is a normal command that is executed with
+.Xr system 3 .
+Before it is executed some substitution will occur:
+`$n' will be replaced by the nth argument on the
+command line, `$V' will be replaced by the numeric value
+of the HID item, `$N' will be replaced by the name
+of the control, and `$H' will be replaced by the name
+of the HID device.
+.Sh FILES
+.Bl -tag -indent
+.It Pa /usr/share/misc/usb_hid_usages
+The HID usage table.
+.It Pa /var/run/usbaction.pid
+The default location of the pid file.
+.El
+.Sh EXAMPLES
+The following configuration file can be used to control a pair
+of Philips USB speakers with the HID controls on the speakers.
+.Bd -literal -offset indent
+# Configuration for various Philips USB speakers
+Consumer:Consumer_Control.Consumer:Volume_Up 1
+ mixerctl -f $1 -n -w fea8-i7-master++
+Consumer:Consumer_Control.Consumer:Volume_Down 1
+ mixerctl -f $1 -n -w fea8-i7-master--
+Consumer:Consumer_Control.Consumer:Mute 1
+ mixerctl -f $1 -n -w fea8-i7-mute++
+Consumer:Consumer_Control.Consumer:Channel_Top.Microsoft:Base_Up 1
+ mixerctl -f $1 -n -w fea8-i7-bass++
+Consumer:Consumer_Control.Consumer:Channel_Top.Microsoft:Base_Down 1
+ mixerctl -f $1 -n -w fea8-i7-bass--
+.Ed
+.Pp
+A sample invocation using this configuration would be
+.Bd -literal -offset indent
+usbhidaction -f /dev/uhid1 -c conf /dev/mixer1
+.Ed
+.Sh SEE ALSO
+.Xr usbhidctl 1 ,
+.Xr usbhid 3 ,
+.Xr uhid 4 ,
+.Xr usb 4
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Nx 1.6 .
+The
+.Nm
+command appeard in
+.Fx 5.1 .
diff --git a/usr.bin/usbhidaction/usbhidaction.c b/usr.bin/usbhidaction/usbhidaction.c
new file mode 100644
index 000000000000..22868b138a21
--- /dev/null
+++ b/usr.bin/usbhidaction/usbhidaction.c
@@ -0,0 +1,449 @@
+/* $NetBSD: usbhidaction.c,v 1.8 2002/06/11 06:06:21 itojun Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson <lennart@augustsson.net>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <usbhid.h>
+#include <syslog.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+static int verbose = 0;
+static int isdemon = 0;
+static int reparse = 1;
+static char * pidfile = "/var/run/usbaction.pid";
+
+struct command {
+ struct command *next;
+ int line;
+
+ struct hid_item item;
+ int value;
+ char anyvalue;
+ char *name;
+ char *action;
+};
+struct command *commands;
+
+#define SIZE 4000
+
+void usage(void);
+struct command *parse_conf(const char *, report_desc_t, int, int);
+void docmd(struct command *, int, const char *, int, char **);
+void freecommands(struct command *);
+
+static void
+sighup(int sig)
+{
+ reparse = 1;
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *conf = NULL;
+ const char *dev = NULL;
+ int fd, fp, ch, sz, n, val, i;
+ int demon, ignore;
+ report_desc_t repd;
+ char buf[100];
+ char devnamebuf[PATH_MAX];
+ struct command *cmd;
+ int reportid;
+
+ demon = 1;
+ ignore = 0;
+ while ((ch = getopt(argc, argv, "c:df:ip:v")) != -1) {
+ switch(ch) {
+ case 'c':
+ conf = optarg;
+ break;
+ case 'd':
+ demon ^= 1;
+ break;
+ case 'i':
+ ignore++;
+ break;
+ case 'f':
+ dev = optarg;
+ break;
+ case 'p':
+ pidfile = optarg;
+ break;
+ case 'v':
+ demon = 0;
+ verbose++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (conf == NULL || dev == NULL)
+ usage();
+
+ hid_init(NULL);
+
+ if (dev[0] != '/') {
+ snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s",
+ isdigit(dev[0]) ? "uhid" : "", dev);
+ dev = devnamebuf;
+ }
+
+ fd = open(dev, O_RDWR);
+ if (fd < 0)
+ err(1, "%s", dev);
+ if (ioctl(fd, USB_GET_REPORT_ID, &reportid) < 0)
+ reportid = -1;
+ repd = hid_get_report_desc(fd);
+ if (repd == NULL)
+ err(1, "hid_get_report_desc() failed");
+
+ commands = parse_conf(conf, repd, reportid, ignore);
+
+ sz = hid_report_size(repd, hid_input, reportid);
+
+ if (verbose)
+ printf("report size %d\n", sz);
+ if (sz > sizeof buf)
+ errx(1, "report too large");
+
+ (void)signal(SIGHUP, sighup);
+
+ if (demon) {
+ if (daemon(0, 0) < 0)
+ err(1, "daemon()");
+ fp = open(pidfile, O_WRONLY|O_CREAT, S_IRUSR|S_IRGRP|S_IROTH);
+ if (fp >= 0) {
+ sz=snprintf(buf,100, "%d\n", getpid());
+ write(fp, buf, sz);
+ close(fp);
+ } else
+ err(1, "%s", pidfile);
+ isdemon = 1;
+ }
+
+ for(;;) {
+ n = read(fd, buf, sz);
+ if (verbose > 2) {
+ printf("read %d bytes:", n);
+ for (i = 0; i < n; i++)
+ printf(" %02x", buf[i]);
+ printf("\n");
+ }
+ if (n < 0) {
+ if (verbose)
+ err(1, "read");
+ else
+ exit(1);
+ }
+#if 0
+ if (n != sz) {
+ err(2, "read size");
+ }
+#endif
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ val = hid_get_data(buf, &cmd->item);
+ if (cmd->value == val || cmd->anyvalue)
+ docmd(cmd, val, dev, argc, argv);
+ }
+ if (reparse) {
+ struct command *cmds =
+ parse_conf(conf, repd, reportid, ignore);
+ if (cmds) {
+ freecommands(commands);
+ commands = cmds;
+ }
+ reparse = 0;
+ }
+ }
+
+ exit(0);
+}
+
+void
+usage(void)
+{
+
+ fprintf(stderr, "Usage: %s -c config_file [-d] -f hid_dev "
+ "[-i] [-p pidfile] [-v]\n", getprogname());
+ exit(1);
+}
+
+static int
+peek(FILE *f)
+{
+ int c;
+
+ c = getc(f);
+ if (c != EOF)
+ ungetc(c, f);
+ return c;
+}
+
+struct command *
+parse_conf(const char *conf, report_desc_t repd, int reportid, int ignore)
+{
+ FILE *f;
+ char *p;
+ int line;
+ char buf[SIZE], name[SIZE], value[SIZE], action[SIZE];
+ char usage[SIZE], coll[SIZE];
+ struct command *cmd, *cmds;
+ struct hid_data *d;
+ struct hid_item h;
+ int u, lo, hi, range;
+
+
+ f = fopen(conf, "r");
+ if (f == NULL)
+ err(1, "%s", conf);
+
+ cmds = NULL;
+ for (line = 1; ; line++) {
+ if (fgets(buf, sizeof buf, f) == NULL)
+ break;
+ if (buf[0] == '#' || buf[0] == '\n')
+ continue;
+ p = strchr(buf, '\n');
+ while (p && isspace(peek(f))) {
+ if (fgets(p, sizeof buf - strlen(buf), f) == NULL)
+ break;
+ p = strchr(buf, '\n');
+ }
+ if (p)
+ *p = 0;
+ if (sscanf(buf, "%s %s %[^\n]", name, value, action) != 3) {
+ if (isdemon) {
+ syslog(LOG_WARNING, "config file `%s', line %d"
+ ", syntax error: %s", conf, line, buf);
+ freecommands(cmds);
+ return (NULL);
+ } else {
+ errx(1, "config file `%s', line %d,"
+ ", syntax error: %s", conf, line, buf);
+ }
+ }
+
+ cmd = malloc(sizeof *cmd);
+ if (cmd == NULL)
+ err(1, "malloc failed");
+ cmd->next = cmds;
+ cmds = cmd;
+ cmd->line = line;
+
+ if (strcmp(value, "*") == 0) {
+ cmd->anyvalue = 1;
+ } else {
+ cmd->anyvalue = 0;
+ if (sscanf(value, "%d", &cmd->value) != 1) {
+ if (isdemon) {
+ syslog(LOG_WARNING,
+ "config file `%s', line %d, "
+ "bad value: %s\n",
+ conf, line, value);
+ freecommands(cmds);
+ return (NULL);
+ } else {
+ errx(1, "config file `%s', line %d, "
+ "bad value: %s\n",
+ conf, line, value);
+ }
+ }
+ }
+
+ coll[0] = 0;
+ for (d = hid_start_parse(repd, 1 << hid_input, reportid);
+ hid_get_item(d, &h); ) {
+ if (verbose > 2)
+ printf("kind=%d usage=%x\n", h.kind, h.usage);
+ if (h.flags & HIO_CONST)
+ continue;
+ switch (h.kind) {
+ case hid_input:
+ if (h.usage_minimum != 0 ||
+ h.usage_maximum != 0) {
+ lo = h.usage_minimum;
+ hi = h.usage_maximum;
+ range = 1;
+ } else {
+ lo = h.usage;
+ hi = h.usage;
+ range = 0;
+ }
+ for (u = lo; u <= hi; u++) {
+ snprintf(usage, sizeof usage, "%s:%s",
+ hid_usage_page(HID_PAGE(u)),
+ hid_usage_in_page(u));
+ if (verbose > 2)
+ printf("usage %s\n", usage);
+ if (!strcasecmp(usage, name))
+ goto foundhid;
+ if (coll[0]) {
+ snprintf(usage, sizeof usage,
+ "%s.%s:%s", coll+1,
+ hid_usage_page(HID_PAGE(u)),
+ hid_usage_in_page(u));
+ if (verbose > 2)
+ printf("usage %s\n",
+ usage);
+ if (!strcasecmp(usage, name))
+ goto foundhid;
+ }
+ }
+ break;
+ case hid_collection:
+ snprintf(coll + strlen(coll),
+ sizeof coll - strlen(coll), ".%s:%s",
+ hid_usage_page(HID_PAGE(h.usage)),
+ hid_usage_in_page(h.usage));
+ break;
+ case hid_endcollection:
+ if (coll[0])
+ *strrchr(coll, '.') = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ if (ignore) {
+ if (verbose)
+ warnx("ignore item '%s'", name);
+ continue;
+ }
+ if (isdemon) {
+ syslog(LOG_WARNING, "config file `%s', line %d, HID "
+ "item not found: `%s'\n", conf, line, name);
+ freecommands(cmds);
+ return (NULL);
+ } else {
+ errx(1, "config file `%s', line %d, HID item "
+ "not found: `%s'\n", conf, line, name);
+ }
+
+ foundhid:
+ hid_end_parse(d);
+ cmd->item = h;
+ cmd->name = strdup(name);
+ cmd->action = strdup(action);
+ if (range) {
+ if (cmd->value == 1)
+ cmd->value = u - lo;
+ else
+ cmd->value = -1;
+ }
+
+ if (verbose)
+ printf("PARSE:%d %s, %d, '%s'\n", cmd->line, name,
+ cmd->value, cmd->action);
+ }
+ fclose(f);
+ return (cmds);
+}
+
+void
+docmd(struct command *cmd, int value, const char *hid, int argc, char **argv)
+{
+ char cmdbuf[SIZE], *p, *q;
+ size_t len;
+ int n, r;
+
+ for (p = cmd->action, q = cmdbuf; *p && q < &cmdbuf[SIZE-1]; ) {
+ if (*p == '$') {
+ p++;
+ len = &cmdbuf[SIZE-1] - q;
+ if (isdigit(*p)) {
+ n = strtol(p, &p, 10) - 1;
+ if (n >= 0 && n < argc) {
+ strncpy(q, argv[n], len);
+ q += strlen(q);
+ }
+ } else if (*p == 'V') {
+ p++;
+ snprintf(q, len, "%d", value);
+ q += strlen(q);
+ } else if (*p == 'N') {
+ p++;
+ strncpy(q, cmd->name, len);
+ q += strlen(q);
+ } else if (*p == 'H') {
+ p++;
+ strncpy(q, hid, len);
+ q += strlen(q);
+ } else if (*p) {
+ *q++ = *p++;
+ }
+ } else {
+ *q++ = *p++;
+ }
+ }
+ *q = 0;
+
+ if (verbose)
+ printf("system '%s'\n", cmdbuf);
+ r = system(cmdbuf);
+ if (verbose > 1 && r)
+ printf("return code = 0x%x\n", r);
+}
+
+void
+freecommands(struct command *cmd)
+{
+ struct command *next;
+
+ while (cmd) {
+ next = cmd->next;
+ free(cmd);
+ cmd = next;
+ }
+}