2 * evdev-manip [<options> <device> ...]
6 * --redact print redacted version
7 * output is <path|label> <value> <str> <str>...
8 * where <str>... identifies the button, axis, etc.
11 * --stdin-monitor quit if stdin becomes readable
13 * redaction mode options:
15 * --redaction <str>... --suppress do not print these
16 * --redaction <str>... --show print these normally
18 * Longest matching <str>... applies. Later identical
19 * <str>... paths override earlier ones.
21 * --new-redactions resets everything to default (which is --show)
23 * Every device specified after --new-redactions gets the new
24 * set of redactions, which includes even redactions specified
25 * _after_ the device but _before_ the next --new-redactions.
26 * It is not possible to selectively edit the redactions list;
27 * devices which need different redaction lists need them
30 * per-device options (applies only to next device):
31 * --label LABEL use LABEL instead of path in redacted output
33 * per-device options (apply to all subsequent):
34 * --evdev subsequent devices are evdev
35 * --hiddev subsequent devices are hiddev
37 * per-evdev options (apply to all subsequent):
38 * --[no-]grab --nograb is default
39 * --expect-sysfs /sys/class/input/inputX/eventY/dev
40 * ^^^^^^^^^^^^^^^^^^^^
41 * this part is in /proc/bus/usb/devices
42 * and can thus be specified by caller
45 * evdev <str>... are type and code, each split up into
46 * prefix and tail, eg EV_KEY KEY_EQUAL becomes EV KEY KEY EQUAL.
47 * If the type or code is not found in our number-to-string
48 * tables, the respective two <strs> are 0x and the value in hex.
50 * per-hiddev options (apply to all subsequent):
51 * --elide|show-unchanged --elide-unchanged is default
53 * hiddev <str>... are application and field. In each case, page
54 * and usage, both in hex, both at least two digits with 0x in
55 * from of page only. This matches what is shown in the USB HID
56 * usage tables specification which can be found at
57 * http://www.usb.org/developers/hidpage/
64 #include <sys/fcntl.h>
67 #include <linux/input.h>
68 #include <linux/hiddev.h>
70 typedef struct InputEventState InputEventState;
71 #include "input-codes.h" /* not really a header */
73 typedef struct Device Device;
74 typedef struct KindInfo KindInfo;
75 typedef struct ModeInfo ModeInfo;
76 typedef const ModeInfo *Mode;
77 typedef const KindInfo *Kind;
79 typedef enum { RA_Show, RA_IntermediateNode, RA_Suppress } RedactionAction;
81 typedef struct RedactionNode {
83 void *children; /* tsearch tree of RedactionNodes */
88 void (*evdev_event)(Device *d, const struct input_event *ie);
89 void (*evdev_readable)(Device *d);
90 void (*evdev_synch)(Device *d, struct timeval tv);
91 void (*hiddev_event)(Device *d, const struct hiddev_usage_ref *ur);
92 int (*hiddev_xflags)(void);
93 void (*opened)(Device *d);
94 void (*redacted)(Device *d, int nstrs, const char *strs[nstrs], int value);
95 void (*died)(Device *d, int revents, int readr, int readc, int e)
96 __attribute__((noreturn));
97 void (*mainloop)(void);
101 void (*prepare)(Device*);
102 void (*readable)(Device*);
107 RedactionNode redactions; /* root; .str is 0 and irrelevant */
111 struct hiddev_field_info fi;
119 const KindInfo *kind;
129 /*---------- globals ----------*/
131 /* command line options */
134 static int grab, stdinmonitor;
135 static const char *expect_sysfs, *label;
136 static DeviceOptions dopts;
139 static Device *devices;
141 /*---------- generally useful ----------*/
143 static void pr_hex(unsigned long value) { printf("%#lx",value); }
145 #define PR_TABLE_STR(tab, val) (pr_table_str(iesis_##tab, ien_##tab, (val)))
146 static void pr_table_str(const InputEventStringInfo *strings, int nstrings,
147 unsigned long value) {
148 const InputEventStringInfo *string;
149 if (value > nstrings) { pr_hex(value); return; }
150 string= &strings[value];
151 if (!string->prefix) { pr_hex(value); return; }
152 printf("%s_%s", string->prefix, string->main);
155 static void pr_time(struct timeval tv) {
156 printf("%ju.%06d", (uintmax_t)tv.tv_sec, (int)tv.tv_usec);
159 static void mread(Device *d, void *buf, size_t l) {
163 for (p=buf, remain=l;
166 r= read(d->fd, p, remain);
167 if (r<=0) { mode->died(d, POLLIN, r, -1, errno); abort(); }
172 static void dump_vpv(unsigned vendor, unsigned product, unsigned version) {
173 printf(" vendor %#x product %#x version %#x",
174 vendor, product, version);
176 #define DUMP_VPV(t) (dump_vpv((t).vendor, (t).product, (t).version))
178 /*---------- evdev kind ----------*/
180 static void evdev_dump(Device *d, const struct input_event *ie) {
181 const InputEventTypeInfo *t;
187 PR_TABLE_STR(ev, ie->type);
190 if (ie->type >= IETIN) {
194 if (!t->strings) t= 0;
196 if (t) pr_table_str(t->strings, t->nstrings, ie->code);
197 else pr_hex(ie->code);
203 printf("%ld",(long)ie->value);
206 printf("%lx",(unsigned long)ie->value);
212 #define MAXTABSTRH 20
213 static void tab_redact(const InputEventStringInfo *strings, int nstrings,
214 unsigned long value, char hexbuf[MAXTABSTRH],
216 const InputEventStringInfo *string;
217 if (value < nstrings &&
218 (string= &strings[value],
220 sb[0]= string->prefix;
223 snprintf(hexbuf,sizeof(hexbuf),"%lx",value);
229 static void evdev_redact(Device *d, const struct input_event *ie) {
230 const InputEventTypeInfo *t;
231 char sbh_type[MAXTABSTRH];
232 char sbh_code[MAXTABSTRH];
235 tab_redact(iesis_ev, ien_ev, ie->type, sbh_type, &strs[0]);
237 if (ie->type >= IETIN) {
241 if (!t->strings) t= 0;
243 tab_redact(t ? t->strings : 0,
245 ie->code, sbh_code, &strs[2]);
247 mode->redacted(d, 4,strs, ie->value);
250 static void evdev_readable(Device *d) {
251 struct input_event ie;
253 if (mode->evdev_readable) mode->evdev_readable(d);
256 mread(d, &ie, sizeof(ie));
257 if (ie.type == EV_SYN) {
258 if (mode->evdev_synch) mode->evdev_synch(d, ie.time);
262 mode->evdev_event(d, &ie);
266 static void evdev_readable_dump(Device *d) {
267 printf("report-from device %s\n",d->path);
269 static void evdev_synch_dump(Device *d, struct timeval tv) {
275 static void check_expect_sysfs(int fd, const char *path, const char *efn) {
277 unsigned long maj, min;
282 r= fstat(fd, &stab); if (r) diee("%s: fstat failed", path);
283 if (!S_ISCHR(stab.st_mode)) die("%s: not a character device", path);
285 sysfs= fopen(efn,"r");
286 if (!sysfs) diee("%s: failed to open sysfs %s", path, efn);
287 if (!fgets(buf,sizeof(buf)-1,sysfs)) {
288 if (ferror(sysfs)) diee("%s: failed to read sysfs %s", path, efn);
289 assert(feof(sysfs)); die("%s: eof on sysfs %s", path, efn);
291 buf[sizeof(buf)-1]= 0;
292 errno=0; maj=strtoul(buf,&ep,0);
293 if (errno || *ep!=':') die("%s: bad major number or no colon in sysfs"
294 " dev file %s", path, efn);
295 errno=0; min=strtoul(ep+1,&ep,0);
296 if (errno || *ep!='\n') die("%s: bad minor number or no colon in sysfs"
297 " dev file %s", path, efn);
299 if (maj != major(stab.st_rdev) || min != minor(stab.st_rdev))
300 die("%s: is %lu:%lu, expected %lu:%lu", path,
301 (unsigned long)major(stab.st_rdev),
302 (unsigned long)minor(stab.st_rdev),
305 if (fclose(sysfs)) die("%s: failed to close sysfs %s", path, efn);
308 static void evdev_prepare(Device *d) {
313 check_expect_sysfs(d->fd, d->path, expect_sysfs);
317 r= ioctl(d->fd, EVIOCGID, &iid);
318 if (r) diee("%s: failed to get id",d->path);
322 PR_TABLE_STR(bus, iid.bustype);
328 r= ioctl(d->fd, EVIOCGRAB, 1);
329 if (r) diee("%s: failed to grab",d->path);
333 static const KindInfo kind_evdev= { evdev_prepare, evdev_readable };
335 /*---------- hiddev kind ----------*/
337 static int hiddev_f_compar(const void *a_v, const void *b_v) {
338 const HiddevField *a=a_v, *b=b_v;
339 /* these are all unsigned 0..0xffff so the differences fit nicely */
340 return (int)a->fi.report_type - (int)b->fi.report_type ? :
341 (int)a->fi.report_id - (int)b->fi.report_id ? :
342 (int)a->fi.field_index - (int)b->fi.field_index;
345 static HiddevField *hiddev_get_f(Device *d,
346 const struct hiddev_usage_ref *ur) {
351 if (ur->field_index == HID_FIELD_INDEX_NONE)
354 if (!d->forkind.hiddev.fbuf) {
355 d->forkind.hiddev.fbuf= mmalloc(sizeof(*d->forkind.hiddev.fbuf));
358 memset(&d->forkind.hiddev.fbuf->fi,0x55,sizeof(d->forkind.hiddev.fbuf->fi));
359 d->forkind.hiddev.fbuf->fi.report_type= ur->report_type;
360 d->forkind.hiddev.fbuf->fi.report_id= ur->report_id;
361 d->forkind.hiddev.fbuf->fi.field_index= ur->field_index;
362 fvp= tsearch(d->forkind.hiddev.fbuf,
363 &d->forkind.hiddev.froot,
365 if (!fvp) diee("tsearch hiddev type/id/index");
368 if (f == d->forkind.hiddev.fbuf) {
369 d->forkind.hiddev.fbuf= 0;
371 r= ioctl(d->fd, HIDIOCGFIELDINFO, &f->fi);
372 if (r) diee("%s: ioctl HIDIOCGFIELDINFO %#x %#x %#x", d->path,
373 f->fi.report_type, f->fi.report_id, f->fi.field_index);
375 size_t sz= sizeof(*f->lastvalues) * f->fi.maxusage;
376 f->lastvalues= mmalloc(sz);
377 memset(f->lastvalues,0,sz);
379 assert(ur->usage_index < f->fi.maxusage);
384 static int hiddev_elide(Device *d, const struct hiddev_usage_ref *ur,
392 unsigned relevant_flags= f->fi.flags &
393 (HID_FIELD_RELATIVE|HID_FIELD_BUFFERED_BYTE);
395 if (relevant_flags == HID_FIELD_RELATIVE &&
399 if (relevant_flags == 0) {
400 if (ur->value == f->lastvalues[ur->usage_index])
402 f->lastvalues[ur->usage_index]= ur->value;
407 static void hiddev_dump(Device *d, const struct hiddev_usage_ref *ur) {
410 f= hiddev_get_f(d, ur);
412 if (hiddev_elide(d, ur, f))
415 printf("hiddev type %04x id %04x", ur->report_type, ur->report_id);
416 if (ur->field_index == HID_FIELD_INDEX_NONE) {
417 printf(" field index NONE\n");
421 printf(" field index %04x"
422 " usage index %04x code %04x value %08lx ",
424 ur->usage_index, ur->usage_code,
425 (unsigned long)ur->value);
427 printf(" maxusage %04x flags %04x"
428 " physical %04x %08lx..%08lx"
429 " logical %04x %08lx..%08lx"
431 " unit %04x exponent %04x",
432 f->fi.maxusage, f->fi.flags,
433 f->fi.physical, (unsigned long)f->fi.physical_minimum,
434 (unsigned long)f->fi.physical_maximum,
435 f->fi.logical, (unsigned long)f->fi.logical_minimum,
436 (unsigned long)f->fi.logical_maximum,
438 f->fi.unit, f->fi.unit_exponent);
443 static void hiddev_redact_pageusage(unsigned long code,
445 const char *strs[2]) {
446 assert(code <= 0xffffffffUL);
447 sprintf(sb_buf[0], "%#04lx", code >> 16);
448 sprintf(sb_buf[1], "%02lx", code & 0xffff);
453 static void hiddev_redact(Device *d, const struct hiddev_usage_ref *ur) {
455 char sb_app[2][7], sb_usage[2][7];
458 if (ur->field_index == HID_FIELD_INDEX_NONE)
461 f= hiddev_get_f(d, ur);
462 if (hiddev_elide(d, ur, f))
465 hiddev_redact_pageusage(f->fi.application, sb_app, &strs[0]);
466 hiddev_redact_pageusage(ur->usage_code, sb_usage, &strs[2]);
468 mode->redacted(d, 4,strs, ur->value);
471 static void hiddev_readable(Device *d) {
472 struct hiddev_usage_ref ur;
473 mread(d, &ur, sizeof(ur));
474 mode->hiddev_event(d, &ur);
477 static void hiddev_prepare(Device *d) {
479 struct hiddev_devinfo di;
481 flags= HIDDEV_FLAG_UREF;
482 if (mode->hiddev_xflags) flags |= mode->hiddev_xflags();
483 r= ioctl(d->fd, HIDIOCSFLAG, &flags);
484 if (r) diee("hiddev %s: ioctl HIDIOCSFLAG", d->path);
486 r= ioctl(d->fd, HIDIOCGDEVINFO, &di);
487 if (r) diee("hiddev %s: ioctl HIDIOCGDEVINFO", d->path);
491 PR_TABLE_STR(bus, di.bustype);
492 printf(" bus %d dev %d if %d", di.busnum, di.devnum, di.ifnum);
494 printf(" napplications %d\n", di.num_applications);
496 d->forkind.hiddev.froot= 0;
497 d->forkind.hiddev.fbuf= 0;
500 static int hiddev_xflags_dump(void) { return HIDDEV_FLAG_REPORT; }
502 static const KindInfo kind_hiddev= { hiddev_prepare, hiddev_readable };
504 /*---------- mode dump ----------*/
506 static void dump_died(Device *d, int revents, int readr, int readc, int e)
507 __attribute__((noreturn));
508 static void dump_died(Device *d, int revents, int readr, int readc, int e) {
509 printf("device-terminated %s %#x ", d->path, revents);
510 if (readr<0) printf("err %s", strerror(e));
511 else if (readr==0) printf("eof");
512 else printf("%#x",readc);
518 static void dump_opened(Device *d) {
519 printf("device %s", d->path);
522 static void mainloop(void) {
523 struct pollfd *polls;
526 npolls= ndevices + stdinmonitor;
527 polls= mmalloc(sizeof(*polls)*npolls);
528 for (i=0; i<ndevices; i++) {
529 polls[i].fd= devices[i].fd;
530 polls[i].events= POLLIN;
533 polls[ndevices].fd= 0;
534 polls[ndevices].events= POLLIN;
538 for (i=0; i<npolls; i++)
541 r= poll(polls,npolls,-1);
543 if (errno==EINTR) continue;
549 if (polls[ndevices].revents) {
550 printf("quitting-stdin-polled %#x\n", polls[ndevices].revents);
555 for (i=0; i<ndevices; i++) {
556 if (polls[i].revents & ~POLLIN) {
558 r= oop_fd_nonblock(polls[i].fd, 1);
559 if (r) diee("nonblock %s during poll unepxected %#x",
560 devices[i].path, polls[i].revents);
561 r= read(polls[i].fd, &dummy,1);
562 mode->died(&devices[i], polls[i].revents, r, dummy, errno);
565 if (polls[i].revents) {
566 Device *d= &devices[i];
567 d->kind->readable(d);
574 static const ModeInfo mode_dump= {
575 evdev_dump, evdev_readable_dump, evdev_synch_dump,
576 hiddev_dump, hiddev_xflags_dump,
577 dump_opened, 0, dump_died, mainloop
580 /*---------- mode redact ----------*/
582 static int redaction_node_compar(const void *a_v, const void *b_v) {
583 const RedactionNode *a=a_v, *b=b_v;
584 return strcmp(a->str, b->str);
587 static RedactionNode *redact_find_node(int nstrs, const char *strs[nstrs]) {
588 RedactionNode key, *lastfound, *search;
591 lastfound= search= &dopts.redactions;
593 if (!nstrs) return lastfound;
595 rn_vp= tfind(&key, &search->children, redaction_node_compar);
596 if (!rn_vp) return lastfound;
598 if (search->act != RA_IntermediateNode)
605 static void redact_redacted(Device *d, int nstrs, const char *strs[nstrs],
610 rn= redact_find_node(nstrs, strs);
613 case RA_Suppress: return;
617 printf("%s %d", d->label, value);
618 for (i=0; i<nstrs; i++)
619 printf(" %s", strs[i]);
623 static void redact_opened(Device *d) {
624 printf("%s opened", d->label);
627 static const ModeInfo mode_redact= {
630 redact_opened, redact_redacted, dump_died, mainloop
633 static void redaction(const char ***argv) {
634 RedactionNode *path, *newnode;
638 path= &dopts.redactions;
641 if (!arg) badusage("missing str or action for --redaction");
642 if (arg[0]=='-') break;
643 newnode= mmalloc(sizeof(*newnode));
645 searched= tsearch(newnode, &path->children, redaction_node_compar);
646 if (!searched) diee("allocate new redaction node");
647 if (*searched == newnode) {
648 newnode->children= 0;
649 newnode->act= RA_IntermediateNode;
656 if (!strcmp(arg,"--suppress")) {
657 path->act= RA_Suppress;
658 } else if (!strcmp(arg,"--show")) {
661 badusage("unknown or missing action for --redaction");
665 /*---------- main program ----------*/
667 static void getdevice(const char *path) {
670 devices= mrealloc(devices, sizeof(*devices)*ndevices);
671 d= &devices[ndevices-1];
673 d->path= mstrdup(path);
674 d->fd= open(path, O_RDONLY); if (d->fd<0) diee("%s: failed to open",path);
688 int main(int argc, const char **argv) {
695 while ((arg= *++argv)) {
699 else if (!strcmp(arg,"--expect-sysfs")) {
700 if (!(expect_sysfs= *++argv)) badusage("missing arg for --expect-sysfs");
702 else if (!strcmp(arg,"--label")) {
703 if (!(label= *++argv)) badusage("missing arg for --expect-sysfs");
705 else if (!strcmp(arg,"--redaction")) {
708 else if (!strcmp(arg,"--new-redactions")) {
709 dopts.redactions.children= 0;
710 dopts.redactions.act= RA_Show;
712 else if (!strcmp(arg,"--dump-raw")) { mode= &mode_dump; }
713 else if (!strcmp(arg,"--redact")) { mode= &mode_redact; }
714 else if (!strcmp(arg,"--evdev")) { kind= &kind_evdev; }
715 else if (!strcmp(arg,"--hiddev")) { kind= &kind_hiddev; }
716 else if (!strcmp(arg,"--grab")) { grab= 1; }
717 else if (!strcmp(arg,"--no-grab")) { grab= 0; }
718 else if (!strcmp(arg,"--show-unchanged")) { dopts.elide= 0; }
719 else if (!strcmp(arg,"--elide-unchanged")) { dopts.elide= 1; }
720 else if (!strcmp(arg,"--stdin-monitor")) { stdinmonitor= 1; }
721 else badusage("unknown option");
727 const char *progname= "evdev-manip";
728 void die_hook(void) { }
729 void die_vprintf_hook(const char *fmt, va_list al) { }