usbhidaction.c revision 188945
163670Snsayer/* $NetBSD: usbhidaction.c,v 1.8 2002/06/11 06:06:21 itojun Exp $ */ 263670Snsayer/* $FreeBSD: head/usr.bin/usbhidaction/usbhidaction.c 188945 2009-02-23 18:36:54Z thompsa $ */ 363670Snsayer 463670Snsayer/* 563670Snsayer * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc. 663670Snsayer * All rights reserved. 763670Snsayer * 863670Snsayer * This code is derived from software contributed to The NetBSD Foundation 963670Snsayer * by Lennart Augustsson <lennart@augustsson.net>. 1063670Snsayer * 1163670Snsayer * Redistribution and use in source and binary forms, with or without 1263670Snsayer * modification, are permitted provided that the following conditions 1363670Snsayer * are met: 1463670Snsayer * 1. Redistributions of source code must retain the above copyright 1563670Snsayer * notice, this list of conditions and the following disclaimer. 1663670Snsayer * 2. Redistributions in binary form must reproduce the above copyright 1763670Snsayer * notice, this list of conditions and the following disclaimer in the 1863670Snsayer * documentation and/or other materials provided with the distribution. 1963670Snsayer * 3. All advertising materials mentioning features or use of this software 2063670Snsayer * must display the following acknowledgement: 2163670Snsayer * This product includes software developed by the NetBSD 2263670Snsayer * Foundation, Inc. and its contributors. 2363670Snsayer * 4. Neither the name of The NetBSD Foundation nor the names of its 2463670Snsayer * contributors may be used to endorse or promote products derived 2563670Snsayer * from this software without specific prior written permission. 2663670Snsayer * 2763670Snsayer * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2863670Snsayer * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2963670Snsayer * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 3063670Snsayer * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 3163670Snsayer * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3263670Snsayer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3363670Snsayer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3463670Snsayer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3563803Snsayer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3663670Snsayer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3763670Snsayer * POSSIBILITY OF SUCH DAMAGE. 3863670Snsayer */ 3963670Snsayer 4063670Snsayer#include <stdio.h> 4163670Snsayer#include <stdlib.h> 4263670Snsayer#include <string.h> 4363670Snsayer#include <ctype.h> 4463670Snsayer#include <err.h> 4563670Snsayer#include <fcntl.h> 4663670Snsayer#include <limits.h> 4763670Snsayer#include <unistd.h> 4863670Snsayer#include <sys/types.h> 4963670Snsayer#include <dev/usb/usbhid.h> 5063670Snsayer#include <usbhid.h> 5163670Snsayer#include <syslog.h> 5263670Snsayer#include <signal.h> 5363670Snsayer#include <errno.h> 5463670Snsayer#include <sys/stat.h> 5563670Snsayer 5663670Snsayerstatic int verbose = 0; 5783043Sbrooksstatic int isdemon = 0; 5883043Sbrooksstatic int reparse = 1; 5983043Sbrooksstatic const char * pidfile = "/var/run/usbaction.pid"; 6063670Snsayer 6163670Snsayerstruct command { 6263670Snsayer struct command *next; 6363670Snsayer int line; 6463670Snsayer 6563670Snsayer struct hid_item item; 6663670Snsayer int value; 6763670Snsayer int lastseen; 6863670Snsayer int lastused; 6963670Snsayer int debounce; 7063670Snsayer char anyvalue; 7163670Snsayer char *name; 7263670Snsayer char *action; 7363670Snsayer}; 7463670Snsayerstruct command *commands; 7563670Snsayer 7663670Snsayer#define SIZE 4000 7763670Snsayer 7863670Snsayervoid usage(void); 7983043Sbrooksstruct command *parse_conf(const char *, report_desc_t, int, int); 8083043Sbrooksvoid docmd(struct command *, int, const char *, int, char **); 8183043Sbrooksvoid freecommands(struct command *); 8263670Snsayer 8363670Snsayerstatic void 8493084Sbdesighup(int sig __unused) 8563670Snsayer{ 8663670Snsayer reparse = 1; 8793084Sbde} 8893084Sbde 8963670Snsayerint 9063670Snsayermain(int argc, char **argv) 9193084Sbde{ 9293084Sbde const char *conf = NULL; 9393084Sbde const char *dev = NULL; 9463670Snsayer const char *table = NULL; 9563670Snsayer int fd, fp, ch, n, val, i; 9663670Snsayer size_t sz, sz1; 9763670Snsayer int demon, ignore, dieearly; 9863670Snsayer report_desc_t repd; 9963670Snsayer char buf[100]; 10063670Snsayer char devnamebuf[PATH_MAX]; 10163670Snsayer struct command *cmd; 10263670Snsayer int reportid; 10363670Snsayer 10463670Snsayer demon = 1; 10563670Snsayer ignore = 0; 10663670Snsayer dieearly = 0; 10763670Snsayer while ((ch = getopt(argc, argv, "c:def:ip:t:v")) != -1) { 10863670Snsayer switch(ch) { 10963670Snsayer case 'c': 11063670Snsayer conf = optarg; 11163670Snsayer break; 11263670Snsayer case 'd': 11363670Snsayer demon ^= 1; 11463670Snsayer break; 11563670Snsayer case 'e': 11663670Snsayer dieearly = 1; 11763670Snsayer break; 11863670Snsayer case 'i': 11983043Sbrooks ignore++; 12083043Sbrooks break; 12183043Sbrooks case 'f': 12283043Sbrooks dev = optarg; 12383043Sbrooks break; 12483043Sbrooks case 'p': 12563670Snsayer pidfile = optarg; 12663670Snsayer break; 12763670Snsayer case 't': 12863670Snsayer table = optarg; 12963670Snsayer break; 13063670Snsayer case 'v': 13163670Snsayer demon = 0; 13263670Snsayer verbose++; 13363670Snsayer break; 13463670Snsayer case '?': 13563670Snsayer default: 13663670Snsayer usage(); 13763670Snsayer } 13863670Snsayer } 13963670Snsayer argc -= optind; 14063670Snsayer argv += optind; 14163670Snsayer 14283043Sbrooks if (conf == NULL || dev == NULL) 14383043Sbrooks usage(); 14483043Sbrooks 14583043Sbrooks hid_init(table); 14663670Snsayer 14763670Snsayer if (dev[0] != '/') { 14863670Snsayer snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s", 14983043Sbrooks isdigit(dev[0]) ? "uhid" : "", dev); 15083043Sbrooks dev = devnamebuf; 15183043Sbrooks } 15283043Sbrooks 15383043Sbrooks fd = open(dev, O_RDWR); 15463670Snsayer if (fd < 0) 15583043Sbrooks err(1, "%s", dev); 15683043Sbrooks reportid = hid_get_report_id(fd); 15783043Sbrooks repd = hid_get_report_desc(fd); 15883043Sbrooks if (repd == NULL) 15983043Sbrooks err(1, "hid_get_report_desc() failed"); 16083043Sbrooks 16183043Sbrooks commands = parse_conf(conf, repd, reportid, ignore); 16283043Sbrooks 16383043Sbrooks sz = (size_t)hid_report_size(repd, hid_input, reportid); 16483043Sbrooks 16583043Sbrooks if (verbose) 16683043Sbrooks printf("report size %zu\n", sz); 16783043Sbrooks if (sz > sizeof buf) 16883043Sbrooks errx(1, "report too large"); 16983043Sbrooks 17083043Sbrooks (void)signal(SIGHUP, sighup); 17183043Sbrooks 17283043Sbrooks if (demon) { 17383043Sbrooks fp = open(pidfile, O_WRONLY|O_CREAT, S_IRUSR|S_IRGRP|S_IROTH); 17483043Sbrooks if (fp >= 0) { 17571602Sphk sz1 = snprintf(buf, sizeof buf, "%d\n", getpid()); 17683043Sbrooks if (sz1 > sizeof buf) 17783043Sbrooks sz1 = sizeof buf; 17883043Sbrooks write(fp, buf, sz1); 17983043Sbrooks close(fp); 18063670Snsayer } else 18183043Sbrooks err(1, "%s", pidfile); 18283043Sbrooks if (daemon(0, 0) < 0) 18383043Sbrooks err(1, "daemon()"); 18483043Sbrooks isdemon = 1; 18583043Sbrooks } 18683043Sbrooks 18783043Sbrooks for(;;) { 18871602Sphk n = read(fd, buf, sz); 18983043Sbrooks if (verbose > 2) { 19083043Sbrooks printf("read %d bytes:", n); 19183043Sbrooks for (i = 0; i < n; i++) 19283043Sbrooks printf(" %02x", buf[i]); 19383043Sbrooks printf("\n"); 19483043Sbrooks } 19583043Sbrooks if (n < 0) { 19663670Snsayer if (verbose) 19783043Sbrooks err(1, "read"); 19883043Sbrooks else 19983043Sbrooks exit(1); 20083043Sbrooks } 20183043Sbrooks#if 0 20271602Sphk if (n != sz) { 20363670Snsayer err(2, "read size"); 20483043Sbrooks } 20583043Sbrooks#endif 20683043Sbrooks for (cmd = commands; cmd; cmd = cmd->next) { 20783043Sbrooks val = hid_get_data(buf, &cmd->item); 20871602Sphk if (cmd->value != val && cmd->anyvalue == 0) 20983043Sbrooks goto next; 21083043Sbrooks if ((cmd->debounce == 0) || 21183043Sbrooks ((cmd->debounce == 1) && ((cmd->lastseen == -1) || 21283043Sbrooks (cmd->lastseen != val)))) { 21383043Sbrooks docmd(cmd, val, dev, argc, argv); 21483043Sbrooks goto next; 21583043Sbrooks } 21683043Sbrooks if ((cmd->debounce > 1) && 21783043Sbrooks ((cmd->lastused == -1) || 21883043Sbrooks (abs(cmd->lastused - val) >= cmd->debounce))) { 21983043Sbrooks docmd(cmd, val, dev, argc, argv); 22083043Sbrooks cmd->lastused = val; 22183043Sbrooks goto next; 22263670Snsayer } 22383043Sbrooksnext: 22463670Snsayer cmd->lastseen = val; 22563670Snsayer } 22693752Sluigi 22783043Sbrooks if (dieearly) 22863670Snsayer exit(0); 22983043Sbrooks 23083043Sbrooks if (reparse) { 23163670Snsayer struct command *cmds = 23283043Sbrooks parse_conf(conf, repd, reportid, ignore); 23383043Sbrooks if (cmds) { 23463670Snsayer freecommands(commands); 23583043Sbrooks commands = cmds; 23663670Snsayer } 23763670Snsayer reparse = 0; 23863670Snsayer } 23963670Snsayer } 24063670Snsayer 24163670Snsayer exit(0); 24263670Snsayer} 24363670Snsayer 24463670Snsayervoid 24563670Snsayerusage(void) 24671602Sphk{ 24771602Sphk 24871602Sphk fprintf(stderr, "Usage: %s [-deiv] -c config_file -f hid_dev " 24971602Sphk "[-p pidfile] [-t tablefile]\n", getprogname()); 25071602Sphk exit(1); 25171602Sphk} 25271602Sphk 25371602Sphkstatic int 25471602Sphkpeek(FILE *f) 25571602Sphk{ 25671602Sphk int c; 25783043Sbrooks 25883043Sbrooks c = getc(f); 25983043Sbrooks if (c != EOF) 26071602Sphk ungetc(c, f); 26171602Sphk return c; 26271602Sphk} 26371602Sphk 26483043Sbrooksstruct command * 26583043Sbrooksparse_conf(const char *conf, report_desc_t repd, int reportid, int ignore) 26683043Sbrooks{ 26783043Sbrooks FILE *f; 26883043Sbrooks char *p; 26983043Sbrooks int line; 27083043Sbrooks char buf[SIZE], name[SIZE], value[SIZE], debounce[SIZE], action[SIZE]; 27183043Sbrooks char usbuf[SIZE], coll[SIZE]; 27283043Sbrooks struct command *cmd, *cmds; 27383043Sbrooks struct hid_data *d; 27483043Sbrooks struct hid_item h; 27583043Sbrooks int u, lo, hi, range; 27683043Sbrooks 27783043Sbrooks 27871602Sphk f = fopen(conf, "r"); 27983043Sbrooks if (f == NULL) 28083043Sbrooks err(1, "%s", conf); 28183043Sbrooks 28271602Sphk cmds = NULL; 28383043Sbrooks for (line = 1; ; line++) { 28483043Sbrooks if (fgets(buf, sizeof buf, f) == NULL) 28583043Sbrooks break; 28683043Sbrooks if (buf[0] == '#' || buf[0] == '\n') 28783043Sbrooks continue; 28883043Sbrooks p = strchr(buf, '\n'); 28983043Sbrooks while (p && isspace(peek(f))) { 29083043Sbrooks if (fgets(p, sizeof buf - strlen(buf), f) == NULL) 29183043Sbrooks break; 29283043Sbrooks p = strchr(buf, '\n'); 29383043Sbrooks } 29483043Sbrooks if (p) 29583043Sbrooks *p = 0; 29683043Sbrooks if (sscanf(buf, "%s %s %s %[^\n]", 29783043Sbrooks name, value, debounce, action) != 4) { 29883043Sbrooks if (isdemon) { 29983043Sbrooks syslog(LOG_WARNING, "config file `%s', line %d" 30083043Sbrooks ", syntax error: %s", conf, line, buf); 30183043Sbrooks freecommands(cmds); 30283043Sbrooks return (NULL); 30383043Sbrooks } else { 30471602Sphk errx(1, "config file `%s', line %d," 30571602Sphk ", syntax error: %s", conf, line, buf); 30683043Sbrooks } 30783043Sbrooks } 30871602Sphk 30971602Sphk cmd = malloc(sizeof *cmd); 31083043Sbrooks if (cmd == NULL) 31183043Sbrooks err(1, "malloc failed"); 31283043Sbrooks cmd->next = cmds; 31383043Sbrooks cmds = cmd; 31483043Sbrooks cmd->line = line; 31583043Sbrooks 31683043Sbrooks if (strcmp(value, "*") == 0) { 31771602Sphk cmd->anyvalue = 1; 31871602Sphk } else { 31971602Sphk cmd->anyvalue = 0; 32071602Sphk if (sscanf(value, "%d", &cmd->value) != 1) { 32163670Snsayer if (isdemon) { 32263670Snsayer syslog(LOG_WARNING, 32363670Snsayer "config file `%s', line %d, " 32463670Snsayer "bad value: %s (should be * or a number)\n", 32563670Snsayer conf, line, value); 32663670Snsayer freecommands(cmds); 32763670Snsayer return (NULL); 32863670Snsayer } else { 32963670Snsayer errx(1, "config file `%s', line %d, " 33063670Snsayer "bad value: %s (should be * or a number)\n", 33163670Snsayer conf, line, value); 33263803Snsayer } 33363670Snsayer } 33463670Snsayer } 33563670Snsayer 33669781Sdwmalone if (sscanf(debounce, "%d", &cmd->debounce) != 1) { 33783043Sbrooks if (isdemon) { 33863670Snsayer syslog(LOG_WARNING, 33983043Sbrooks "config file `%s', line %d, " 34083043Sbrooks "bad value: %s (should be a number >= 0)\n", 34163670Snsayer conf, line, debounce); 34263670Snsayer freecommands(cmds); 34363670Snsayer return (NULL); 34463803Snsayer } else { 34583043Sbrooks errx(1, "config file `%s', line %d, " 34663670Snsayer "bad value: %s (should be a number >= 0)\n", 34763670Snsayer conf, line, debounce); 34883043Sbrooks } 34983043Sbrooks } 35083043Sbrooks 35183043Sbrooks coll[0] = 0; 35263670Snsayer for (d = hid_start_parse(repd, 1 << hid_input, reportid); 35363670Snsayer hid_get_item(d, &h); ) { 35463670Snsayer if (verbose > 2) 35563670Snsayer printf("kind=%d usage=%x\n", h.kind, h.usage); 35663670Snsayer if (h.flags & HIO_CONST) 35763670Snsayer continue; 35863670Snsayer switch (h.kind) { 35963670Snsayer case hid_input: 36063670Snsayer if (h.usage_minimum != 0 || 36163670Snsayer h.usage_maximum != 0) { 36263670Snsayer lo = h.usage_minimum; 36363670Snsayer hi = h.usage_maximum; 36463670Snsayer range = 1; 36563670Snsayer } else { 36663670Snsayer lo = h.usage; 36763670Snsayer hi = h.usage; 36863670Snsayer range = 0; 36963670Snsayer } 37063670Snsayer for (u = lo; u <= hi; u++) { 37163670Snsayer snprintf(usbuf, sizeof usbuf, "%s:%s", 37263670Snsayer hid_usage_page(HID_PAGE(u)), 37383043Sbrooks hid_usage_in_page(u)); 37483043Sbrooks if (verbose > 2) 37563803Snsayer printf("usage %s\n", usbuf); 37683043Sbrooks if (!strcasecmp(usbuf, name)) 37763803Snsayer goto foundhid; 37863670Snsayer if (coll[0]) { 37963803Snsayer snprintf(usbuf, sizeof usbuf, 38063803Snsayer "%s.%s:%s", coll+1, 38183043Sbrooks hid_usage_page(HID_PAGE(u)), 38283043Sbrooks hid_usage_in_page(u)); 38363670Snsayer if (verbose > 2) 38463670Snsayer printf("usage %s\n", 38563670Snsayer usbuf); 38663670Snsayer if (!strcasecmp(usbuf, name)) 38763670Snsayer goto foundhid; 38863670Snsayer } 38963670Snsayer } 39063670Snsayer break; 39163670Snsayer case hid_collection: 39283366Sjulian snprintf(coll + strlen(coll), 39363670Snsayer sizeof coll - strlen(coll), ".%s:%s", 39463670Snsayer hid_usage_page(HID_PAGE(h.usage)), 39563670Snsayer hid_usage_in_page(h.usage)); 39683366Sjulian break; 39763670Snsayer case hid_endcollection: 39863670Snsayer if (coll[0]) 39983043Sbrooks *strrchr(coll, '.') = 0; 40083043Sbrooks break; 40163670Snsayer default: 40293593Sjhb break; 40363670Snsayer } 40463670Snsayer } 40583043Sbrooks if (ignore) { 40683043Sbrooks if (verbose) 40783043Sbrooks warnx("ignore item '%s'", name); 40883043Sbrooks continue; 40983043Sbrooks } 41083043Sbrooks if (isdemon) { 41183043Sbrooks syslog(LOG_WARNING, "config file `%s', line %d, HID " 41283043Sbrooks "item not found: `%s'\n", conf, line, name); 41383043Sbrooks freecommands(cmds); 41483043Sbrooks return (NULL); 41583043Sbrooks } else { 41683043Sbrooks errx(1, "config file `%s', line %d, HID item " 41783043Sbrooks "not found: `%s'\n", conf, line, name); 41883043Sbrooks } 41963670Snsayer 42063670Snsayer foundhid: 42163670Snsayer hid_end_parse(d); 42263670Snsayer cmd->lastseen = -1; 42363670Snsayer cmd->lastused = -1; 42463670Snsayer cmd->item = h; 42583043Sbrooks cmd->name = strdup(name); 42683043Sbrooks cmd->action = strdup(action); 42763670Snsayer if (range) { 42863861Snsayer if (cmd->value == 1) 42963861Snsayer cmd->value = u - lo; 43083043Sbrooks else 43183366Sjulian cmd->value = -1; 43263670Snsayer } 43363670Snsayer 43483043Sbrooks if (verbose) 43583043Sbrooks printf("PARSE:%d %s, %d, '%s'\n", cmd->line, name, 43663670Snsayer cmd->value, cmd->action); 43763670Snsayer } 43863670Snsayer fclose(f); 43963670Snsayer return (cmds); 44063670Snsayer} 44163670Snsayer 44263670Snsayervoid 44363670Snsayerdocmd(struct command *cmd, int value, const char *hid, int argc, char **argv) 44463670Snsayer{ 44563670Snsayer char cmdbuf[SIZE], *p, *q; 44663670Snsayer size_t len; 44783366Sjulian int n, r; 44863670Snsayer 44963670Snsayer for (p = cmd->action, q = cmdbuf; *p && q < &cmdbuf[SIZE-1]; ) { 45063670Snsayer if (*p == '$') { 45183366Sjulian p++; 45263670Snsayer len = &cmdbuf[SIZE-1] - q; 45383043Sbrooks if (isdigit(*p)) { 45463670Snsayer n = strtol(p, &p, 10) - 1; 45563670Snsayer if (n >= 0 && n < argc) { 45663670Snsayer strncpy(q, argv[n], len); 45787914Sjlemon q += strlen(q); 45883043Sbrooks } 45983043Sbrooks } else if (*p == 'V') { 46063670Snsayer p++; 46183043Sbrooks snprintf(q, len, "%d", value); 46263670Snsayer q += strlen(q); 46363803Snsayer } else if (*p == 'N') { 46463803Snsayer p++; 46563803Snsayer strncpy(q, cmd->name, len); 46663803Snsayer q += strlen(q); 46763803Snsayer } else if (*p == 'H') { 46863803Snsayer p++; 46963670Snsayer strncpy(q, hid, len); 47063670Snsayer q += strlen(q); 47163670Snsayer } else if (*p) { 47263670Snsayer *q++ = *p++; 47363670Snsayer } 47463670Snsayer } else { 47563803Snsayer *q++ = *p++; 47663670Snsayer } 47763670Snsayer } 47863670Snsayer *q = 0; 47963670Snsayer 48063670Snsayer if (verbose) 48163670Snsayer printf("system '%s'\n", cmdbuf); 48263670Snsayer r = system(cmdbuf); 48363670Snsayer if (verbose > 1 && r) 48463670Snsayer printf("return code = 0x%x\n", r); 48563670Snsayer} 48663670Snsayer 48763670Snsayervoid 48863670Snsayerfreecommands(struct command *cmd) 48963670Snsayer{ 49063670Snsayer struct command *next; 49163670Snsayer 49263670Snsayer while (cmd) { 49363670Snsayer next = cmd->next; 49463670Snsayer free(cmd); 49563670Snsayer cmd = next; 49663670Snsayer } 49763670Snsayer} 49863670Snsayer