usbhidaction.c revision 1.14 1 /* $NetBSD: usbhidaction.c,v 1.14 2004/10/20 13:53:56 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 (at) 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 #include <sys/cdefs.h>
39
40 #ifndef lint
41 __RCSID("$NetBSD: usbhidaction.c,v 1.14 2004/10/20 13:53:56 augustss Exp $");
42 #endif
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <ctype.h>
48 #include <err.h>
49 #include <fcntl.h>
50 #include <limits.h>
51 #include <unistd.h>
52 #include <sys/types.h>
53 #include <sys/ioctl.h>
54 #include <dev/usb/usb.h>
55 #include <dev/usb/usbhid.h>
56 #include <usbhid.h>
57 #include <util.h>
58 #include <syslog.h>
59 #include <signal.h>
60
61 int verbose = 0;
62 int isdemon = 0;
63 int reparse = 0;
64
65 struct command {
66 struct command *next;
67 int line;
68
69 struct hid_item item;
70 int value;
71 char anyvalue;
72 char *name;
73 char *action;
74 };
75 struct command *commands;
76
77 #define SIZE 4000
78
79 void usage(void);
80 struct command *parse_conf(const char *, report_desc_t, int, int);
81 void docmd(struct command *, int, const char *, int, char **);
82 void freecommands(struct command *);
83
84 static void
85 sighup(int sig)
86 {
87 reparse = 1;
88 }
89
90 int
91 main(int argc, char **argv)
92 {
93 const char *conf = NULL;
94 const char *dev = NULL;
95 int fd, ch, sz, n, val, i;
96 int demon, ignore;
97 report_desc_t repd;
98 char buf[100];
99 char devnamebuf[PATH_MAX];
100 struct command *cmd;
101 int reportid;
102 const char *table = NULL;
103
104 demon = 1;
105 ignore = 0;
106 while ((ch = getopt(argc, argv, "c:df:it:v")) != -1) {
107 switch(ch) {
108 case 'c':
109 conf = optarg;
110 break;
111 case 'd':
112 demon ^= 1;
113 break;
114 case 'i':
115 ignore++;
116 break;
117 case 'f':
118 dev = optarg;
119 break;
120 case 't':
121 table = optarg;
122 break;
123 case 'v':
124 demon = 0;
125 verbose++;
126 break;
127 case '?':
128 default:
129 usage();
130 }
131 }
132 argc -= optind;
133 argv += optind;
134
135 if (conf == NULL || dev == NULL)
136 usage();
137
138 hid_init(table);
139
140 if (dev[0] != '/') {
141 snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s",
142 isdigit(dev[0]) ? "uhid" : "", dev);
143 dev = devnamebuf;
144 }
145
146 fd = open(dev, O_RDWR);
147 if (fd < 0)
148 err(1, "%s", dev);
149 if (ioctl(fd, USB_GET_REPORT_ID, &reportid) < 0)
150 reportid = -1;
151 repd = hid_get_report_desc(fd);
152 if (repd == NULL)
153 err(1, "hid_get_report_desc() failed");
154
155 commands = parse_conf(conf, repd, reportid, ignore);
156
157 sz = hid_report_size(repd, hid_input, reportid);
158
159 if (verbose)
160 printf("report size %d\n", sz);
161 if (sz > sizeof buf)
162 errx(1, "report too large");
163
164 (void)signal(SIGHUP, sighup);
165
166 if (demon) {
167 if (daemon(0, 0) < 0)
168 err(1, "daemon()");
169 pidfile(NULL);
170 isdemon = 1;
171 }
172
173 for(;;) {
174 n = read(fd, buf, sz);
175 if (verbose > 2) {
176 printf("read %d bytes:", n);
177 for (i = 0; i < n; i++)
178 printf(" %02x", buf[i]);
179 printf("\n");
180 }
181 if (n < 0) {
182 if (verbose)
183 err(1, "read");
184 else
185 exit(1);
186 }
187 #if 0
188 if (n != sz) {
189 err(2, "read size");
190 }
191 #endif
192 for (cmd = commands; cmd; cmd = cmd->next) {
193 val = hid_get_data(buf, &cmd->item);
194 if (cmd->value == val || cmd->anyvalue)
195 docmd(cmd, val, dev, argc, argv);
196 }
197 if (reparse) {
198 struct command *cmds =
199 parse_conf(conf, repd, reportid, ignore);
200 if (cmds) {
201 freecommands(commands);
202 commands = cmds;
203 }
204 reparse = 0;
205 }
206 }
207
208 exit(0);
209 }
210
211 void
212 usage(void)
213 {
214
215 fprintf(stderr, "usage: %s -c config_file [-d] -f hid_dev "
216 "[-i] [-t table] [-v]\n", getprogname());
217 exit(1);
218 }
219
220 static int
221 peek(FILE *f)
222 {
223 int c;
224
225 c = getc(f);
226 if (c != EOF)
227 ungetc(c, f);
228 return c;
229 }
230
231 struct command *
232 parse_conf(const char *conf, report_desc_t repd, int reportid, int ignore)
233 {
234 FILE *f;
235 char *p;
236 int line;
237 char buf[SIZE], name[SIZE], value[SIZE], action[SIZE];
238 char usage[SIZE], coll[SIZE];
239 struct command *cmd, *cmds;
240 struct hid_data *d;
241 struct hid_item h;
242 int u, lo, hi, range;
243
244
245 f = fopen(conf, "r");
246 if (f == NULL)
247 err(1, "%s", conf);
248
249 cmds = NULL;
250 for (line = 1; ; line++) {
251 if (fgets(buf, sizeof buf, f) == NULL)
252 break;
253 if (buf[0] == '#' || buf[0] == '\n')
254 continue;
255 p = strchr(buf, '\n');
256 while (p && isspace(peek(f))) {
257 if (fgets(p, sizeof buf - strlen(buf), f) == NULL)
258 break;
259 p = strchr(buf, '\n');
260 }
261 if (p)
262 *p = 0;
263 /* XXX SIZE == 4000 */
264 if (sscanf(buf, "%3999s %3999s %[^\n]", name, value, action) != 3) {
265 if (isdemon) {
266 syslog(LOG_WARNING, "config file `%s', line %d"
267 ", syntax error: %s", conf, line, buf);
268 freecommands(cmds);
269 return (NULL);
270 } else {
271 errx(1, "config file `%s', line %d,"
272 ", syntax error: %s", conf, line, buf);
273 }
274 }
275
276 cmd = malloc(sizeof *cmd);
277 if (cmd == NULL)
278 err(1, "malloc failed");
279 cmd->next = cmds;
280 cmds = cmd;
281 cmd->line = line;
282
283 if (strcmp(value, "*") == 0) {
284 cmd->anyvalue = 1;
285 } else {
286 cmd->anyvalue = 0;
287 if (sscanf(value, "%d", &cmd->value) != 1) {
288 if (isdemon) {
289 syslog(LOG_WARNING,
290 "config file `%s', line %d, "
291 "bad value: %s\n",
292 conf, line, value);
293 freecommands(cmds);
294 return (NULL);
295 } else {
296 errx(1, "config file `%s', line %d, "
297 "bad value: %s\n",
298 conf, line, value);
299 }
300 }
301 }
302
303 coll[0] = 0;
304 for (d = hid_start_parse(repd, 1 << hid_input, reportid);
305 hid_get_item(d, &h); ) {
306 if (verbose > 2)
307 printf("kind=%d usage=%x flags=%x\n",
308 h.kind, h.usage, h.flags);
309 if ((h.flags & HIO_CONST) && h.kind != hid_collection)
310 continue;
311 switch (h.kind) {
312 case hid_input:
313 if (h.usage_minimum != 0 ||
314 h.usage_maximum != 0) {
315 lo = h.usage_minimum;
316 hi = h.usage_maximum;
317 range = 1;
318 } else {
319 lo = h.usage;
320 hi = h.usage;
321 range = 0;
322 }
323 for (u = lo; u <= hi; u++) {
324 snprintf(usage, sizeof usage, "%s:%s",
325 hid_usage_page(HID_PAGE(u)),
326 hid_usage_in_page(u));
327 if (verbose > 2)
328 printf("usage %s\n", usage);
329 if (!strcasecmp(usage, name))
330 goto foundhid;
331 if (coll[0]) {
332 snprintf(usage, sizeof usage,
333 "%s.%s:%s", coll+1,
334 hid_usage_page(HID_PAGE(u)),
335 hid_usage_in_page(u));
336 if (verbose > 2)
337 printf("usage %s\n",
338 usage);
339 if (!strcasecmp(usage, name))
340 goto foundhid;
341 }
342 }
343 break;
344 case hid_collection:
345 snprintf(coll + strlen(coll),
346 sizeof coll - strlen(coll), ".%s:%s",
347 hid_usage_page(HID_PAGE(h.usage)),
348 hid_usage_in_page(h.usage));
349 if (verbose > 2)
350 printf("coll '%s'\n", coll);
351 break;
352 case hid_endcollection:
353 if (coll[0])
354 *strrchr(coll, '.') = 0;
355 break;
356 default:
357 break;
358 }
359 }
360 if (ignore) {
361 if (verbose)
362 warnx("ignore item '%s'", name);
363 continue;
364 }
365 if (isdemon) {
366 syslog(LOG_WARNING, "config file `%s', line %d, HID "
367 "item not found: `%s'", conf, line, name);
368 freecommands(cmds);
369 return (NULL);
370 } else {
371 errx(1, "config file `%s', line %d, HID item "
372 "not found: `%s'", conf, line, name);
373 }
374
375 foundhid:
376 hid_end_parse(d);
377 cmd->item = h;
378 cmd->name = strdup(name);
379 cmd->action = strdup(action);
380 if (range) {
381 if (cmd->value == 1)
382 cmd->value = u - lo;
383 else
384 cmd->value = -1;
385 }
386
387 if (verbose)
388 printf("PARSE:%d %s, %d, '%s'\n", cmd->line, name,
389 cmd->value, cmd->action);
390 }
391 fclose(f);
392 return (cmds);
393 }
394
395 void
396 docmd(struct command *cmd, int value, const char *hid, int argc, char **argv)
397 {
398 char cmdbuf[SIZE], *p, *q;
399 size_t len;
400 int n, r;
401
402 for (p = cmd->action, q = cmdbuf; *p && q < &cmdbuf[SIZE-1]; ) {
403 if (*p == '$') {
404 p++;
405 len = &cmdbuf[SIZE-1] - q;
406 if (isdigit(*p)) {
407 n = strtol(p, &p, 10) - 1;
408 if (n >= 0 && n < argc) {
409 strncpy(q, argv[n], len);
410 q += strlen(q);
411 }
412 } else if (*p == 'V') {
413 p++;
414 snprintf(q, len, "%d", value);
415 q += strlen(q);
416 } else if (*p == 'N') {
417 p++;
418 strncpy(q, cmd->name, len);
419 q += strlen(q);
420 } else if (*p == 'H') {
421 p++;
422 strncpy(q, hid, len);
423 q += strlen(q);
424 } else if (*p) {
425 *q++ = *p++;
426 }
427 } else {
428 *q++ = *p++;
429 }
430 }
431 *q = 0;
432
433 if (verbose)
434 printf("system '%s'\n", cmdbuf);
435 r = system(cmdbuf);
436 if (verbose > 1 && r)
437 printf("return code = 0x%x\n", r);
438 }
439
440 void
441 freecommands(struct command *cmd)
442 {
443 struct command *next;
444
445 while (cmd) {
446 next = cmd->next;
447 free(cmd);
448 cmd = next;
449 }
450 }
451