From: Robin Krens <robin@robinkrens.nl>
Date: Sat, 28 May 2022 21:40:53 +0000 (+0200)
Subject: simple mouse virtual device
X-Git-Url: https://robinkrens.nl/gitweb/?a=commitdiff_plain;p=uhid-examples

simple mouse virtual device
---

dc7ec815b2597a4a201dc8361c9bdd1e9b6ebdc6
diff --git a/vmouse.c b/vmouse.c
new file mode 100644
index 0000000..73dc3d3
--- /dev/null
+++ b/vmouse.c
@@ -0,0 +1,152 @@
+#include <errno.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/uhid.h>
+
+#define SLEEP_TIME_SEC		10
+
+/* mouse report descriptor 
+ * you can use a tool like https://eleccelerator.com/usbdescreqparser/
+ * or rip a report descriptor with usbhid-dump 
+ * don't try to make any sense of the numbers, it is mostly legacy
+ * convention */
+
+static unsigned char rdesc[] = {
+0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
+0x09, 0x02,        // Usage (Mouse)
+0xA1, 0x01,        // Collection (Application)
+0x09, 0x01,        //   Usage (Pointer)
+0xA1, 0x00,        //   Collection (Physical)
+0x05, 0x09,        //     Usage Page (Button)
+0x19, 0x01,        //     Usage Minimum (0x01)
+0x29, 0x03,        //     Usage Maximum (0x03)
+0x15, 0x00,        //     Logical Minimum (0)
+0x25, 0x01,        //     Logical Maximum (1)
+0x75, 0x01,        //     Report Size (1)
+0x95, 0x03,        //     Report Count (3)
+0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x75, 0x05,        //     Report Size (5)
+0x95, 0x01,        //     Report Count (1)
+0x81, 0x01,        //     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
+0x09, 0x30,        //     Usage (X)
+0x09, 0x31,        //     Usage (Y)
+0x09, 0x38,        //     Usage (Wheel)
+0x15, 0x81,        //     Logical Minimum (-127)
+0x25, 0x7F,        //     Logical Maximum (127)
+0x75, 0x08,        //     Report Size (8)
+0x95, 0x03,        //     Report Count (3)
+0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+0xC0,              //   End Collection
+0xC0,              // End Collection
+
+// 52 bytes
+};
+
+/* input report: report id is 0 (not specified in report
+ * descriptor so assumed */
+typedef struct _report {
+	unsigned char buttons;
+	unsigned char x_pos;
+	unsigned char y_pos;
+	unsigned char wheel;
+} report_t;
+
+static report_t in_report;
+
+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: %ld != %lu\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, "mr_woggle's HID device");
+	ev.u.create.rd_data = rdesc;
+	ev.u.create.rd_size = sizeof(rdesc);
+	ev.u.create.bus = BUS_BLUETOOTH;
+	ev.u.create.vendor = 0x1234;
+	ev.u.create.product = 0xabcd;
+	ev.u.create.version = 1;
+	ev.u.create.country = 0;
+
+	return uhid_write(fd, &ev);
+}
+
+static int send_event(int fd)
+{
+	struct uhid_event ev;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_INPUT;
+
+	/* move mouse two pixels to the right */
+	in_report.x_pos = 2;
+
+	ev.u.input.size = 5; /* includes report ID */
+	memcpy(ev.u.input.data, (uint8_t *) &in_report, sizeof(in_report));
+
+	return uhid_write(fd, &ev);
+}
+
+
+int main(int argc, char **argv)
+{
+	int fd;
+	const char *path = "/dev/uhid";
+	int ret;
+
+	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];
+		}
+	}
+
+	fd = open(path, O_RDWR | O_CLOEXEC);
+	if (fd < 0) {
+		fprintf(stderr, "cannot open uhid-cdev (try as root?) %s: %m\n", path);
+		return EXIT_FAILURE;
+	}
+
+	ret = create(fd);
+	if (ret) {
+		close(fd);
+		fprintf(stderr, "xannot create uhid device");
+		return EXIT_FAILURE;
+	}
+
+	fprintf(stderr, "created virtual HID device\n");
+	fprintf(stderr, "ctrl-c to quit\n");
+
+	while (1) {
+		send_event(fd);
+		sleep(SLEEP_TIME_SEC);
+	}
+
+	/* ugly exit */
+}