vhidkb

virtual HID keyboard
git clone git://git.rr3.xyz/vhidkb
Log | Files | Refs | README | LICENSE

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 }