vhidkb.c (5743B)
1 #include <assert.h> 2 #include <errno.h> 3 #include <fcntl.h> 4 #include <poll.h> 5 #include <stdarg.h> 6 #include <stdbool.h> 7 #include <stdint.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <unistd.h> 12 #include <linux/uhid.h> 13 14 #define NAME "vhidkb" 15 #define USAGE "usage: %s [-h] [-d DEVICE] [-t DELAY]\n" 16 17 typedef uint8_t u8; 18 typedef uint32_t u32; 19 20 /* This describes the report format of a 256-key NKRO keyboard. To understand 21 * this, read the HID specification available on USB's website (which 22 * unfortunately is of very low quality). */ 23 u8 rdesc[] = { 24 0x05, 0x01, /* [Global] Usage Page 0x01 (Generic Desktop) */ 25 0x09, 0x06, /* [Local] Usage 0x06 (Keyboard) */ 26 0xa1, 0x01, /* [Main] Collection 0x01 (Application) */ 27 0x05, 0x07, /* [Global] Usage Page 0x07 (Keyboard) */ 28 0x15, 0x00, /* [Global] Logical Minimum 0 */ 29 0x25, 0x01, /* [Global] Logical Minimum 1 */ 30 0x75, 0x01, /* [Global] Report Size 1 */ 31 /* Modifiers field */ 32 0x19, 0xe0, /* [Local] Usage Minimum 0xe0 (Left Control) */ 33 0x29, 0xe7, /* [Local] Usage Maximum 0xe7 (Right GUI) */ 34 0x95, 0x08, /* [Global] Report Count 8 */ 35 0x81, 0x02, /* [Main] Input (Variable) */ 36 /* Keys field */ 37 0x19, 0x00, /* [Local] Usage Minimum 0x00 */ 38 0x29, 0xff, /* [Local] Usage Maximum 0xff */ 39 0x96, 0x00, 0x01, /* [Global] Report Count 256 */ 40 0x81, 0x02, /* [Main] Input (Variable) */ 41 0xc0, /* [Main] End Collection */ 42 }; 43 44 void 45 fatalf(char *fmt, ...) { 46 va_list args; 47 va_start(args, fmt); 48 fprintf(stderr, "error: "); 49 vfprintf(stderr, fmt, args); 50 fprintf(stderr, "\n"); 51 va_end(args); 52 exit(1); 53 } 54 55 void 56 write_event(int fd, struct uhid_event *e) { 57 ssize_t n = write(fd, e, sizeof *e); 58 if (n < 0) 59 fatalf("write: %s", strerror(errno)); 60 else if (n != sizeof *e) 61 fatalf("write: incomplete write"); 62 } 63 64 void 65 send_create2(int fd) { 66 struct uhid_event e = {0}; 67 68 assert(sizeof rdesc <= sizeof e.u.create2.rd_data); 69 assert(sizeof NAME <= sizeof e.u.create2.name); 70 71 e.type = UHID_CREATE2; 72 strcpy((char *)e.u.create2.name, NAME); 73 e.u.create2.rd_size = sizeof rdesc; 74 e.u.create2.bus = BUS_VIRTUAL; 75 e.u.create2.vendor = 0; /* XXX */ 76 e.u.create2.product = 0; /* XXX */ 77 e.u.create2.version = 0; 78 e.u.create2.country = 0; 79 memcpy(e.u.create2.rd_data, rdesc, sizeof rdesc); 80 81 write_event(fd, &e); 82 } 83 84 void 85 send_input2(int fd, u8 *keys) { 86 struct uhid_event e = {0}; 87 88 assert(1+32 <= sizeof e.u.input2.data); 89 90 e.type = UHID_INPUT2; 91 e.u.input2.size = 1 + 32; 92 e.u.input2.data[0] = keys[28]; /* modifiers field */ 93 memcpy(&e.u.input2.data[1], keys, 32); /* keys field */ 94 95 write_event(fd, &e); 96 } 97 98 void 99 send_get_report_reply(int fd, u8 *keys, struct uhid_get_report_req req) { 100 if (req.rnum != 0 || req.rtype != UHID_INPUT_REPORT) { 101 fprintf(stderr, "warning: ignoring unexpected UHID event\n"); 102 return; 103 } 104 105 struct uhid_event e = {0}; 106 107 assert(1+32 <= sizeof e.u.get_report_reply.data); 108 109 e.type = UHID_GET_REPORT_REPLY; 110 e.u.get_report_reply.id = req.id; 111 e.u.get_report_reply.err = 0; 112 e.u.get_report_reply.size = 1 + 32; 113 e.u.get_report_reply.data[0] = keys[28]; /* modifiers field */ 114 memcpy(&e.u.get_report_reply.data[1], keys, 32); /* keys field */ 115 116 write_event(fd, &e); 117 } 118 119 int 120 main(int argc, char **argv) { 121 char *dev = "/dev/uhid"; 122 long delayus = 100000; 123 124 int opt; 125 while ((opt = getopt(argc, argv, "hd:t:")) != -1) { 126 switch (opt) { 127 case 'h': 128 printf(USAGE, argv[0]); 129 return 0; 130 case 'd': 131 dev = optarg; 132 break; 133 case 't': { 134 long delayms = strtol(optarg, 0, 10); 135 if (delayms >= 1000) 136 delayus = 999999L; /* Max POSIX usleep argument */ 137 else if (delayms < -1) 138 delayus = -1; 139 else 140 delayus = delayms * 1000; 141 break; 142 } 143 default: 144 fatalf("unknown option: %c\n" USAGE, opt, argv[0]); 145 } 146 } 147 148 if (optind < argc) 149 fatalf("unexpected arguments\n" USAGE, argv[0]); 150 151 int fd = open(dev, O_RDWR); 152 if (fd < 0) 153 fatalf("open: %s", strerror(errno)); 154 155 send_create2(fd); 156 157 u8 keys[32] = {0}; 158 159 struct pollfd pfds[2]; 160 pfds[0].fd = delayus < 0 ? 0 : -1; 161 pfds[0].events = POLLIN; 162 pfds[1].fd = fd; 163 pfds[1].events = POLLIN; 164 165 for (;;) { 166 int ret = poll(pfds, 2, -1); 167 if (ret < 0) 168 fatalf("poll: %s", strerror(errno)); 169 170 if ((pfds[0].revents & ~(POLLIN|POLLHUP)) 171 || (pfds[1].revents & ~POLLIN)) 172 fatalf("poll: unexpected event"); 173 174 if (pfds[0].revents & POLLIN) { 175 u8 buf[64]; 176 ssize_t n = read(0, buf, sizeof buf); 177 if (n < 0) 178 fatalf("read: %s", strerror(errno)); 179 if (n == 0) 180 break; 181 for (size_t i = 0; i < n; i++) { 182 u8 k = buf[i]; 183 keys[k / 8] ^= 1 << (k % 8); 184 send_input2(fd, keys); 185 } 186 } else if (pfds[0].revents & POLLHUP) { 187 break; 188 } 189 190 if (pfds[1].revents & POLLIN) { 191 struct uhid_event e; 192 ssize_t n = read(fd, &e, sizeof e); 193 if (n < 0) 194 fatalf("read: %s", strerror(errno)); 195 if (n == 0) 196 fatalf("HUP on UHID device"); 197 if (n < sizeof e.type) 198 fatalf("UHID event too small"); 199 switch (e.type) { 200 case UHID_START: 201 case UHID_STOP: 202 /* Ignore */ 203 break; 204 case UHID_OPEN: 205 if (delayus >= 0) { 206 usleep(delayus); 207 pfds[0].fd = 0; 208 } 209 break; 210 case UHID_CLOSE: 211 if (delayus >= 0) 212 pfds[0].fd = -1; 213 break; 214 case UHID_GET_REPORT: 215 /* Note that struct uhid_event is packed. */ 216 if (n < sizeof e.type + sizeof e.u.get_report) 217 fatalf("UHID event too small"); 218 send_get_report_reply(fd, keys, e.u.get_report); 219 break; 220 case UHID_OUTPUT: 221 case UHID_SET_REPORT: 222 fprintf(stderr, "warning: ignoring unexpected UHID event\n"); 223 break; 224 default: 225 fatalf("invalid UHID event type"); 226 } 227 } 228 } 229 }