Hello, I am trying to simulate a keyboard using the uhid interface. The fake device shows up in dmesg, but attempting to press the "a" key does nothing I can see. No input appears in the active terminal, nor in an xev window. The program receives UHID_OPEN, UHID_START, and UHID_CLOSE. Many different report descriptors were tried. The one currently uncommented is the one distributed with the USB implementor forum's HID report descriptor tool. The other is one found in a description of a boot device compatible keyboard. Thanks in advance, R0b0t1
#define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <poll.h> #include <sys/ioctl.h> #include <linux/uhid.h> #define handle_error(m) \ do { perror(m); exit(EXIT_FAILURE); } while (0) static unsigned char rdesc[] = { 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x03, 0x95, 0x05, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x03, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0xc0 }; /* static unsigned char rdesc[] = { 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x06, // Usage (Keyboard) 0xa1, 0x01, // Collection (Application) // Modifier report. 0x75, 0x01, // Report Size (1) 0x95, 0x08, // Report Count (8) 0x05, 0x07, // Usage Page (Key Codes) 0x19, 0xe0, // Usage Minimum (224) 0x29, 0xe7, // Usage Maximum (231) 0x15, 0x00, // Logical Minimum (0) - Shorten (0x14)? 0x25, 0x01, // Logical Maximum (1) 0x81, 0x02, // Input (Data, Variable, Absolute) // Reserved byte. 0x95, 0x01, // Report Count (1) 0x75, 0x08, // Report Size (8) 0x81, 0x01, // Input (Constant) // LED report. 0x95, 0x05, // Report Count (5) 0x75, 0x01, // Report Size (1) 0x05, 0x08, // Usage Page (LEDs) 0x19, 0x01, // Usage Minimum (1) 0x29, 0x05, // Usage Maximum (5) 0x91, 0x02, // Output (Data, Variable, Absolute) // LED report padding. 0x95, 0x01, // Report Count (1) 0x75, 0x03, // Report Size (3) 0x91, 0x01, // Output (Constant) // Key code report. 0x95, 0x06, // Report Count (6) 0x75, 0x08, // Report Size (8) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xff, 0x00, // Logical Maximum (255) 0x05, 0x07, // Usage Page (Key Codes) 0x19, 0x00, // Usage Minimum (0) 0x29, 0xff, // Usage Maximum (255) 0x81, 0x00, // Input (Data, Array) 0xc0 // End Collection }; */ static int uhid_write(int fd, const struct uhid_event *he) { ssize_t r = write(fd, he, sizeof(*he)); if (r < 0) { fprintf(stderr, "write: cannot write to uhid: %m\n"); return -errno; } else if (r != sizeof(*he)) { fprintf(stderr, "write: wrong size written to uhid: %ld != %lu\n", r, sizeof(*he)); return -EFAULT; } else return 0; } static int uhid_event(int fd) { struct uhid_event he; ssize_t sz; if ((sz = read(fd, &he, sizeof(he))) == -1) handle_error("read"); switch (he.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"); return 0; case UHID_CLOSE: fprintf(stderr, "UHID_CLOSE from uhid-dev.\n"); break; case UHID_OUTPUT: fprintf(stderr, "UHID_OUTPUT from uhid-dev.\n"); // TODO: Handle output. 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", he.type); } return 1; } int main(int argc, char *argv[]) { int fd; ssize_t sz; struct uhid_event he; if ((fd = open("/dev/uhid", O_RDWR | O_CLOEXEC)) == -1) handle_error("open"); struct pollfd fds[1] = { {.fd = fd, .events = POLLIN} }; memset(&he, 0, sizeof(he)); he.type = UHID_CREATE2; strcpy((char *)he.u.create.name, "kb-daemon-device"); memcpy(he.u.create2.rd_data, rdesc, sizeof(rdesc)); he.u.create2.rd_size = sizeof(rdesc); he.u.create2.bus = BUS_USB; he.u.create2.vendor = 0x15d9; he.u.create2.product = 0x0a37; he.u.create2.version = 0; he.u.create2.country = 0; if (uhid_write(fd, &he)) handle_error("uhid_write"); char cont = 1; while (cont) { int n = ppoll(fds, sizeof(fds), NULL, NULL); if (n == -1) handle_error("ppoll"); for (int i = 0; i < n; i++) { if (fds[i].fd == fd && fds[i].revents & POLLIN) cont = uhid_event(fds[i].fd); } } memset(&he, 0, sizeof(he)); he.type = UHID_INPUT2; he.u.input2.size = 8; he.u.input2.data[0] = 0x00; he.u.input2.data[1] = 0x00; he.u.input2.data[2] = 0x04; he.u.input2.data[3] = 0x00; he.u.input2.data[4] = 0x00; he.u.input2.data[5] = 0x00; he.u.input2.data[6] = 0x00; he.u.input2.data[7] = 0x00; if (uhid_write(fd, &he)) handle_error("uhid_write"); memset(&he, 0, sizeof(he)); he.type = UHID_INPUT2; he.u.input2.size = 8; he.u.input2.data[0] = 0x00; he.u.input2.data[1] = 0x00; he.u.input2.data[2] = 0x00; he.u.input2.data[3] = 0x00; he.u.input2.data[4] = 0x00; he.u.input2.data[5] = 0x00; he.u.input2.data[6] = 0x00; he.u.input2.data[7] = 0x00; if (uhid_write(fd, &he)) handle_error("uhid_write"); memset(&he, 0, sizeof(he)); he.type = UHID_DESTROY; if (uhid_write(fd, &he)) handle_error("uhid_write"); uhid_event(fd); return EXIT_SUCCESS; }