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