/* evtool.c — tiny evdev probe tool for R3 Pro II (static MIPS32r2 musl). * * Subcommands: * evtool codes dump EV bits (types + key codes) * evtool dump [dev...] read + print events (no grab) * evtool send inject one event via write() * evtool grab [dev...] EVIOCGRAB, then read + print * evtool fwd EVIOCGRAB, read, write back every event * (forward experiment) + print * evtool keys EVIOCGKEY poll loop; print state changes * evtool grabchk EVIOCGRAB result only (0 / errno) * * Event struct is fixed 16 bytes on MIPS32: timeval(8) + type(2) + code(2) + value(4). */ #include #include #include #include #include #include #include #include #include #include #include #ifndef EVIOCGRAB #define EVIOCGRAB _IOW('E', 0x90, int) #endif #ifndef EVIOCGKEY #define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) #endif #define EV_MAXC 0x1f #define KEY_MAXC 0x2ff #define KEY_WORDS ((KEY_MAXC + 1 + 31) / 32) static unsigned long long now_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (unsigned long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; } static int open_dev(const char *dev, int wr) { int fd = open(dev, wr ? O_RDWR : O_RDONLY); if (fd < 0) { fprintf(stderr, "open %s: %s\n", dev, strerror(errno)); return -1; } return fd; } static void print_ev(const char *tag, const unsigned char *ev) { uint16_t type = *(uint16_t *)(ev + 8); uint16_t code = *(uint16_t *)(ev + 10); int32_t val = *(int32_t *)(ev + 12); fprintf(stderr, "%s t=%llu type=%d code=%d val=%d\n", tag, now_ms(), type, code, val); } /* ---------- codes ---------- */ static void cmd_codes(const char *dev) { int fd = open_dev(dev, 0); if (fd < 0) return; unsigned long evbits[EV_MAXC / 32 + 1]; memset(evbits, 0, sizeof evbits); if (ioctl(fd, EVIOCGBIT(0, sizeof evbits), evbits) < 0) { perror("EVIOCGBIT(0)"); return; } printf("EV bits:"); for (int i = 0; i <= EV_MAXC; i++) if (evbits[i / 32] & (1UL << (i % 32))) printf(" %d", i); printf("\n"); unsigned long kb[KEY_WORDS]; memset(kb, 0, sizeof kb); if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof kb), kb) >= 0) { printf("KEY codes:"); for (int i = 0; i <= KEY_MAXC; i++) if (kb[i / 32] & (1UL << (i % 32))) printf(" %d", i); printf("\n"); } else { perror("EVIOCGBIT(EV_KEY)"); } close(fd); } /* ---------- dump ---------- */ static void dump_dev(const char *dev, const char *tag) { int fd = open_dev(dev, 0); if (fd < 0) return; printf("opened %s as %s\n", dev, tag); fflush(stdout); struct pollfd pfd = { fd, POLLIN, 0 }; unsigned char ev[16]; for (;;) { int r = poll(&pfd, 1, -1); if (r < 0) { perror("poll"); break; } if (pfd.revents & POLLIN) { ssize_t n = read(fd, ev, sizeof ev); if (n == 16) print_ev(tag, ev); else if (n < 0) { perror("read"); break; } } } close(fd); } static void cmd_dump(int argc, char **argv) { for (int i = 0; i < argc; i++) { pid_t p = fork(); if (p == 0) { dump_dev(argv[i], argv[i]); _exit(0); } } for (;;) pause(); } /* ---------- send ---------- */ static void cmd_send(const char *dev, int type, int code, int value) { int fd = open_dev(dev, 1); if (fd < 0) return; struct input_event ev; memset(&ev, 0, sizeof ev); ev.type = type; ev.code = code; ev.value = value; ssize_t n = write(fd, &ev, sizeof ev); printf("write(%s) -> %zd errno=%d (%s)\n", dev, n, errno, strerror(errno)); close(fd); } /* ---------- grab / fwd ---------- */ static int do_grab(int fd, int on) { int v = on ? 1 : 0; int r = ioctl(fd, EVIOCGRAB, v); printf("EVIOCGRAB(%d) -> %d errno=%d (%s)\n", on, r, errno, strerror(errno)); return r; } static void loop_forward(const char *dev, int fwd) { int fd = open_dev(dev, 1); if (fd < 0) return; if (do_grab(fd, 1) < 0) return; printf("GRABBED %s (fwd=%d), reading...\n", dev, fwd); fflush(stdout); struct pollfd pfd = { fd, POLLIN, 0 }; unsigned char ev[16]; for (;;) { int r = poll(&pfd, 1, -1); if (r < 0) { perror("poll"); break; } if (pfd.revents & POLLIN) { ssize_t n = read(fd, ev, sizeof ev); if (n != 16) { if (n < 0) perror("read"); continue; } print_ev("G", ev); if (fwd) { ssize_t w = write(fd, ev, sizeof ev); if (w != 16) fprintf(stderr, " write-back failed: %s\n", strerror(errno)); } } } close(fd); } /* ---------- fwdt: grab + self-inject + forward ---------- */ /* Sequence: grab ; after wait_ms inject press(code); after +300ms inject * release(code); meanwhile forward every read event back to . * Optional 5th arg: total duration ms (0 = run forever). * A second reader on tells us whether write-back reaches non-grabbers. */ static void loop_fwdt(const char *dev, int code, int wait_ms, int dur_ms) { int fd = open_dev(dev, 1); if (fd < 0) return; if (do_grab(fd, 1) < 0) return; printf("FWDT grabbed %s code=%d wait=%d dur=%d\n", dev, code, wait_ms, dur_ms); fflush(stdout); struct pollfd pfd = { fd, POLLIN, 0 }; unsigned char ev[16]; unsigned long long t0 = now_ms(), inject_done = 0; int phase = 0; /* 0=idle, 1=pressed, 2=released */ for (;;) { unsigned long long t = now_ms(); if (dur_ms > 0 && t - t0 >= (unsigned long long)dur_ms) break; if (!inject_done && t - t0 >= (unsigned long long)wait_ms) { struct input_event e; memset(&e, 0, sizeof e); e.type = EV_KEY; e.code = code; e.value = 1; if (write(fd, &e, sizeof e) != sizeof e) fprintf(stderr, "inject press fail\n"); printf("== injected PRESS %d\n", code); phase = 1; inject_done = 1; } else if (phase == 1 && t - t0 >= (unsigned long long)wait_ms + 300) { struct input_event e; memset(&e, 0, sizeof e); e.type = EV_KEY; e.code = code; e.value = 0; if (write(fd, &e, sizeof e) != sizeof e) fprintf(stderr, "inject release fail\n"); printf("== injected RELEASE %d\n", code); phase = 2; } int r = poll(&pfd, 1, 100); if (r < 0) { perror("poll"); break; } if (pfd.revents & POLLIN) { ssize_t n = read(fd, ev, sizeof ev); if (n == 16) { print_ev("RD", ev); ssize_t w = write(fd, ev, sizeof ev); if (w != 16) fprintf(stderr, " WB fail: %s\n", strerror(errno)); } } } close(fd); } /* ---------- keys (EVIOCGKEY poll) ---------- */ static void cmd_keys(const char *dev, int dur_ms) { int fd = open_dev(dev, 0); if (fd < 0) return; unsigned long prev[KEY_WORDS], cur[KEY_WORDS]; memset(prev, 0, sizeof prev); printf("EVIOCGKEY poll on %s (dur=%dms)\n", dev, dur_ms); fflush(stdout); unsigned long long t0 = now_ms(); for (;;) { if (dur_ms > 0 && now_ms() - t0 >= (unsigned long long)dur_ms) break; memset(cur, 0, sizeof cur); if (ioctl(fd, EVIOCGKEY(sizeof cur), cur) < 0) { perror("EVIOCGKEY"); break; } for (int w = 0; w < KEY_WORDS; w++) { if (cur[w] != prev[w]) { for (int b = 0; b < 32; b++) { int cb = (cur[w] >> b) & 1; int pb = (prev[w] >> b) & 1; if (cb != pb) printf("key %d %s\n", w * 32 + b, cb ? "DOWN" : "UP"); } prev[w] = cur[w]; } } fflush(stdout); usleep(10000); /* 10 ms */ } close(fd); } int main(int argc, char **argv) { setvbuf(stderr, NULL, _IONBF, 0); if (argc < 2) { fprintf(stderr, "usage: evtool codes|dump|send|grab|fwd|keys|grabchk \n"); return 1; } const char *cmd = argv[1]; if (!strcmp(cmd, "codes") && argc >= 3) { cmd_codes(argv[2]); return 0; } if (!strcmp(cmd, "dump") && argc >= 3) { cmd_dump(argc - 2, argv + 2); return 0; } if (!strcmp(cmd, "send") && argc >= 6) { cmd_send(argv[2], atoi(argv[3]), atoi(argv[4]), atoi(argv[5])); return 0; } if (!strcmp(cmd, "grab") && argc >= 3) { loop_forward(argv[2], 0); return 0; } if (!strcmp(cmd, "fwd") && argc >= 3) { loop_forward(argv[2], 1); return 0; } if (!strcmp(cmd, "keys") && argc >= 3) { cmd_keys(argv[2], argc >= 4 ? atoi(argv[3]) : 0); return 0; } if (!strcmp(cmd, "fwdt") && argc >= 4) { loop_fwdt(argv[2], atoi(argv[3]), argc >= 5 ? atoi(argv[4]) : 1000, argc >= 6 ? atoi(argv[5]) : 0); return 0; } if (!strcmp(cmd, "grabchk") && argc >= 3) { int fd = open_dev(argv[2], 1); if (fd >= 0) { do_grab(fd, 1); do_grab(fd, 0); close(fd); } return 0; } fprintf(stderr, "bad args\n"); return 1; }