Annotation of src/usr.bin/usbhidaction/usbhidaction.c, Revision 1.1
1.1 ! nate 1: /* $OpenBSD$ */
! 2: /* $NetBSD: usbhidaction.c,v 1.7 2002/01/18 14:38:59 augustss Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc.
! 6: * All rights reserved.
! 7: *
! 8: * This code is derived from software contributed to The NetBSD Foundation
! 9: * by Lennart Augustsson <lennart@augustsson.net>.
! 10: *
! 11: * Redistribution and use in source and binary forms, with or without
! 12: * modification, are permitted provided that the following conditions
! 13: * are met:
! 14: * 1. Redistributions of source code must retain the above copyright
! 15: * notice, this list of conditions and the following disclaimer.
! 16: * 2. Redistributions in binary form must reproduce the above copyright
! 17: * notice, this list of conditions and the following disclaimer in the
! 18: * documentation and/or other materials provided with the distribution.
! 19: * 3. All advertising materials mentioning features or use of this software
! 20: * must display the following acknowledgement:
! 21: * This product includes software developed by the NetBSD
! 22: * Foundation, Inc. and its contributors.
! 23: * 4. Neither the name of The NetBSD Foundation nor the names of its
! 24: * contributors may be used to endorse or promote products derived
! 25: * from this software without specific prior written permission.
! 26: *
! 27: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
! 28: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
! 29: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
! 30: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
! 31: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 32: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 33: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 34: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 35: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 36: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 37: * POSSIBILITY OF SUCH DAMAGE.
! 38: */
! 39:
! 40: #include <stdio.h>
! 41: #include <stdlib.h>
! 42: #include <string.h>
! 43: #include <ctype.h>
! 44: #include <err.h>
! 45: #include <fcntl.h>
! 46: #include <limits.h>
! 47: #include <unistd.h>
! 48: #include <sys/types.h>
! 49: #include <sys/ioctl.h>
! 50: #include <dev/usb/usb.h>
! 51: #include <dev/usb/usbhid.h>
! 52: #include <usbhid.h>
! 53: #include <util.h>
! 54: #include <syslog.h>
! 55: #include <signal.h>
! 56:
! 57: int verbose = 0;
! 58: int isdemon = 0;
! 59: int reparse = 1;
! 60:
! 61: struct command {
! 62: struct command *next;
! 63: int line;
! 64:
! 65: struct hid_item item;
! 66: int value;
! 67: char anyvalue;
! 68: char *name;
! 69: char *action;
! 70: };
! 71: struct command *commands;
! 72:
! 73: #define SIZE 4000
! 74:
! 75: void usage(void);
! 76: struct command *parse_conf(const char *, report_desc_t, int, int);
! 77: void docmd(struct command *, int, const char *, int, char **);
! 78: void freecommands(struct command *);
! 79:
! 80: static void
! 81: sighup(int sig)
! 82: {
! 83: reparse = 1;
! 84: }
! 85:
! 86: int
! 87: main(int argc, char **argv)
! 88: {
! 89: const char *conf = NULL;
! 90: const char *dev = NULL;
! 91: int fd, ch, sz, n, val, i;
! 92: int demon, ignore;
! 93: report_desc_t repd;
! 94: char buf[100];
! 95: char devnamebuf[PATH_MAX];
! 96: struct command *cmd;
! 97: int reportid;
! 98:
! 99: demon = 1;
! 100: ignore = 0;
! 101: while ((ch = getopt(argc, argv, "c:df:iv")) != -1) {
! 102: switch(ch) {
! 103: case 'c':
! 104: conf = optarg;
! 105: break;
! 106: case 'd':
! 107: demon ^= 1;
! 108: break;
! 109: case 'i':
! 110: ignore++;
! 111: break;
! 112: case 'f':
! 113: dev = optarg;
! 114: break;
! 115: case 'v':
! 116: demon = 0;
! 117: verbose++;
! 118: break;
! 119: case '?':
! 120: default:
! 121: usage();
! 122: }
! 123: }
! 124: argc -= optind;
! 125: argv += optind;
! 126:
! 127: if (conf == NULL || dev == NULL)
! 128: usage();
! 129:
! 130: hid_init(NULL);
! 131:
! 132: if (dev[0] != '/') {
! 133: snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s",
! 134: isdigit(dev[0]) ? "uhid" : "", dev);
! 135: dev = devnamebuf;
! 136: }
! 137:
! 138: fd = open(dev, O_RDWR);
! 139: if (fd < 0)
! 140: err(1, "%s", dev);
! 141: if (ioctl(fd, USB_GET_REPORT_ID, &reportid) < 0)
! 142: reportid = -1;
! 143: repd = hid_get_report_desc(fd);
! 144: if (repd == NULL)
! 145: err(1, "hid_get_report_desc() failed\n");
! 146:
! 147: commands = parse_conf(conf, repd, reportid, ignore);
! 148:
! 149: sz = hid_report_size(repd, hid_input, reportid);
! 150:
! 151: if (verbose)
! 152: printf("report size %d\n", sz);
! 153: if (sz > sizeof buf)
! 154: errx(1, "report too large");
! 155:
! 156: (void)signal(SIGHUP, sighup);
! 157:
! 158: if (demon) {
! 159: if (daemon(0, 0) < 0)
! 160: err(1, "daemon()");
! 161: pidfile(NULL);
! 162: isdemon = 1;
! 163: }
! 164:
! 165: for(;;) {
! 166: n = read(fd, buf, sz);
! 167: if (verbose > 2) {
! 168: printf("read %d bytes:", n);
! 169: for (i = 0; i < n; i++)
! 170: printf(" %02x", buf[i]);
! 171: printf("\n");
! 172: }
! 173: if (n < 0) {
! 174: if (verbose)
! 175: err(1, "read");
! 176: else
! 177: exit(1);
! 178: }
! 179: #if 0
! 180: if (n != sz) {
! 181: err(2, "read size");
! 182: }
! 183: #endif
! 184: for (cmd = commands; cmd; cmd = cmd->next) {
! 185: val = hid_get_data(buf, &cmd->item);
! 186: if (cmd->value == val || cmd->anyvalue)
! 187: docmd(cmd, val, dev, argc, argv);
! 188: }
! 189: if (reparse) {
! 190: struct command *cmds =
! 191: parse_conf(conf, repd, reportid, ignore);
! 192: if (cmds) {
! 193: freecommands(commands);
! 194: commands = cmds;
! 195: }
! 196: reparse = 0;
! 197: }
! 198: }
! 199:
! 200: exit(0);
! 201: }
! 202:
! 203: void
! 204: usage(void)
! 205: {
! 206: extern char *__progname;
! 207:
! 208: fprintf(stderr, "Usage: %s -c config_file [-d] -f hid_dev "
! 209: "[-i] [-v]\n", __progname);
! 210: exit(1);
! 211: }
! 212:
! 213: static int
! 214: peek(FILE *f)
! 215: {
! 216: int c;
! 217:
! 218: c = getc(f);
! 219: if (c != EOF)
! 220: ungetc(c, f);
! 221: return c;
! 222: }
! 223:
! 224: struct command *
! 225: parse_conf(const char *conf, report_desc_t repd, int reportid, int ignore)
! 226: {
! 227: FILE *f;
! 228: char *p;
! 229: int line;
! 230: char buf[SIZE], name[SIZE], value[SIZE], action[SIZE];
! 231: char usage[SIZE], coll[SIZE];
! 232: struct command *cmd, *cmds;
! 233: struct hid_data *d;
! 234: struct hid_item h;
! 235: int u, lo, hi, range;
! 236:
! 237:
! 238: f = fopen(conf, "r");
! 239: if (f == NULL)
! 240: err(1, "%s", conf);
! 241:
! 242: cmds = NULL;
! 243: for (line = 1; ; line++) {
! 244: if (fgets(buf, sizeof buf, f) == NULL)
! 245: break;
! 246: if (buf[0] == '#' || buf[0] == '\n')
! 247: continue;
! 248: p = strchr(buf, '\n');
! 249: while (p && isspace(peek(f))) {
! 250: if (fgets(p, sizeof buf - strlen(buf), f) == NULL)
! 251: break;
! 252: p = strchr(buf, '\n');
! 253: }
! 254: if (p)
! 255: *p = 0;
! 256: if (sscanf(buf, "%s %s %[^\n]", name, value, action) != 3) {
! 257: if (isdemon) {
! 258: syslog(LOG_WARNING, "config file `%s', line %d"
! 259: ", syntax error: %s", conf, line, buf);
! 260: freecommands(cmds);
! 261: return (NULL);
! 262: } else {
! 263: errx(1, "config file `%s', line %d,"
! 264: ", syntax error: %s", conf, line, buf);
! 265: }
! 266: }
! 267:
! 268: cmd = malloc(sizeof *cmd);
! 269: if (cmd == NULL)
! 270: err(1, "malloc failed");
! 271: cmd->next = cmds;
! 272: cmds = cmd;
! 273: cmd->line = line;
! 274:
! 275: if (strcmp(value, "*") == 0) {
! 276: cmd->anyvalue = 1;
! 277: } else {
! 278: cmd->anyvalue = 0;
! 279: if (sscanf(value, "%d", &cmd->value) != 1) {
! 280: if (isdemon) {
! 281: syslog(LOG_WARNING,
! 282: "config file `%s', line %d, "
! 283: "bad value: %s\n",
! 284: conf, line, value);
! 285: freecommands(cmds);
! 286: return (NULL);
! 287: } else {
! 288: errx(1, "config file `%s', line %d, "
! 289: "bad value: %s\n",
! 290: conf, line, value);
! 291: }
! 292: }
! 293: }
! 294:
! 295: coll[0] = 0;
! 296: for (d = hid_start_parse(repd, 1 << hid_input, reportid);
! 297: hid_get_item(d, &h); ) {
! 298: if (verbose > 2)
! 299: printf("kind=%d usage=%x\n", h.kind, h.usage);
! 300: if (h.flags & HIO_CONST)
! 301: continue;
! 302: switch (h.kind) {
! 303: case hid_input:
! 304: if (h.usage_minimum != 0 ||
! 305: h.usage_maximum != 0) {
! 306: lo = h.usage_minimum;
! 307: hi = h.usage_maximum;
! 308: range = 1;
! 309: } else {
! 310: lo = h.usage;
! 311: hi = h.usage;
! 312: range = 0;
! 313: }
! 314: for (u = lo; u <= hi; u++) {
! 315: snprintf(usage, sizeof usage, "%s:%s",
! 316: hid_usage_page(HID_PAGE(u)),
! 317: hid_usage_in_page(u));
! 318: if (verbose > 2)
! 319: printf("usage %s\n", usage);
! 320: if (!strcasecmp(usage, name))
! 321: goto foundhid;
! 322: if (coll[0]) {
! 323: snprintf(usage, sizeof usage,
! 324: "%s.%s:%s", coll+1,
! 325: hid_usage_page(HID_PAGE(u)),
! 326: hid_usage_in_page(u));
! 327: if (verbose > 2)
! 328: printf("usage %s\n",
! 329: usage);
! 330: if (!strcasecmp(usage, name))
! 331: goto foundhid;
! 332: }
! 333: }
! 334: break;
! 335: case hid_collection:
! 336: snprintf(coll + strlen(coll),
! 337: sizeof coll - strlen(coll), ".%s:%s",
! 338: hid_usage_page(HID_PAGE(h.usage)),
! 339: hid_usage_in_page(h.usage));
! 340: break;
! 341: case hid_endcollection:
! 342: if (coll[0])
! 343: *strrchr(coll, '.') = 0;
! 344: break;
! 345: default:
! 346: break;
! 347: }
! 348: }
! 349: if (ignore) {
! 350: if (verbose)
! 351: warnx("ignore item '%s'\n", name);
! 352: continue;
! 353: }
! 354: if (isdemon) {
! 355: syslog(LOG_WARNING, "config file `%s', line %d, HID "
! 356: "item not found: `%s'\n", conf, line, name);
! 357: freecommands(cmds);
! 358: return (NULL);
! 359: } else {
! 360: errx(1, "config file `%s', line %d, HID item "
! 361: "not found: `%s'\n", conf, line, name);
! 362: }
! 363:
! 364: foundhid:
! 365: hid_end_parse(d);
! 366: cmd->item = h;
! 367: cmd->name = strdup(name);
! 368: cmd->action = strdup(action);
! 369: if (range) {
! 370: if (cmd->value == 1)
! 371: cmd->value = u - lo;
! 372: else
! 373: cmd->value = -1;
! 374: }
! 375:
! 376: if (verbose)
! 377: printf("PARSE:%d %s, %d, '%s'\n", cmd->line, name,
! 378: cmd->value, cmd->action);
! 379: }
! 380: fclose(f);
! 381: return (cmds);
! 382: }
! 383:
! 384: void
! 385: docmd(struct command *cmd, int value, const char *hid, int argc, char **argv)
! 386: {
! 387: char cmdbuf[SIZE], *p, *q;
! 388: size_t len;
! 389: int n, r;
! 390:
! 391: for (p = cmd->action, q = cmdbuf; *p && q < &cmdbuf[SIZE-1]; ) {
! 392: if (*p == '$') {
! 393: p++;
! 394: len = &cmdbuf[SIZE-1] - q;
! 395: if (isdigit(*p)) {
! 396: n = strtol(p, &p, 10) - 1;
! 397: if (n >= 0 && n < argc) {
! 398: strncpy(q, argv[n], len);
! 399: q += strlen(q);
! 400: }
! 401: } else if (*p == 'V') {
! 402: p++;
! 403: snprintf(q, len, "%d", value);
! 404: q += strlen(q);
! 405: } else if (*p == 'N') {
! 406: p++;
! 407: strncpy(q, cmd->name, len);
! 408: q += strlen(q);
! 409: } else if (*p == 'H') {
! 410: p++;
! 411: strncpy(q, hid, len);
! 412: q += strlen(q);
! 413: } else if (*p) {
! 414: *q++ = *p++;
! 415: }
! 416: } else {
! 417: *q++ = *p++;
! 418: }
! 419: }
! 420: *q = 0;
! 421:
! 422: if (verbose)
! 423: printf("system '%s'\n", cmdbuf);
! 424: r = system(cmdbuf);
! 425: if (verbose > 1 && r)
! 426: printf("return code = 0x%x\n", r);
! 427: }
! 428:
! 429: void
! 430: freecommands(struct command *cmd)
! 431: {
! 432: struct command *next;
! 433:
! 434: while (cmd) {
! 435: next = cmd->next;
! 436: free(cmd);
! 437: cmd = next;
! 438: }
! 439: }