diff options
author | mdodd <mdodd@FreeBSD.org> | 2003-04-09 11:16:34 +0000 |
---|---|---|
committer | mdodd <mdodd@FreeBSD.org> | 2003-04-09 11:16:34 +0000 |
commit | 10cf38638588a4fce7f343231a94eab7909408e4 (patch) | |
tree | bfaac44acbbd4e9aade1a87b48e2bcacc3e0c8b4 /usr.bin/usbhidaction/usbhidaction.c | |
parent | 64d3a8bc2198c8655bd946674dfd47ce8fb31a8a (diff) | |
download | FreeBSD-src-10cf38638588a4fce7f343231a94eab7909408e4.zip FreeBSD-src-10cf38638588a4fce7f343231a94eab7909408e4.tar.gz |
Add usbhidaction(1).
This allows actions to be bound to HID events.
Obtained from: NetBSD
Diffstat (limited to 'usr.bin/usbhidaction/usbhidaction.c')
-rw-r--r-- | usr.bin/usbhidaction/usbhidaction.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/usr.bin/usbhidaction/usbhidaction.c b/usr.bin/usbhidaction/usbhidaction.c new file mode 100644 index 0000000..22868b1 --- /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; + } +} |