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