1 /* $OpenBSD: usbhidaction.c,v 1.2 2002/06/11 06:16:36 jsyn Exp $ */ 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"); 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'", 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 } 440