simple mouse virtual device
[uhid-examples] / vmouse.c
1 #include <errno.h>
2 #include <poll.h>
3 #include <fcntl.h>
4 #include <stdbool.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <linux/uhid.h>
11
12 #define SLEEP_TIME_SEC          10
13
14 /* mouse report descriptor 
15  * you can use a tool like https://eleccelerator.com/usbdescreqparser/
16  * or rip a report descriptor with usbhid-dump 
17  * don't try to make any sense of the numbers, it is mostly legacy
18  * convention */
19
20 static unsigned char rdesc[] = {
21 0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
22 0x09, 0x02,        // Usage (Mouse)
23 0xA1, 0x01,        // Collection (Application)
24 0x09, 0x01,        //   Usage (Pointer)
25 0xA1, 0x00,        //   Collection (Physical)
26 0x05, 0x09,        //     Usage Page (Button)
27 0x19, 0x01,        //     Usage Minimum (0x01)
28 0x29, 0x03,        //     Usage Maximum (0x03)
29 0x15, 0x00,        //     Logical Minimum (0)
30 0x25, 0x01,        //     Logical Maximum (1)
31 0x75, 0x01,        //     Report Size (1)
32 0x95, 0x03,        //     Report Count (3)
33 0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
34 0x75, 0x05,        //     Report Size (5)
35 0x95, 0x01,        //     Report Count (1)
36 0x81, 0x01,        //     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
37 0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
38 0x09, 0x30,        //     Usage (X)
39 0x09, 0x31,        //     Usage (Y)
40 0x09, 0x38,        //     Usage (Wheel)
41 0x15, 0x81,        //     Logical Minimum (-127)
42 0x25, 0x7F,        //     Logical Maximum (127)
43 0x75, 0x08,        //     Report Size (8)
44 0x95, 0x03,        //     Report Count (3)
45 0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
46 0xC0,              //   End Collection
47 0xC0,              // End Collection
48
49 // 52 bytes
50 };
51
52 /* input report: report id is 0 (not specified in report
53  * descriptor so assumed */
54 typedef struct _report {
55         unsigned char buttons;
56         unsigned char x_pos;
57         unsigned char y_pos;
58         unsigned char wheel;
59 } report_t;
60
61 static report_t in_report;
62
63 static int uhid_write(int fd, const struct uhid_event *ev)
64 {
65         ssize_t ret;
66
67         ret = write(fd, ev, sizeof(*ev));
68         if (ret < 0) {
69                 fprintf(stderr, "cannot write to uhid: %m\n");
70                 return -errno;
71         } else if (ret != sizeof(*ev)) {
72                 fprintf(stderr, "wrong size written to uhid: %ld != %lu\n",
73                         ret, sizeof(ev));
74                 return -EFAULT;
75         } else {
76                 return 0;
77         }
78 }
79
80 static int create(int fd)
81 {
82         struct uhid_event ev;
83
84         memset(&ev, 0, sizeof(ev));
85         ev.type = UHID_CREATE;
86         strcpy((char*)ev.u.create.name, "mr_woggle's HID device");
87         ev.u.create.rd_data = rdesc;
88         ev.u.create.rd_size = sizeof(rdesc);
89         ev.u.create.bus = BUS_BLUETOOTH;
90         ev.u.create.vendor = 0x1234;
91         ev.u.create.product = 0xabcd;
92         ev.u.create.version = 1;
93         ev.u.create.country = 0;
94
95         return uhid_write(fd, &ev);
96 }
97
98 static int send_event(int fd)
99 {
100         struct uhid_event ev;
101
102         memset(&ev, 0, sizeof(ev));
103         ev.type = UHID_INPUT;
104
105         /* move mouse two pixels to the right */
106         in_report.x_pos = 2;
107
108         ev.u.input.size = 5; /* includes report ID */
109         memcpy(ev.u.input.data, (uint8_t *) &in_report, sizeof(in_report));
110
111         return uhid_write(fd, &ev);
112 }
113
114
115 int main(int argc, char **argv)
116 {
117         int fd;
118         const char *path = "/dev/uhid";
119         int ret;
120
121         if (argc >= 2) {
122                 if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
123                         fprintf(stderr, "Usage: %s [%s]\n", argv[0], path);
124                         return EXIT_SUCCESS;
125                 } else {
126                         path = argv[1];
127                 }
128         }
129
130         fd = open(path, O_RDWR | O_CLOEXEC);
131         if (fd < 0) {
132                 fprintf(stderr, "cannot open uhid-cdev (try as root?) %s: %m\n", path);
133                 return EXIT_FAILURE;
134         }
135
136         ret = create(fd);
137         if (ret) {
138                 close(fd);
139                 fprintf(stderr, "xannot create uhid device");
140                 return EXIT_FAILURE;
141         }
142
143         fprintf(stderr, "created virtual HID device\n");
144         fprintf(stderr, "ctrl-c to quit\n");
145
146         while (1) {
147                 send_event(fd);
148                 sleep(SLEEP_TIME_SEC);
149         }
150
151         /* ugly exit */
152 }