#include #include #include #include #include #include #include #include #include #include #include static unsigned char rdesc[] = { #include "wheel_hid_report_desc.h" }; static int uhid_write(int fd, const struct uhid_event *ev) { ssize_t ret; ret = write(fd, ev, sizeof(*ev)); if (ret < 0) { fprintf(stderr, "Cannot write to uhid: %m\n"); return -errno; } else if (ret != sizeof(*ev)) { fprintf(stderr, "Wrong size written to uhid: %zd != %zu\n", ret, sizeof(ev)); return -EFAULT; } else { return 0; } } static int create(int fd) { struct uhid_event ev; memset(&ev, 0, sizeof(ev)); ev.type = UHID_CREATE; strcpy((char*)ev.u.create.name, "test-uhid-device"); ev.u.create.rd_data = rdesc; ev.u.create.rd_size = sizeof(rdesc); ev.u.create.bus = BUS_USB; ev.u.create.vendor = 0x15d9; ev.u.create.product = 0x0a37; ev.u.create.version = 0; ev.u.create.country = 0; return uhid_write(fd, &ev); } static void destroy(int fd) { struct uhid_event ev; memset(&ev, 0, sizeof(ev)); ev.type = UHID_DESTROY; uhid_write(fd, &ev); } /* This parses raw output reports sent by the kernel to the device. A normal * uhid program shouldn't do this but instead just forward the raw report. * However, for ducomentational purposes, we try to detect LED events here and * print debug messages for it. */ static void handle_output(struct uhid_event *ev) { /* LED messages are adverised via OUTPUT reports; ignore the rest */ if (ev->u.output.rtype != UHID_OUTPUT_REPORT) return; /* LED reports have length 2 bytes */ if (ev->u.output.size != 2) return; /* first byte is report-id which is 0x02 for LEDs in our rdesc */ if (ev->u.output.data[0] != 0x2) return; /* print flags payload */ fprintf(stderr, "LED output report received with flags %x\n", ev->u.output.data[1]); } static int event(int fd) { struct uhid_event ev; ssize_t ret; memset(&ev, 0, sizeof(ev)); ret = read(fd, &ev, sizeof(ev)); if (ret == 0) { fprintf(stderr, "Read HUP on uhid-cdev\n"); return -EFAULT; } else if (ret < 0) { fprintf(stderr, "Cannot read uhid-cdev: %m\n"); return -errno; } else if (ret != sizeof(ev)) { fprintf(stderr, "Invalid size read from uhid-dev: %zd != %zu\n", ret, sizeof(ev)); return -EFAULT; } switch (ev.type) { case UHID_START: fprintf(stderr, "UHID_START from uhid-dev\n"); break; case UHID_STOP: fprintf(stderr, "UHID_STOP from uhid-dev\n"); break; case UHID_OPEN: fprintf(stderr, "UHID_OPEN from uhid-dev\n"); break; case UHID_CLOSE: fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); break; case UHID_OUTPUT: fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); handle_output(&ev); break; case UHID_OUTPUT_EV: fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); break; default: fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); } return 0; } struct my_report { uint8_t reportid; int8_t a[4]; uint16_t rest; } __attribute__((packed)) r = {1}; static int send_event(int fd) { struct uhid_event ev; memset(&ev, 0, sizeof(ev)); ev.type = UHID_INPUT; ev.u.input.size = sizeof(r); memcpy(ev.u.input.data, &r, sizeof(r)); printf("\n"); for(size_t i = 0; i < sizeof(r); i++) { printf("0x%x ", ev.u.input.data[i]); } printf("\n"); printf("Sending event\n"); return uhid_write(fd, &ev); } static int keyboard(int fd) { static int axisidx = 0; char buf[128]; ssize_t ret = read(STDIN_FILENO, buf, sizeof(buf)); if (ret == 0) { fprintf(stderr, "Read HUP on stdin\n"); return -EFAULT; } else if (ret < 0) { fprintf(stderr, "Cannot read stdin: %m\n"); return -errno; } for(size_t i = 0; i < ret; ++i) { if(buf[i] >= '1' && buf[i] <= '8') { axisidx = buf[i] - '0' - 1; continue; } switch(buf[i]) { case 'u': r.a[axisidx] += 2; send_event(fd); break; case 'd': r.a[axisidx] -= 2; send_event(fd); break; case 'q': return -ECANCELED; default: fprintf(stderr, "Unknown command %c\n", buf[i]); } } return 0; } int main(int argc, char **argv) { int fd; const char *path = "/dev/uhid"; struct pollfd pfds[2]; int ret; struct termios state; ret = tcgetattr(STDIN_FILENO, &state); if (ret) { fprintf(stderr, "Cannot get tty state\n"); } else { state.c_lflag &= ~ICANON; state.c_cc[VMIN] = 1; ret = tcsetattr(STDIN_FILENO, TCSANOW, &state); if (ret) fprintf(stderr, "Cannot set tty state\n"); } if (argc >= 2) { if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { fprintf(stderr, "Usage: %s [%s]\n", argv[0], path); return EXIT_SUCCESS; } else { path = argv[1]; } } fprintf(stderr, "Open uhid-cdev %s\n", path); fd = open(path, O_RDWR | O_CLOEXEC); if (fd < 0) { fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path); return EXIT_FAILURE; } fprintf(stderr, "Create uhid device\n"); ret = create(fd); if (ret) { close(fd); return EXIT_FAILURE; } pfds[0].fd = STDIN_FILENO; pfds[0].events = POLLIN; pfds[1].fd = fd; pfds[1].events = POLLIN; fprintf(stderr, "Press 'q' to quit...\n"); while (1) { ret = poll(pfds, 2, -1); if (ret < 0) { fprintf(stderr, "Cannot poll for fds: %m\n"); break; } if (pfds[0].revents & POLLHUP) { fprintf(stderr, "Received HUP on stdin\n"); break; } if (pfds[1].revents & POLLHUP) { fprintf(stderr, "Received HUP on uhid-cdev\n"); break; } if (pfds[0].revents & POLLIN) { ret = keyboard(fd); if (ret) break; } if (pfds[1].revents & POLLIN) { ret = event(fd); if (ret) break; } } fprintf(stderr, "Destroy uhid device\n"); destroy(fd); return EXIT_SUCCESS; }