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>
68 #include <linux/input.h>
69 #include <linux/hiddev.h>
71 typedef struct InputEventState InputEventState;
72 #include "input-codes.h" /* not really a header */
74 typedef struct Device Device;
75 typedef struct KindInfo KindInfo;
76 typedef struct ModeInfo ModeInfo;
77 typedef const ModeInfo *Mode;
78 typedef const KindInfo *Kind;
80 typedef enum { RA_Show, RA_IntermediateNode, RA_Suppress } RedactionAction;
82 typedef struct RedactionNode {
84 void *children; /* tsearch tree of RedactionNodes */
89 void (*evdev_event)(Device *d, const struct input_event *ie);
90 void (*evdev_readable)(Device *d);
91 void (*evdev_synch)(Device *d, struct timeval tv);
92 void (*hiddev_event)(Device *d, const struct hiddev_usage_ref *ur);
93 int (*hiddev_xflags)(void);
94 void (*opened)(Device *d);
95 void (*redacted)(Device *d, int nstrs, const char *strs[nstrs], int value);
96 void (*died)(Device *d, int revents, int readr, int readc, int e)
97 __attribute__((noreturn));
98 void (*mainloop)(void);
102 void (*prepare)(Device*);
103 void (*readable)(Device*);
108 RedactionNode redactions; /* root; .str is 0 and irrelevant */
112 struct hiddev_field_info fi;
121 const KindInfo *kind;
130 struct input_absinfo **absinfos;
135 /*---------- globals ----------*/
137 /* command line options */
140 static int grab, stdinmonitor;
141 static const char *expect_sysfs, *label;
142 static DeviceOptions dopts;
145 static Device *devices;
147 /*---------- generally useful ----------*/
149 static void pr_hex(unsigned long value) { printf("%#lx",value); }
151 #define PR_TABLE_STR(tab, val) (pr_table_str(iesis_##tab, ien_##tab, (val)))
152 static void pr_table_str(const InputEventStringInfo *strings, int nstrings,
153 unsigned long value) {
154 const InputEventStringInfo *string;
155 if (value > nstrings) { pr_hex(value); return; }
156 string= &strings[value];
157 if (!string->prefix) { pr_hex(value); return; }
158 printf("%s_%s", string->prefix, string->main);
161 static void pr_time(struct timeval tv) {
162 printf("%ju.%06d", (uintmax_t)tv.tv_sec, (int)tv.tv_usec);
165 static void mread(Device *d, void *buf, size_t l) {
169 for (p=buf, remain=l;
172 r= read(d->fd, p, remain);
173 if (r<=0) { mode->died(d, POLLIN, r, -1, errno); abort(); }
178 static void dump_vpv(unsigned vendor, unsigned product, unsigned version) {
179 printf(" vendor %#x product %#x version %#x",
180 vendor, product, version);
182 #define DUMP_VPV(t) (dump_vpv((t).vendor, (t).product, (t).version))
184 /*---------- evdev kind ----------*/
186 static const struct input_absinfo *evdev_getabsinfo(Device *d, uint16_t code) {
187 struct input_absinfo **aip, *ai;
190 if (code >= 0x80) return 0; /* absurd ioctl scheme! */
192 if (code >= d->forkind.evdev.nabsinfos) {
193 int newsize= code+10;
194 d->forkind.evdev.absinfos= mrealloc(d->forkind.evdev.absinfos,
195 newsize * sizeof(*d->forkind.evdev.absinfos));
196 for (i=d->forkind.evdev.nabsinfos; i<newsize; i++)
197 d->forkind.evdev.absinfos[i]= 0;
199 aip= &d->forkind.evdev.absinfos[code];
203 *aip= ai= mmalloc(sizeof(*ai));
204 r= ioctl(d->fd, EVIOCGABS(code), ai);
205 ai->value= r ? errno : 0;
209 static void evdev_dump(Device *d, const struct input_event *ie) {
210 const InputEventTypeInfo *t;
211 const struct input_absinfo *ai;
216 PR_TABLE_STR(ev, ie->type);
219 if (ie->type >= IETIN) {
223 if (!t->strings) t= 0;
225 if (t) pr_table_str(t->strings, t->nstrings, ie->code);
226 else pr_hex(ie->code);
231 ai= evdev_getabsinfo(d, ie->code);
234 } else if (ai->value) {
235 printf("?%ld [%s]", (long)ie->value, strerror(ai->value));
237 } else if ((ai->minimum==-1 || ai->minimum== 0) &&
238 (ai->maximum== 0 || ai->maximum==+1)) {
241 (double)(ie->value - ai->minimum)/(ai->maximum - ai->minimum));
246 printf("%ld",(long)ie->value);
249 printf("%lx",(unsigned long)ie->value);
255 #define MAXTABSTRH 20
256 static void tab_redact(const InputEventStringInfo *strings, int nstrings,
257 unsigned long value, char hexbuf[MAXTABSTRH],
259 const InputEventStringInfo *string;
260 if (value < nstrings &&
261 (string= &strings[value],
263 sb[0]= string->prefix;
266 snprintf(hexbuf,sizeof(hexbuf),"%lx",value);
272 static void evdev_redact(Device *d, const struct input_event *ie) {
273 const InputEventTypeInfo *t;
274 char sbh_type[MAXTABSTRH];
275 char sbh_code[MAXTABSTRH];
278 tab_redact(iesis_ev, ien_ev, ie->type, sbh_type, &strs[0]);
280 if (ie->type >= IETIN) {
284 if (!t->strings) t= 0;
286 tab_redact(t ? t->strings : 0,
288 ie->code, sbh_code, &strs[2]);
290 mode->redacted(d, 4,strs, ie->value);
293 static void evdev_readable(Device *d) {
294 struct input_event ie;
296 if (mode->evdev_readable) mode->evdev_readable(d);
299 mread(d, &ie, sizeof(ie));
300 if (ie.type == EV_SYN) {
301 if (mode->evdev_synch) mode->evdev_synch(d, ie.time);
305 mode->evdev_event(d, &ie);
309 static void evdev_readable_dump(Device *d) {
310 printf("report-from device %s\n",d->path);
312 static void evdev_synch_dump(Device *d, struct timeval tv) {
318 static void check_expect_sysfs(int fd, const char *path, const char *efn) {
320 unsigned long maj, min;
325 r= fstat(fd, &stab); if (r) diee("%s: fstat failed", path);
326 if (!S_ISCHR(stab.st_mode)) die("%s: not a character device", path);
328 sysfs= fopen(efn,"r");
329 if (!sysfs) diee("%s: failed to open sysfs %s", path, efn);
330 if (!fgets(buf,sizeof(buf)-1,sysfs)) {
331 if (ferror(sysfs)) diee("%s: failed to read sysfs %s", path, efn);
332 assert(feof(sysfs)); die("%s: eof on sysfs %s", path, efn);
334 buf[sizeof(buf)-1]= 0;
335 errno=0; maj=strtoul(buf,&ep,0);
336 if (errno || *ep!=':') die("%s: bad major number or no colon in sysfs"
337 " dev file %s", path, efn);
338 errno=0; min=strtoul(ep+1,&ep,0);
339 if (errno || *ep!='\n') die("%s: bad minor number or no colon in sysfs"
340 " dev file %s", path, efn);
342 if (maj != major(stab.st_rdev) || min != minor(stab.st_rdev))
343 die("%s: is %lu:%lu, expected %lu:%lu", path,
344 (unsigned long)major(stab.st_rdev),
345 (unsigned long)minor(stab.st_rdev),
348 if (fclose(sysfs)) die("%s: failed to close sysfs %s", path, efn);
351 static void evdev_prepare(Device *d) {
356 check_expect_sysfs(d->fd, d->path, expect_sysfs);
360 r= ioctl(d->fd, EVIOCGID, &iid);
361 if (r) diee("%s: failed to get id",d->path);
365 PR_TABLE_STR(bus, iid.bustype);
371 r= ioctl(d->fd, EVIOCGRAB, 1);
372 if (r) diee("%s: failed to grab",d->path);
375 d->forkind.evdev.nabsinfos= 0;
376 d->forkind.evdev.absinfos= 0;
379 static const KindInfo kind_evdev= { evdev_prepare, evdev_readable };
381 /*---------- hiddev kind ----------*/
383 static int hiddev_f_compar(const void *a_v, const void *b_v) {
384 const HiddevField *a=a_v, *b=b_v;
385 /* these are all unsigned 0..0xffff so the differences fit nicely */
386 return (int)a->fi.report_type - (int)b->fi.report_type ? :
387 (int)a->fi.report_id - (int)b->fi.report_id ? :
388 (int)a->fi.field_index - (int)b->fi.field_index;
391 static HiddevField *hiddev_get_f(Device *d,
392 const struct hiddev_usage_ref *ur) {
397 if (ur->field_index == HID_FIELD_INDEX_NONE)
400 if (!d->forkind.hiddev.fbuf) {
401 d->forkind.hiddev.fbuf= mmalloc(sizeof(*d->forkind.hiddev.fbuf));
404 memset(&d->forkind.hiddev.fbuf->fi,0x55,sizeof(d->forkind.hiddev.fbuf->fi));
405 d->forkind.hiddev.fbuf->fi.report_type= ur->report_type;
406 d->forkind.hiddev.fbuf->fi.report_id= ur->report_id;
407 d->forkind.hiddev.fbuf->fi.field_index= ur->field_index;
408 fvp= tsearch(d->forkind.hiddev.fbuf,
409 &d->forkind.hiddev.froot,
411 if (!fvp) diee("tsearch hiddev type/id/index");
414 if (f == d->forkind.hiddev.fbuf) {
415 d->forkind.hiddev.fbuf= 0;
417 r= ioctl(d->fd, HIDIOCGFIELDINFO, &f->fi);
418 if (r) diee("%s: ioctl HIDIOCGFIELDINFO %#x %#x %#x", d->path,
419 f->fi.report_type, f->fi.report_id, f->fi.field_index);
421 f->maxusage= f->fi.maxusage;
423 size_t sz= sizeof(*f->lastvalues) * f->maxusage;
424 f->lastvalues= mmalloc(sz);
425 memset(f->lastvalues,0,sz);
427 if (ur->usage_index >= f->maxusage) {
428 uint32_t newmax= ur->usage_index + 1;
429 fprintf(stderr,"%s: usage_index %"PRIu32" >= maxusage %"PRIu32"\n",
430 d->path, ur->usage_index, f->maxusage);
431 f->lastvalues= mrealloc(f->lastvalues, sizeof(*f->lastvalues) * newmax);
432 memset(f->lastvalues + f->maxusage, 0,
433 sizeof(*f->lastvalues) * (newmax - f->maxusage));
440 static int hiddev_elide(Device *d, const struct hiddev_usage_ref *ur,
448 unsigned relevant_flags= f->fi.flags &
449 (HID_FIELD_RELATIVE|HID_FIELD_BUFFERED_BYTE);
451 if (relevant_flags == HID_FIELD_RELATIVE &&
455 if (relevant_flags == 0) {
456 if (ur->value == f->lastvalues[ur->usage_index])
458 f->lastvalues[ur->usage_index]= ur->value;
463 static void hiddev_dump(Device *d, const struct hiddev_usage_ref *ur) {
466 f= hiddev_get_f(d, ur);
468 if (hiddev_elide(d, ur, f))
471 printf("hiddev type %04x id %04x", ur->report_type, ur->report_id);
472 if (ur->field_index == HID_FIELD_INDEX_NONE) {
473 printf(" field index NONE\n");
477 printf(" field index %04x"
478 " usage index %04x code %04x value %08lx ",
480 ur->usage_index, ur->usage_code,
481 (unsigned long)ur->value);
483 printf(" maxusage %04x flags %04x"
484 " physical %04x %08lx..%08lx"
485 " logical %04x %08lx..%08lx"
487 " unit %04x exponent %04x",
488 f->fi.maxusage, f->fi.flags,
489 f->fi.physical, (unsigned long)f->fi.physical_minimum,
490 (unsigned long)f->fi.physical_maximum,
491 f->fi.logical, (unsigned long)f->fi.logical_minimum,
492 (unsigned long)f->fi.logical_maximum,
494 f->fi.unit, f->fi.unit_exponent);
499 static void hiddev_redact_pageusage(unsigned long code,
501 const char *strs[2]) {
502 assert(code <= 0xffffffffUL);
503 sprintf(sb_buf[0], "%#04lx", code >> 16);
504 sprintf(sb_buf[1], "%02lx", code & 0xffff);
509 static void hiddev_redact(Device *d, const struct hiddev_usage_ref *ur) {
511 char sb_app[2][7], sb_usage[2][7];
514 if (ur->field_index == HID_FIELD_INDEX_NONE)
517 f= hiddev_get_f(d, ur);
518 if (hiddev_elide(d, ur, f))
521 hiddev_redact_pageusage(f->fi.application, sb_app, &strs[0]);
522 hiddev_redact_pageusage(ur->usage_code, sb_usage, &strs[2]);
524 mode->redacted(d, 4,strs, ur->value);
527 static void hiddev_readable(Device *d) {
528 struct hiddev_usage_ref ur;
529 mread(d, &ur, sizeof(ur));
530 mode->hiddev_event(d, &ur);
533 static void hiddev_prepare(Device *d) {
535 struct hiddev_devinfo di;
537 flags= HIDDEV_FLAG_UREF;
538 if (mode->hiddev_xflags) flags |= mode->hiddev_xflags();
539 r= ioctl(d->fd, HIDIOCSFLAG, &flags);
540 if (r) diee("hiddev %s: ioctl HIDIOCSFLAG", d->path);
542 r= ioctl(d->fd, HIDIOCGDEVINFO, &di);
543 if (r) diee("hiddev %s: ioctl HIDIOCGDEVINFO", d->path);
547 PR_TABLE_STR(bus, di.bustype);
548 printf(" bus %d dev %d if %d", di.busnum, di.devnum, di.ifnum);
550 printf(" napplications %d\n", di.num_applications);
552 d->forkind.hiddev.froot= 0;
553 d->forkind.hiddev.fbuf= 0;
556 static int hiddev_xflags_dump(void) { return HIDDEV_FLAG_REPORT; }
558 static const KindInfo kind_hiddev= { hiddev_prepare, hiddev_readable };
560 /*---------- mode dump ----------*/
562 static void dump_died(Device *d, int revents, int readr, int readc, int e)
563 __attribute__((noreturn));
564 static void dump_died(Device *d, int revents, int readr, int readc, int e) {
565 printf("device-terminated %s %#x ", d->path, revents);
566 if (readr<0) printf("err %s", strerror(e));
567 else if (readr==0) printf("eof");
568 else printf("%#x",readc);
574 static void dump_opened(Device *d) {
575 printf("device %s", d->path);
578 static void mainloop(void) {
579 struct pollfd *polls;
582 npolls= ndevices + stdinmonitor;
583 polls= mmalloc(sizeof(*polls)*npolls);
584 for (i=0; i<ndevices; i++) {
585 polls[i].fd= devices[i].fd;
586 polls[i].events= POLLIN;
589 polls[ndevices].fd= 0;
590 polls[ndevices].events= POLLIN;
594 for (i=0; i<npolls; i++)
597 r= poll(polls,npolls,-1);
599 if (errno==EINTR) continue;
605 if (polls[ndevices].revents) {
606 printf("quitting-stdin-polled %#x\n", polls[ndevices].revents);
611 for (i=0; i<ndevices; i++) {
612 if (polls[i].revents & ~POLLIN) {
614 r= oop_fd_nonblock(polls[i].fd, 1);
615 if (r) diee("nonblock %s during poll unepxected %#x",
616 devices[i].path, polls[i].revents);
617 r= read(polls[i].fd, &dummy,1);
618 mode->died(&devices[i], polls[i].revents, r, dummy, errno);
621 if (polls[i].revents) {
622 Device *d= &devices[i];
623 d->kind->readable(d);
630 static const ModeInfo mode_dump= {
631 evdev_dump, evdev_readable_dump, evdev_synch_dump,
632 hiddev_dump, hiddev_xflags_dump,
633 dump_opened, 0, dump_died, mainloop
636 /*---------- mode redact ----------*/
638 static int redaction_node_compar(const void *a_v, const void *b_v) {
639 const RedactionNode *a=a_v, *b=b_v;
640 return strcmp(a->str, b->str);
643 static RedactionNode *redact_find_node(int nstrs, const char *strs[nstrs]) {
644 RedactionNode key, *lastfound, *search;
647 lastfound= search= &dopts.redactions;
649 if (!nstrs) return lastfound;
651 rn_vp= tfind(&key, &search->children, redaction_node_compar);
652 if (!rn_vp) return lastfound;
654 if (search->act != RA_IntermediateNode)
661 static void redact_redacted(Device *d, int nstrs, const char *strs[nstrs],
666 rn= redact_find_node(nstrs, strs);
669 case RA_Suppress: return;
673 printf("%s %d", d->label, value);
674 for (i=0; i<nstrs; i++)
675 printf(" %s", strs[i]);
679 static void redact_opened(Device *d) {
680 printf("%s opened", d->label);
683 static const ModeInfo mode_redact= {
686 redact_opened, redact_redacted, dump_died, mainloop
689 static void redaction(const char ***argv) {
690 RedactionNode *path, *newnode;
694 path= &dopts.redactions;
697 if (!arg) badusage("missing str or action for --redaction");
698 if (arg[0]=='-') break;
699 newnode= mmalloc(sizeof(*newnode));
701 searched= tsearch(newnode, &path->children, redaction_node_compar);
702 if (!searched) diee("allocate new redaction node");
703 if (*searched == newnode) {
704 newnode->children= 0;
705 newnode->act= RA_IntermediateNode;
712 if (!strcmp(arg,"--suppress")) {
713 path->act= RA_Suppress;
714 } else if (!strcmp(arg,"--show")) {
717 badusage("unknown or missing action for --redaction");
721 /*---------- main program ----------*/
723 static void getdevice(const char *path) {
726 devices= mrealloc(devices, sizeof(*devices)*ndevices);
727 d= &devices[ndevices-1];
729 d->path= mstrdup(path);
730 d->fd= open(path, O_RDONLY); if (d->fd<0) diee("%s: failed to open",path);
744 int main(int argc, const char **argv) {
751 while ((arg= *++argv)) {
755 else if (!strcmp(arg,"--expect-sysfs")) {
756 if (!(expect_sysfs= *++argv)) badusage("missing arg for --expect-sysfs");
758 else if (!strcmp(arg,"--label")) {
759 if (!(label= *++argv)) badusage("missing arg for --expect-sysfs");
761 else if (!strcmp(arg,"--redaction")) {
764 else if (!strcmp(arg,"--new-redactions")) {
765 dopts.redactions.children= 0;
766 dopts.redactions.act= RA_Show;
768 else if (!strcmp(arg,"--dump-raw")) { mode= &mode_dump; }
769 else if (!strcmp(arg,"--redact")) { mode= &mode_redact; }
770 else if (!strcmp(arg,"--evdev")) { kind= &kind_evdev; }
771 else if (!strcmp(arg,"--hiddev")) { kind= &kind_hiddev; }
772 else if (!strcmp(arg,"--grab")) { grab= 1; }
773 else if (!strcmp(arg,"--no-grab")) { grab= 0; }
774 else if (!strcmp(arg,"--show-unchanged")) { dopts.elide= 0; }
775 else if (!strcmp(arg,"--elide-unchanged")) { dopts.elide= 1; }
776 else if (!strcmp(arg,"--stdin-monitor")) { stdinmonitor= 1; }
777 else badusage("unknown option");
783 const char *progname= "evdev-manip";
784 void die_hook(void) { }
785 void die_vprintf_hook(const char *fmt, va_list al) { }