usbhidaction.c revision 113328
1195534Sscottl/* $NetBSD: usbhidaction.c,v 1.8 2002/06/11 06:06:21 itojun Exp $ */ 2195534Sscottl/* $FreeBSD: head/usr.bin/usbhidaction/usbhidaction.c 113328 2003-04-10 08:06:56Z mdodd $ */ 3195534Sscottl 4195534Sscottl/* 5195534Sscottl * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc. 6195534Sscottl * All rights reserved. 7195534Sscottl * 8195534Sscottl * This code is derived from software contributed to The NetBSD Foundation 9195534Sscottl * by Lennart Augustsson <lennart@augustsson.net>. 10195534Sscottl * 11195534Sscottl * Redistribution and use in source and binary forms, with or without 12195534Sscottl * modification, are permitted provided that the following conditions 13195534Sscottl * are met: 14195534Sscottl * 1. Redistributions of source code must retain the above copyright 15195534Sscottl * notice, this list of conditions and the following disclaimer. 16195534Sscottl * 2. Redistributions in binary form must reproduce the above copyright 17195534Sscottl * notice, this list of conditions and the following disclaimer in the 18195534Sscottl * documentation and/or other materials provided with the distribution. 19195534Sscottl * 3. All advertising materials mentioning features or use of this software 20195534Sscottl * must display the following acknowledgement: 21195534Sscottl * This product includes software developed by the NetBSD 22195534Sscottl * Foundation, Inc. and its contributors. 23195534Sscottl * 4. Neither the name of The NetBSD Foundation nor the names of its 24195534Sscottl * contributors may be used to endorse or promote products derived 25195534Sscottl * from this software without specific prior written permission. 26195534Sscottl * 27195534Sscottl * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28195534Sscottl * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29195534Sscottl * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30195534Sscottl * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31195534Sscottl * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32195534Sscottl * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33195534Sscottl * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34195534Sscottl * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35195534Sscottl * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36195534Sscottl * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37195534Sscottl * POSSIBILITY OF SUCH DAMAGE. 38195534Sscottl */ 39195534Sscottl 40195534Sscottl#include <stdio.h> 41195534Sscottl#include <stdlib.h> 42195534Sscottl#include <string.h> 43195534Sscottl#include <ctype.h> 44195534Sscottl#include <err.h> 45195534Sscottl#include <fcntl.h> 46195534Sscottl#include <limits.h> 47195534Sscottl#include <unistd.h> 48195534Sscottl#include <sys/types.h> 49195534Sscottl#include <sys/ioctl.h> 50195534Sscottl#include <dev/usb/usb.h> 51195534Sscottl#include <dev/usb/usbhid.h> 52195534Sscottl#include <usbhid.h> 53195534Sscottl#include <syslog.h> 54195534Sscottl#include <signal.h> 55195534Sscottl#include <errno.h> 56195534Sscottl#include <sys/stat.h> 57195534Sscottl 58195534Sscottlstatic int verbose = 0; 59195534Sscottlstatic int isdemon = 0; 60195534Sscottlstatic int reparse = 1; 61195534Sscottlstatic char * pidfile = "/var/run/usbaction.pid"; 62195534Sscottl 63195534Sscottlstruct command { 64195534Sscottl struct command *next; 65195534Sscottl int line; 66195534Sscottl 67195534Sscottl struct hid_item item; 68195534Sscottl int value; 69195534Sscottl int lastseen; 70195534Sscottl int lastused; 71198849Smav int debounce; 72198849Smav char anyvalue; 73198849Smav char *name; 74198849Smav char *action; 75220616Smav}; 76220616Smavstruct command *commands; 77198849Smav 78198849Smav#define SIZE 4000 79198849Smav 80201139Smavvoid usage(void); 81201139Smavstruct command *parse_conf(const char *, report_desc_t, int, int); 82201139Smavvoid docmd(struct command *, int, const char *, int, char **); 83201139Smavvoid freecommands(struct command *); 84201139Smav 85198849Smavstatic void 86198849Smavsighup(int sig) 87198849Smav{ 88198849Smav reparse = 1; 89198849Smav} 90198849Smav 91198849Smavint 92198849Smavmain(int argc, char **argv) 93198849Smav{ 94198849Smav const char *conf = NULL; 95198849Smav const char *dev = NULL; 96198849Smav int fd, fp, ch, sz, n, val, i; 97198849Smav int demon, ignore, dieearly; 98198849Smav report_desc_t repd; 99198849Smav char buf[100]; 100198849Smav char devnamebuf[PATH_MAX]; 101198849Smav struct command *cmd; 102198849Smav int reportid; 103200008Smav 104200008Smav demon = 1; 105198849Smav ignore = 0; 106198849Smav dieearly = 0; 107198849Smav while ((ch = getopt(argc, argv, "c:def:ip:v")) != -1) { 108198849Smav switch(ch) { 109198849Smav case 'c': 110198849Smav conf = optarg; 111235897Smav break; 112235897Smav case 'd': 113235897Smav demon ^= 1; 114235897Smav break; 115235897Smav case 'e': 116235897Smav dieearly = 1; 117235897Smav break; 118235897Smav case 'i': 119235897Smav ignore++; 120235897Smav break; 121198849Smav case 'f': 122198849Smav dev = optarg; 123198849Smav break; 124198849Smav case 'p': 125198849Smav pidfile = optarg; 126198849Smav break; 127198849Smav case 'v': 128198849Smav demon = 0; 129198849Smav verbose++; 130198849Smav break; 131198849Smav case '?': 132198849Smav default: 133198849Smav usage(); 134198849Smav } 135198849Smav } 136198849Smav argc -= optind; 137198849Smav argv += optind; 138198849Smav 139200008Smav if (conf == NULL || dev == NULL) 140198849Smav usage(); 141198849Smav 142198849Smav hid_init(NULL); 143198849Smav 144198849Smav if (dev[0] != '/') { 145198849Smav snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s", 146198849Smav isdigit(dev[0]) ? "uhid" : "", dev); 147198849Smav dev = devnamebuf; 148198849Smav } 149198849Smav 150198849Smav fd = open(dev, O_RDWR); 151198849Smav if (fd < 0) 152198849Smav err(1, "%s", dev); 153198849Smav if (ioctl(fd, USB_GET_REPORT_ID, &reportid) < 0) 154198849Smav reportid = -1; 155198849Smav repd = hid_get_report_desc(fd); 156198849Smav if (repd == NULL) 157198849Smav err(1, "hid_get_report_desc() failed"); 158198849Smav 159198849Smav commands = parse_conf(conf, repd, reportid, ignore); 160198849Smav 161203421Smav sz = hid_report_size(repd, hid_input, reportid); 162203421Smav 163203421Smav if (verbose) 164220616Smav printf("report size %d\n", sz); 165220616Smav if (sz > sizeof buf) 166198849Smav errx(1, "report too large"); 167198849Smav 168198849Smav (void)signal(SIGHUP, sighup); 169198849Smav 170198849Smav if (demon) { 171198849Smav fp = open(pidfile, O_WRONLY|O_CREAT, S_IRUSR|S_IRGRP|S_IROTH); 172198849Smav if (fp >= 0) { 173198849Smav sz=snprintf(buf,100, "%d\n", getpid()); 174238363Sbrueffer write(fp, buf, sz); 175238363Sbrueffer close(fp); 176198849Smav } else 177198849Smav err(1, "%s", pidfile); 178198849Smav if (daemon(0, 0) < 0) 179198849Smav err(1, "daemon()"); 180198849Smav isdemon = 1; 181198849Smav } 182198849Smav 183198849Smav for(;;) { 184198849Smav n = read(fd, buf, sz); 185198849Smav if (verbose > 2) { 186198849Smav printf("read %d bytes:", n); 187198849Smav for (i = 0; i < n; i++) 188198849Smav printf(" %02x", buf[i]); 189198849Smav printf("\n"); 190198849Smav } 191198849Smav if (n < 0) { 192198849Smav if (verbose) 193198849Smav err(1, "read"); 194198849Smav else 195198849Smav exit(1); 196198849Smav } 197198849Smav#if 0 198198849Smav if (n != sz) { 199198849Smav err(2, "read size"); 200198849Smav } 201198849Smav#endif 202198849Smav for (cmd = commands; cmd; cmd = cmd->next) { 203198849Smav val = hid_get_data(buf, &cmd->item); 204198849Smav if (cmd->value != val && cmd->anyvalue == 0) 205198849Smav goto next; 206198849Smav if ((cmd->debounce == 0) || 207198849Smav ((cmd->debounce == 1) && ((cmd->lastseen == -1) || 208198849Smav (cmd->lastseen != val)))) { 209198849Smav docmd(cmd, val, dev, argc, argv); 210198849Smav goto next; 211198849Smav } 212198849Smav if ((cmd->debounce > 1) && 213198849Smav ((cmd->lastused == -1) || 214198849Smav (abs(cmd->lastused - val) >= cmd->debounce))) { 215198849Smav docmd(cmd, val, dev, argc, argv); 216198849Smav cmd->lastused = val; 217198849Smav goto next; 218203108Smav } 219198849Smavnext: 220198849Smav cmd->lastseen = val; 221198849Smav } 222198849Smav 223198849Smav if (dieearly) 224198849Smav exit(0); 225198849Smav 226198849Smav if (reparse) { 227198849Smav struct command *cmds = 228198849Smav parse_conf(conf, repd, reportid, ignore); 229198849Smav if (cmds) { 230198849Smav freecommands(commands); 231198849Smav commands = cmds; 232203108Smav } 233198849Smav reparse = 0; 234198849Smav } 235198849Smav } 236198849Smav 237198849Smav exit(0); 238198849Smav} 239198849Smav 240198849Smavvoid 241198849Smavusage(void) 242198849Smav{ 243203108Smav 244198849Smav fprintf(stderr, "Usage: %s [-deiv] -c config_file -f hid_dev " 245198849Smav "[-p pidfile]\n", getprogname()); 246198849Smav exit(1); 247198849Smav} 248198849Smav 249198849Smavstatic int 250198849Smavpeek(FILE *f) 251198849Smav{ 252198849Smav int c; 253198849Smav 254198849Smav c = getc(f); 255198849Smav if (c != EOF) 256198849Smav ungetc(c, f); 257198849Smav return c; 258198849Smav} 259198849Smav 260198849Smavstruct command * 261198849Smavparse_conf(const char *conf, report_desc_t repd, int reportid, int ignore) 262198849Smav{ 263198849Smav FILE *f; 264198849Smav char *p; 265198849Smav int line; 266198849Smav char buf[SIZE], name[SIZE], value[SIZE], debounce[SIZE], action[SIZE]; 267198849Smav char usage[SIZE], coll[SIZE]; 268198849Smav struct command *cmd, *cmds; 269198849Smav struct hid_data *d; 270198849Smav struct hid_item h; 271198849Smav int u, lo, hi, range; 272195534Sscottl 273195534Sscottl 274195534Sscottl f = fopen(conf, "r"); 275195534Sscottl if (f == NULL) 276195534Sscottl err(1, "%s", conf); 277195534Sscottl 278195534Sscottl cmds = NULL; 279195534Sscottl for (line = 1; ; line++) { 280195534Sscottl if (fgets(buf, sizeof buf, f) == NULL) 281200218Smav break; 282200218Smav if (buf[0] == '#' || buf[0] == '\n') 283223019Smav continue; 284200218Smav p = strchr(buf, '\n'); 285200218Smav while (p && isspace(peek(f))) { 286195534Sscottl if (fgets(p, sizeof buf - strlen(buf), f) == NULL) 287197541Smav break; 288197541Smav p = strchr(buf, '\n'); 289197541Smav } 290195534Sscottl if (p) 291195534Sscottl *p = 0; 292195534Sscottl if (sscanf(buf, "%s %s %s %[^\n]", 293195534Sscottl name, value, debounce, action) != 4) { 294195534Sscottl if (isdemon) { 295195534Sscottl syslog(LOG_WARNING, "config file `%s', line %d" 296195534Sscottl ", syntax error: %s", conf, line, buf); 297195534Sscottl freecommands(cmds); 298195534Sscottl return (NULL); 299235897Smav } else { 300235897Smav errx(1, "config file `%s', line %d," 301235897Smav ", syntax error: %s", conf, line, buf); 302235897Smav } 303235897Smav } 304235897Smav 305235897Smav cmd = malloc(sizeof *cmd); 306235897Smav if (cmd == NULL) 307235897Smav err(1, "malloc failed"); 308235897Smav cmd->next = cmds; 309235897Smav cmds = cmd; 310235897Smav cmd->line = line; 311235897Smav 312235897Smav if (strcmp(value, "*") == 0) { 313235897Smav cmd->anyvalue = 1; 314198897Smav } else { 315198897Smav cmd->anyvalue = 0; 316198897Smav if (sscanf(value, "%d", &cmd->value) != 1) { 317198897Smav if (isdemon) { 318198897Smav syslog(LOG_WARNING, 319198897Smav "config file `%s', line %d, " 320198897Smav "bad value: %s (should be * or a number)\n", 321198897Smav conf, line, value); 322198897Smav freecommands(cmds); 323198897Smav return (NULL); 324198897Smav } else { 325198897Smav errx(1, "config file `%s', line %d, " 326198897Smav "bad value: %s (should be * or a number)\n", 327198897Smav conf, line, value); 328198897Smav } 329198897Smav } 330198897Smav } 331198897Smav 332198897Smav if (sscanf(debounce, "%d", &cmd->debounce) != 1) { 333198897Smav if (isdemon) { 334198897Smav syslog(LOG_WARNING, 335198897Smav "config file `%s', line %d, " 336198897Smav "bad value: %s (should be a number >= 0)\n", 337198897Smav conf, line, debounce); 338198897Smav freecommands(cmds); 339198897Smav return (NULL); 340198897Smav } else { 341198897Smav errx(1, "config file `%s', line %d, " 342198897Smav "bad value: %s (should be a number >= 0)\n", 343198897Smav conf, line, debounce); 344198897Smav } 345198897Smav } 346195534Sscottl 347196659Smav coll[0] = 0; 348195534Sscottl for (d = hid_start_parse(repd, 1 << hid_input, reportid); 349195534Sscottl hid_get_item(d, &h); ) { 350195534Sscottl if (verbose > 2) 351195534Sscottl printf("kind=%d usage=%x\n", h.kind, h.usage); 352200008Smav if (h.flags & HIO_CONST) 353200008Smav continue; 354200008Smav switch (h.kind) { 355200008Smav case hid_input: 356200008Smav if (h.usage_minimum != 0 || 357195534Sscottl h.usage_maximum != 0) { 358195534Sscottl lo = h.usage_minimum; 359195534Sscottl hi = h.usage_maximum; 360195534Sscottl range = 1; 361195534Sscottl } else { 362238393Sbrueffer lo = h.usage; 363195534Sscottl hi = h.usage; 364195534Sscottl range = 0; 365195534Sscottl } 366195534Sscottl for (u = lo; u <= hi; u++) { 367195534Sscottl snprintf(usage, sizeof usage, "%s:%s", 368195534Sscottl hid_usage_page(HID_PAGE(u)), 369195534Sscottl hid_usage_in_page(u)); 370248695Smav if (verbose > 2) 371195534Sscottl printf("usage %s\n", usage); 372200008Smav if (!strcasecmp(usage, name)) 373200008Smav goto foundhid; 374200008Smav if (coll[0]) { 375200008Smav snprintf(usage, sizeof usage, 376200008Smav "%s.%s:%s", coll+1, 377200008Smav hid_usage_page(HID_PAGE(u)), 378200008Smav hid_usage_in_page(u)); 379201139Smav if (verbose > 2) 380201139Smav printf("usage %s\n", 381200008Smav usage); 382195534Sscottl if (!strcasecmp(usage, name)) 383195534Sscottl goto foundhid; 384195534Sscottl } 385195534Sscottl } 386195534Sscottl break; 387238393Sbrueffer case hid_collection: 388195534Sscottl snprintf(coll + strlen(coll), 389195534Sscottl sizeof coll - strlen(coll), ".%s:%s", 390195534Sscottl hid_usage_page(HID_PAGE(h.usage)), 391195534Sscottl hid_usage_in_page(h.usage)); 392195534Sscottl break; 393195534Sscottl case hid_endcollection: 394248695Smav if (coll[0]) 395195534Sscottl *strrchr(coll, '.') = 0; 396195534Sscottl break; 397195534Sscottl default: 398195534Sscottl break; 399195534Sscottl } 400195534Sscottl } 401248695Smav if (ignore) { 402195534Sscottl if (verbose) 403195534Sscottl warnx("ignore item '%s'", name); 404195534Sscottl continue; 405195534Sscottl } 406195534Sscottl if (isdemon) { 407195534Sscottl syslog(LOG_WARNING, "config file `%s', line %d, HID " 408238393Sbrueffer "item not found: `%s'\n", conf, line, name); 409195534Sscottl freecommands(cmds); 410195534Sscottl return (NULL); 411195534Sscottl } else { 412195534Sscottl errx(1, "config file `%s', line %d, HID item " 413248695Smav "not found: `%s'\n", conf, line, name); 414248695Smav } 415248695Smav 416195534Sscottl foundhid: 417195534Sscottl hid_end_parse(d); 418195534Sscottl cmd->lastseen = -1; 419195534Sscottl cmd->lastused = -1; 420195534Sscottl cmd->item = h; 421195534Sscottl cmd->name = strdup(name); 422195534Sscottl cmd->action = strdup(action); 423195534Sscottl if (range) { 424195534Sscottl if (cmd->value == 1) 425195534Sscottl cmd->value = u - lo; 426195534Sscottl else 427195534Sscottl cmd->value = -1; 428195534Sscottl } 429195534Sscottl 430198904Smav if (verbose) 431195534Sscottl printf("PARSE:%d %s, %d, '%s'\n", cmd->line, name, 432195534Sscottl cmd->value, cmd->action); 433195534Sscottl } 434195534Sscottl fclose(f); 435195534Sscottl return (cmds); 436195534Sscottl} 437198904Smav 438195534Sscottlvoid 439195534Sscottldocmd(struct command *cmd, int value, const char *hid, int argc, char **argv) 440198904Smav{ 441195534Sscottl char cmdbuf[SIZE], *p, *q; 442195534Sscottl size_t len; 443198904Smav int n, r; 444195534Sscottl 445195534Sscottl for (p = cmd->action, q = cmdbuf; *p && q < &cmdbuf[SIZE-1]; ) { 446195534Sscottl if (*p == '$') { 447195534Sscottl p++; 448195534Sscottl len = &cmdbuf[SIZE-1] - q; 449195534Sscottl if (isdigit(*p)) { 450195534Sscottl n = strtol(p, &p, 10) - 1; 451195534Sscottl if (n >= 0 && n < argc) { 452195534Sscottl strncpy(q, argv[n], len); 453195534Sscottl q += strlen(q); 454195534Sscottl } 455195534Sscottl } else if (*p == 'V') { 456195534Sscottl p++; 457195534Sscottl snprintf(q, len, "%d", value); 458195534Sscottl q += strlen(q); 459195534Sscottl } else if (*p == 'N') { 460195534Sscottl p++; 461195534Sscottl strncpy(q, cmd->name, len); 462195534Sscottl q += strlen(q); 463195534Sscottl } else if (*p == 'H') { 464195534Sscottl p++; 465195534Sscottl strncpy(q, hid, len); 466195534Sscottl q += strlen(q); 467195534Sscottl } else if (*p) { 468195534Sscottl *q++ = *p++; 469195534Sscottl } 470195534Sscottl } else { 471195534Sscottl *q++ = *p++; 472195534Sscottl } 473195534Sscottl } 474195534Sscottl *q = 0; 475195534Sscottl 476195534Sscottl if (verbose) 477195534Sscottl printf("system '%s'\n", cmdbuf); 478195534Sscottl r = system(cmdbuf); 479195534Sscottl if (verbose > 1 && r) 480195534Sscottl printf("return code = 0x%x\n", r); 481195534Sscottl} 482195534Sscottl 483195534Sscottlvoid 484195534Sscottlfreecommands(struct command *cmd) 485195534Sscottl{ 486195534Sscottl struct command *next; 487195534Sscottl 488195534Sscottl while (cmd) { 489195534Sscottl next = cmd->next; 490195534Sscottl free(cmd); 491195534Sscottl cmd = next; 492195534Sscottl } 493195534Sscottl} 494195534Sscottl