From: ian Date: Mon, 16 Jun 2008 00:16:00 +0000 (+0000) Subject: incorporated hiddev stuff and new redact mode into evdev-manip X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ijackson/git?a=commitdiff_plain;h=5f68e7218879f0bb0325dd532f8538f477710759;p=trains.git incorporated hiddev stuff and new redact mode into evdev-manip --- diff --git a/hostside/Makefile b/hostside/Makefile index 33fc791..d53e16f 100644 --- a/hostside/Makefile +++ b/hostside/Makefile @@ -6,7 +6,7 @@ AUTOINCS= auproto-pic.h layoutinfo.h retransmit-table.h \ errorcodes.h stastate.h record-y.h record-l.h \ realtime+dflags.h input-codes.h -CLIENT_TARGETS= gui-plan-bot topology-dump evdev-manip hiddev-test +CLIENT_TARGETS= gui-plan-bot topology-dump evdev-manip TARGETS= hostside-old realtime \ $(CLIENT_TARGETS) \ @@ -35,8 +35,6 @@ topology-dump: topology-dump.o utils.o $(LAYOUT_DATA).o evdev-manip: evdev-manip.o utils.o $(LIBOOP_OBJS) -hiddev-test: hiddev-test.o utils.o - gui-plan-bot: gui-plan-%: gui-plan.o utils.o parseutils.o obc.o \ ../layout/ours.dgram-%.plandata.o \ $(LAYOUT_DATA).o $(LIBOOP_OBJS) diff --git a/hostside/evdev-manip.c b/hostside/evdev-manip.c index cd3ac0f..1634dbb 100644 --- a/hostside/evdev-manip.c +++ b/hostside/evdev-manip.c @@ -1,15 +1,32 @@ /* * evdev-manip [ ...] + * * modes: - * --dump default - * per-device options: - * --[no-]grab --nograb is default + * --dump-raw default + * --redact print redacted version + * output is ... + * where ... identifies the button, axis, etc. + * + * global options: + * --stdin-monitor quit if stdin becomes readable + * + * per-device options (applies only to next device): + * --label LABEL use LABEL instead of path in redacted output + * + * per-device options (apply to all subsequent): + * --elide|show-unchanged --elide-unchanged is default + * --evdev subsequent devices are evdev + * --hiddev subsequent devices are hiddev + * hiddev are application and field + * both in hex, page<<16 | usage + * + * per-evdev options (apply to all subsequent): + * --[no-]grab --nograb is default * --expect-sysfs /sys/class/input/inputX/eventY/dev * ^^^^^^^^^^^^^^^^^^^^ * this part is in /proc/bus/usb/devices * and can thus be specified by caller - * - * --stdin-monitor quit if stdin becomes readable + * for evdev devices */ #include "common.h" @@ -17,32 +34,73 @@ #include #include #include +#include #include +#include typedef struct InputEventState InputEventState; #include "input-codes.h" /* not really a header */ -typedef struct { - char *path; - int fd; -} Device; - -typedef struct { - void (*event)(Device *d, const struct input_event *ie); +typedef struct Device Device; +typedef struct KindInfo KindInfo; +typedef struct ModeInfo ModeInfo; +typedef const ModeInfo *Mode; +typedef const KindInfo *Kind; + +struct ModeInfo { + void (*evdev_event)(Device *d, const struct input_event *ie); + void (*evdev_readable)(Device *d); + void (*evdev_synch)(Device *d, struct timeval tv); + void (*hiddev_event)(Device *d, const struct hiddev_usage_ref *ur); + int (*hiddev_xflags)(void); + void (*redacted)(Device *d, int nstrs, const char *strs[nstrs], int value); void (*died)(Device *d, int revents, int readr, int readc, int e) __attribute__((noreturn)); void (*mainloop)(void); -} ModeInfo; +}; -typedef const ModeInfo *Mode; +struct KindInfo { + void (*prepare)(Device*); + void (*readable)(Device*); +}; -static int ndevices; -static Device *devices; +typedef struct { + int elide; +} DeviceFlags; +typedef struct { + struct hiddev_field_info fi; + int *lastvalues; +} HiddevField; + +struct Device { + char *path; + const char *label; + int fd; + const KindInfo *kind; + DeviceFlags flags; + union { + struct { + void *froot; + HiddevField *fbuf; + } hiddev; + } forkind; +}; + +/*---------- globals ----------*/ + +/* command line options */ static Mode mode; +static Kind kind; static int grab, stdinmonitor; -static const char *expect_sysfs; +static const char *expect_sysfs, *label; +static DeviceFlags dflags= { 1 }; + +static int ndevices; +static Device *devices; + +/*---------- generally useful ----------*/ static void pr_hex(unsigned long value) { printf("%#lx",value); } @@ -60,10 +118,31 @@ static void pr_time(struct timeval tv) { printf("%ju.%06d", (uintmax_t)tv.tv_sec, (int)tv.tv_usec); } -static void dump_event(Device *d, const struct input_event *ie) { +static void mread(Device *d, void *buf, size_t l) { + char *p; + int r, remain; + + for (p=buf, remain=l; + remain; + p+=r, remain-=r) { + r= read(d->fd, p, remain); + if (r<=0) { mode->died(d, POLLIN, r, -1, errno); abort(); } + assert(r <= remain); + } +} + +static void dump_vpv(unsigned vendor, unsigned product, unsigned version) { + printf(" vendor %#x product %#x version %#x", + vendor, product, version); +} +#define DUMP_VPV(t) (dump_vpv((t).vendor, (t).product, (t).version)) + +/*---------- evdev kind ----------*/ + +static void evdev_dump(Device *d, const struct input_event *ie) { const InputEventTypeInfo *t; - printf("event "); + printf("evdev "); pr_time(ie->time); printf(" "); @@ -92,33 +171,292 @@ static void dump_event(Device *d, const struct input_event *ie) { printf("\n"); } -static void process_device(Device *d) { +#define MAXTABSTRH 20 +static void tab_redact(const InputEventStringInfo *strings, int nstrings, + unsigned long value, char hexbuf[MAXTABSTRH], + const char *sb[2]) { + const InputEventStringInfo *string; + if (value <= nstrings && + (string= &strings[value], + string->prefix)) { + sb[0]= string->prefix; + sb[1]= string->main; + } else { + snprintf(hexbuf,sizeof(hexbuf),"%lx",value); + sb[0]= "0x"; + sb[1]= hexbuf; + } +} + +static void evdev_redact(Device *d, const struct input_event *ie) { + const InputEventTypeInfo *t; + char sbh_type[MAXTABSTRH]; + char sbh_code[MAXTABSTRH]; + const char *strs[4]; + + tab_redact(iesis_ev, ien_ev, ie->type, sbh_type, &strs[0]); + + if (ie->type >= IETIN) { + t= 0; + } else { + t= &ietis[ie->type]; + if (!t->strings) t= 0; + } + tab_redact(t ? t->strings : 0, + t ? t->nstrings : 0, + ie->code, sbh_code, &strs[2]); + + mode->redacted(d, 4,strs, ie->value); +} + +static void evdev_readable(Device *d) { struct input_event ie; - int r, remain; - char *p; - printf("report-from device %s\n",d->path); + if (mode->evdev_readable) mode->evdev_readable(d); for (;;) { - for (p=(void*)&ie, remain=sizeof(ie); - remain; - p+=r, remain-=r) { - r= read(d->fd, &ie, remain); - if (r<=0) { mode->died(d, POLLIN, r, -1, errno); abort(); } - assert(r <= remain); - } + mread(d, &ie, sizeof(ie)); if (ie.type == EV_SYN) { - printf("synch "); - pr_time(ie.time); - printf("\n"); + if (mode->evdev_synch) mode->evdev_synch(d, ie.time); break; } - mode->event(d, &ie); + mode->evdev_event(d, &ie); + } +} + +static void evdev_readable_dump(Device *d) { + printf("report-from device %s\n",d->path); +} +static void evdev_synch_dump(Device *d, struct timeval tv) { + printf("synch "); + pr_time(tv); + printf("\n"); +} + +static void check_expect_sysfs(int fd, const char *path, const char *efn) { + char buf[50], *ep; + unsigned long maj, min; + struct stat stab; + FILE *sysfs; + int r; + + r= fstat(fd, &stab); if (r) diee("%s: fstat failed", path); + if (!S_ISCHR(stab.st_mode)) die("%s: not a character device", path); + + sysfs= fopen(efn,"r"); + if (!sysfs) diee("%s: failed to open sysfs %s", path, efn); + if (!fgets(buf,sizeof(buf)-1,sysfs)) { + if (ferror(sysfs)) diee("%s: failed to read sysfs %s", path, efn); + assert(feof(sysfs)); die("%s: eof on sysfs %s", path, efn); } + buf[sizeof(buf)-1]= 0; + errno=0; maj=strtoul(buf,&ep,0); + if (errno || *ep!=':') die("%s: bad major number or no colon in sysfs" + " dev file %s", path, efn); + errno=0; min=strtoul(ep+1,&ep,0); + if (errno || *ep!='\n') die("%s: bad minor number or no colon in sysfs" + " dev file %s", path, efn); + + if (maj != major(stab.st_rdev) || min != minor(stab.st_rdev)) + die("%s: is %lu:%lu, expected %lu:%lu", path, + (unsigned long)major(stab.st_rdev), + (unsigned long)minor(stab.st_rdev), + maj, min); + + if (fclose(sysfs)) die("%s: failed to close sysfs %s", path, efn); +} + +static void evdev_prepare(Device *d) { + int r; + struct input_id iid; + + if (expect_sysfs) { + check_expect_sysfs(d->fd, d->path, expect_sysfs); + expect_sysfs= 0; + } + + r= ioctl(d->fd, EVIOCGID, &iid); + if (r) diee("%s: failed to get id",d->path); + + printf("device %s bustype ", d->path); + PR_TABLE_STR(bus, iid.bustype); + DUMP_VPV(iid); + putchar('\n'); mflushstdout(); + + if (grab) { + r= ioctl(d->fd, EVIOCGRAB, 1); + if (r) diee("%s: failed to grab",d->path); + } +} + +static const KindInfo kind_evdev= { evdev_prepare, evdev_readable }; + +/*---------- hiddev kind ----------*/ + +static int hiddev_f_compar(const void *a_v, const void *b_v) { + const HiddevField *a=a_v, *b=b_v; + /* these are all unsigned 0..0xffff so the differences fit nicely */ + return (int)a->fi.report_type - (int)b->fi.report_type ? : + (int)a->fi.report_id - (int)b->fi.report_id ? : + (int)a->fi.field_index - (int)b->fi.field_index; +} + +static HiddevField *hiddev_get_f(Device *d, + const struct hiddev_usage_ref *ur) { + HiddevField *f; + void **fvp; + int r; + + if (ur->field_index == HID_FIELD_INDEX_NONE) + return 0; + + if (!d->forkind.hiddev.fbuf) { + d->forkind.hiddev.fbuf= mmalloc(sizeof(*d->forkind.hiddev.fbuf)); + } + + memset(&d->forkind.hiddev.fbuf->fi,0x55,sizeof(d->forkind.hiddev.fbuf->fi)); + d->forkind.hiddev.fbuf->fi.report_type= ur->report_type; + d->forkind.hiddev.fbuf->fi.report_id= ur->report_id; + d->forkind.hiddev.fbuf->fi.field_index= ur->field_index; + fvp= tsearch(d->forkind.hiddev.fbuf, + &d->forkind.hiddev.froot, + hiddev_f_compar); + if (!fvp) diee("tsearch hiddev type/id/index"); + f= *fvp; + + if (f == d->forkind.hiddev.fbuf) { + d->forkind.hiddev.fbuf= 0; + + r= ioctl(d->fd, HIDIOCGFIELDINFO, &f->fi); + if (r) diee("%s: ioctl HIDIOCGFIELDINFO %#x %#x %#x", d->path, + f->fi.report_type, f->fi.report_id, f->fi.field_index); + + size_t sz= sizeof(*f->lastvalues) * f->fi.maxusage; + f->lastvalues= mmalloc(sz); + memset(f->lastvalues,0,sz); + } + assert(ur->usage_index < f->fi.maxusage); + + return f; +} + +static int hiddev_elide(Device *d, const struct hiddev_usage_ref *ur, + HiddevField *f) { + if (!f) + return 0; + + if (!d->flags.elide) + return 0; + + unsigned relevant_flags= f->fi.flags & + (HID_FIELD_RELATIVE|HID_FIELD_BUFFERED_BYTE); + + if (relevant_flags == HID_FIELD_RELATIVE && + !ur->value) { + return 1; + } + if (relevant_flags == 0) { + if (ur->value == f->lastvalues[ur->usage_index]) + return 1; + f->lastvalues[ur->usage_index]= ur->value; + } + return 0; +} + +static void hiddev_dump(Device *d, const struct hiddev_usage_ref *ur) { + HiddevField *f; + + f= hiddev_get_f(d, ur); + + if (hiddev_elide(d, ur, f)) + return; + + printf("hiddev type %04x id %04x", ur->report_type, ur->report_id); + if (ur->field_index == HID_FIELD_INDEX_NONE) { + printf(" field index NONE\n"); + return; + } + + printf(" field index %04x" + " usage index %04x code %04x value %08lx ", + ur->field_index, + ur->usage_index, ur->usage_code, + (unsigned long)ur->value); + + printf(" maxusage %04x flags %04x" + " physical %04x %08lx..%08lx" + " logical %04x %08lx..%08lx" + " application %04x" + " unit %04x exponent %04x", + f->fi.maxusage, f->fi.flags, + f->fi.physical, (unsigned long)f->fi.physical_minimum, + (unsigned long)f->fi.physical_maximum, + f->fi.logical, (unsigned long)f->fi.logical_minimum, + (unsigned long)f->fi.logical_maximum, + f->fi.application, + f->fi.unit, f->fi.unit_exponent); + + putchar('\n'); +} + +static void hiddev_redact(Device *d, const struct hiddev_usage_ref *ur) { + HiddevField *f; + char sb_app[9], sb_usage[9]; + const char *strs[2]; + + if (ur->field_index == HID_FIELD_INDEX_NONE) + return; + + f= hiddev_get_f(d, ur); + if (hiddev_elide(d, ur, f)) + return; + + assert(f->fi.application <= 0xffffffffUL); + assert(ur->usage_code <= 0xffffffffUL); + sprintf(sb_app, "%lx", (unsigned long)f->fi.application); + sprintf(sb_usage, "%lx", (unsigned long)ur->usage_code); + + strs[0]= sb_app; + strs[1]= sb_usage; + mode->redacted(d, 2,strs, ur->value); +} + +static void hiddev_readable(Device *d) { + struct hiddev_usage_ref ur; + mread(d, &ur, sizeof(ur)); + mode->hiddev_event(d, &ur); +} + +static void hiddev_prepare(Device *d) { + int r, flags; + struct hiddev_devinfo di; + + flags= HIDDEV_FLAG_UREF; + if (mode->hiddev_xflags) flags |= mode->hiddev_xflags(); + r= ioctl(d->fd, HIDIOCSFLAG, &flags); + if (r) diee("hiddev %s: ioctl HIDIOCSFLAG", d->path); + + r= ioctl(d->fd, HIDIOCGDEVINFO, &di); + if (r) diee("hiddev %s: ioctl HIDIOCGDEVINFO", d->path); + + printf("device %s bustype ", d->path); + PR_TABLE_STR(bus, di.bustype); + printf(" bus %d dev %d if %d", di.busnum, di.devnum, di.ifnum); + DUMP_VPV(di); + printf(" napplications %d\n", di.num_applications); + + d->forkind.hiddev.froot= 0; + d->forkind.hiddev.fbuf= 0; } +static int hiddev_xflags_dump(void) { return HIDDEV_FLAG_REPORT; } + +static const KindInfo kind_hiddev= { hiddev_prepare, hiddev_readable }; + +/*---------- mode dump ----------*/ + static void dump_died(Device *d, int revents, int readr, int readc, int e) __attribute__((noreturn)); static void dump_died(Device *d, int revents, int readr, int readc, int e) { @@ -174,50 +512,42 @@ static void mainloop(void) { mode->died(&devices[i], polls[i].revents, r, dummy, errno); abort(); } - if (polls[i].revents) - process_device(&devices[i]); + if (polls[i].revents) { + Device *d= &devices[i]; + d->kind->readable(d); + mflushstdout(); + } } } } -static const ModeInfo mode_dump= { dump_event, dump_died, mainloop }; - -static void check_expect_sysfs(int fd, const char *path, const char *efn) { - char buf[50], *ep; - unsigned long maj, min; - struct stat stab; - FILE *sysfs; - int r; - - r= fstat(fd, &stab); if (r) diee("%s: fstat failed", path); - if (!S_ISCHR(stab.st_mode)) die("%s: not a character device", path); - - sysfs= fopen(efn,"r"); - if (!sysfs) diee("%s: failed to open sysfs %s", path, efn); - if (!fgets(buf,sizeof(buf)-1,sysfs)) { - if (ferror(sysfs)) diee("%s: failed to read sysfs %s", path, efn); - assert(feof(sysfs)); die("%s: eof on sysfs %s", path, efn); - } - buf[sizeof(buf)-1]= 0; - errno=0; maj=strtoul(buf,&ep,0); - if (errno || *ep!=':') die("%s: bad major number or no colon in sysfs" - " dev file %s", path, efn); - errno=0; min=strtoul(ep+1,&ep,0); - if (errno || *ep!='\n') die("%s: bad minor number or no colon in sysfs" - " dev file %s", path, efn); +static const ModeInfo mode_dump= { + evdev_dump, evdev_readable_dump, evdev_synch_dump, + hiddev_dump, hiddev_xflags_dump, + 0, dump_died, mainloop +}; + +/*---------- mode redact ----------*/ + +static void redact_redacted(Device *d, int nstrs, const char *strs[nstrs], + int value) { + int i; + + printf("%s %d", d->label, value); + for (i=0; ipath= mstrdup(path); d->fd= open(path, O_RDONLY); if (d->fd<0) diee("%s: failed to open",path); + d->kind= kind; + d->flags= dflags; - if (expect_sysfs) { - check_expect_sysfs(d->fd, path, expect_sysfs); - expect_sysfs= 0; + if (label) { + d->label= label; + label= 0; + } else { + d->label= d->path; } - r= ioctl(d->fd, EVIOCGID, &iid); if (r) diee("%s: failed to get id",path); - printf("device %s bustype ", path); - PR_TABLE_STR(bus, iid.bustype); - printf(" vendor %#x product %#x version %#x\n", - iid.vendor, iid.product, iid.version); - mflushstdout(); - - if (grab) - r= ioctl(d->fd, EVIOCGRAB, 1); if (r) diee("%s: failed to grab",path); + kind->prepare(d); } int main(int argc, const char **argv) { const char *arg; mode= &mode_dump; + kind= &kind_evdev; while ((arg= *++argv)) { if (arg[0] != '-') { @@ -254,9 +581,17 @@ int main(int argc, const char **argv) { else if (!strcmp(arg,"--expect-sysfs")) { if (!(expect_sysfs= *++argv)) badusage("missing arg for --expect-sysfs"); } - else if (!strcmp(arg,"--dump")) { mode= &mode_dump; } + else if (!strcmp(arg,"--label")) { + if (!(label= *++argv)) badusage("missing arg for --expect-sysfs"); + } + else if (!strcmp(arg,"--dump-raw")) { mode= &mode_dump; } + else if (!strcmp(arg,"--redact")) { mode= &mode_redact; } + else if (!strcmp(arg,"--evdev")) { kind= &kind_evdev; } + else if (!strcmp(arg,"--hiddev")) { kind= &kind_hiddev; } else if (!strcmp(arg,"--grab")) { grab= 1; } else if (!strcmp(arg,"--no-grab")) { grab= 0; } + else if (!strcmp(arg,"--show-unchanged")) { dflags.elide= 0; } + else if (!strcmp(arg,"--elide-unchanged")) { dflags.elide= 1; } else if (!strcmp(arg,"--stdin-monitor")) { stdinmonitor= 1; } else badusage("unknown option"); } diff --git a/hostside/hiddev-test.c b/hostside/hiddev-test.c index d2bc01b..3636b72 100644 --- a/hostside/hiddev-test.c +++ b/hostside/hiddev-test.c @@ -16,12 +16,6 @@ int main(int argc, char **argv) { struct hiddev_usage_ref ev; - struct hiddev_field_info fi; - int r, flag; - - flag= HIDDEV_FLAG_UREF|HIDDEV_FLAG_REPORT; - r= ioctl(0, HIDIOCSFLAG, &flag); - if (r) diee("ioctl HIDIOCSFLAG"); if (!argv[0] || argv[1]) badusage("no arguments allowed"); @@ -34,36 +28,6 @@ int main(int argc, char **argv) { if (ev.field_index == HID_FIELD_INDEX_NONE) printf("report type %04x id %04x field index NONE\n", ev.report_type, ev.report_id); - else { - printf("report type %04x id %04x field index %04x" - " usage index %04x code %04x value %08lx ", - ev.report_type, ev.report_id, - ev.field_index, - ev.usage_index, ev.usage_code, - (unsigned long)ev.value); - memset(&fi,0x55,sizeof(fi)); - fi.report_type= ev.report_type; - fi.report_id= ev.report_id; - fi.field_index= ev.field_index; - r= ioctl(0, HIDIOCGFIELDINFO, &fi); - if (r) { - printf(" ? %s",strerror(errno)); - } else { - printf(" maxusage %04x flags %04x" - " physical %04x %08lx..%08lx" - " logical %04x %08lx..%08lx" - " application %04x" - " unit %04x exponent %04x", - fi.maxusage, fi.flags, - fi.physical, (unsigned long)fi.physical_minimum, - (unsigned long)fi.physical_maximum, - fi.logical, (unsigned long)fi.logical_minimum, - (unsigned long)fi.logical_maximum, - fi.application, - fi.unit, fi.unit_exponent); - } - putchar('\n'); - } } }