Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * UHID Example
0004  *
0005  * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
0006  *
0007  * The code may be used by anyone for any purpose,
0008  * and can serve as a starting point for developing
0009  * applications using uhid.
0010  */
0011 
0012 /*
0013  * UHID Example
0014  * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this
0015  * program as root and then use the following keys to control the mouse:
0016  *   q: Quit the application
0017  *   1: Toggle left button (down, up, ...)
0018  *   2: Toggle right button
0019  *   3: Toggle middle button
0020  *   a: Move mouse left
0021  *   d: Move mouse right
0022  *   w: Move mouse up
0023  *   s: Move mouse down
0024  *   r: Move wheel up
0025  *   f: Move wheel down
0026  *
0027  * Additionally to 3 button mouse, 3 keyboard LEDs are also supported (LED_NUML,
0028  * LED_CAPSL and LED_SCROLLL). The device doesn't generate any related keyboard
0029  * events, though. You need to manually write the EV_LED/LED_XY/1 activation
0030  * input event to the evdev device to see it being sent to this device.
0031  *
0032  * If uhid is not available as /dev/uhid, then you can pass a different path as
0033  * first argument.
0034  * If <linux/uhid.h> is not installed in /usr, then compile this with:
0035  *   gcc -o ./uhid_test -Wall -I./include ./samples/uhid/uhid-example.c
0036  * And ignore the warning about kernel headers. However, it is recommended to
0037  * use the installed uhid.h if available.
0038  */
0039 
0040 #include <errno.h>
0041 #include <fcntl.h>
0042 #include <poll.h>
0043 #include <stdbool.h>
0044 #include <stdio.h>
0045 #include <stdlib.h>
0046 #include <string.h>
0047 #include <termios.h>
0048 #include <unistd.h>
0049 #include <linux/uhid.h>
0050 
0051 /*
0052  * HID Report Desciptor
0053  * We emulate a basic 3 button mouse with wheel and 3 keyboard LEDs. This is
0054  * the report-descriptor as the kernel will parse it:
0055  *
0056  * INPUT(1)[INPUT]
0057  *   Field(0)
0058  *     Physical(GenericDesktop.Pointer)
0059  *     Application(GenericDesktop.Mouse)
0060  *     Usage(3)
0061  *       Button.0001
0062  *       Button.0002
0063  *       Button.0003
0064  *     Logical Minimum(0)
0065  *     Logical Maximum(1)
0066  *     Report Size(1)
0067  *     Report Count(3)
0068  *     Report Offset(0)
0069  *     Flags( Variable Absolute )
0070  *   Field(1)
0071  *     Physical(GenericDesktop.Pointer)
0072  *     Application(GenericDesktop.Mouse)
0073  *     Usage(3)
0074  *       GenericDesktop.X
0075  *       GenericDesktop.Y
0076  *       GenericDesktop.Wheel
0077  *     Logical Minimum(-128)
0078  *     Logical Maximum(127)
0079  *     Report Size(8)
0080  *     Report Count(3)
0081  *     Report Offset(8)
0082  *     Flags( Variable Relative )
0083  * OUTPUT(2)[OUTPUT]
0084  *   Field(0)
0085  *     Application(GenericDesktop.Keyboard)
0086  *     Usage(3)
0087  *       LED.NumLock
0088  *       LED.CapsLock
0089  *       LED.ScrollLock
0090  *     Logical Minimum(0)
0091  *     Logical Maximum(1)
0092  *     Report Size(1)
0093  *     Report Count(3)
0094  *     Report Offset(0)
0095  *     Flags( Variable Absolute )
0096  *
0097  * This is the mapping that we expect:
0098  *   Button.0001 ---> Key.LeftBtn
0099  *   Button.0002 ---> Key.RightBtn
0100  *   Button.0003 ---> Key.MiddleBtn
0101  *   GenericDesktop.X ---> Relative.X
0102  *   GenericDesktop.Y ---> Relative.Y
0103  *   GenericDesktop.Wheel ---> Relative.Wheel
0104  *   LED.NumLock ---> LED.NumLock
0105  *   LED.CapsLock ---> LED.CapsLock
0106  *   LED.ScrollLock ---> LED.ScrollLock
0107  *
0108  * This information can be verified by reading /sys/kernel/debug/hid/<dev>/rdesc
0109  * This file should print the same information as showed above.
0110  */
0111 
0112 static unsigned char rdesc[] = {
0113     0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0114     0x09, 0x02, /* USAGE (Mouse) */
0115     0xa1, 0x01, /* COLLECTION (Application) */
0116     0x09, 0x01,     /* USAGE (Pointer) */
0117     0xa1, 0x00,     /* COLLECTION (Physical) */
0118     0x85, 0x01,         /* REPORT_ID (1) */
0119     0x05, 0x09,         /* USAGE_PAGE (Button) */
0120     0x19, 0x01,         /* USAGE_MINIMUM (Button 1) */
0121     0x29, 0x03,         /* USAGE_MAXIMUM (Button 3) */
0122     0x15, 0x00,         /* LOGICAL_MINIMUM (0) */
0123     0x25, 0x01,         /* LOGICAL_MAXIMUM (1) */
0124     0x95, 0x03,         /* REPORT_COUNT (3) */
0125     0x75, 0x01,         /* REPORT_SIZE (1) */
0126     0x81, 0x02,         /* INPUT (Data,Var,Abs) */
0127     0x95, 0x01,         /* REPORT_COUNT (1) */
0128     0x75, 0x05,         /* REPORT_SIZE (5) */
0129     0x81, 0x01,         /* INPUT (Cnst,Var,Abs) */
0130     0x05, 0x01,         /* USAGE_PAGE (Generic Desktop) */
0131     0x09, 0x30,         /* USAGE (X) */
0132     0x09, 0x31,         /* USAGE (Y) */
0133     0x09, 0x38,         /* USAGE (WHEEL) */
0134     0x15, 0x81,         /* LOGICAL_MINIMUM (-127) */
0135     0x25, 0x7f,         /* LOGICAL_MAXIMUM (127) */
0136     0x75, 0x08,         /* REPORT_SIZE (8) */
0137     0x95, 0x03,         /* REPORT_COUNT (3) */
0138     0x81, 0x06,         /* INPUT (Data,Var,Rel) */
0139     0xc0,           /* END_COLLECTION */
0140     0xc0,       /* END_COLLECTION */
0141     0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0142     0x09, 0x06, /* USAGE (Keyboard) */
0143     0xa1, 0x01, /* COLLECTION (Application) */
0144     0x85, 0x02,     /* REPORT_ID (2) */
0145     0x05, 0x08,     /* USAGE_PAGE (Led) */
0146     0x19, 0x01,     /* USAGE_MINIMUM (1) */
0147     0x29, 0x03,     /* USAGE_MAXIMUM (3) */
0148     0x15, 0x00,     /* LOGICAL_MINIMUM (0) */
0149     0x25, 0x01,     /* LOGICAL_MAXIMUM (1) */
0150     0x95, 0x03,     /* REPORT_COUNT (3) */
0151     0x75, 0x01,     /* REPORT_SIZE (1) */
0152     0x91, 0x02,     /* Output (Data,Var,Abs) */
0153     0x95, 0x01,     /* REPORT_COUNT (1) */
0154     0x75, 0x05,     /* REPORT_SIZE (5) */
0155     0x91, 0x01,     /* Output (Cnst,Var,Abs) */
0156     0xc0,       /* END_COLLECTION */
0157 };
0158 
0159 static int uhid_write(int fd, const struct uhid_event *ev)
0160 {
0161     ssize_t ret;
0162 
0163     ret = write(fd, ev, sizeof(*ev));
0164     if (ret < 0) {
0165         fprintf(stderr, "Cannot write to uhid: %m\n");
0166         return -errno;
0167     } else if (ret != sizeof(*ev)) {
0168         fprintf(stderr, "Wrong size written to uhid: %zd != %zu\n",
0169             ret, sizeof(ev));
0170         return -EFAULT;
0171     } else {
0172         return 0;
0173     }
0174 }
0175 
0176 static int create(int fd)
0177 {
0178     struct uhid_event ev;
0179 
0180     memset(&ev, 0, sizeof(ev));
0181     ev.type = UHID_CREATE;
0182     strcpy((char*)ev.u.create.name, "test-uhid-device");
0183     ev.u.create.rd_data = rdesc;
0184     ev.u.create.rd_size = sizeof(rdesc);
0185     ev.u.create.bus = BUS_USB;
0186     ev.u.create.vendor = 0x15d9;
0187     ev.u.create.product = 0x0a37;
0188     ev.u.create.version = 0;
0189     ev.u.create.country = 0;
0190 
0191     return uhid_write(fd, &ev);
0192 }
0193 
0194 static void destroy(int fd)
0195 {
0196     struct uhid_event ev;
0197 
0198     memset(&ev, 0, sizeof(ev));
0199     ev.type = UHID_DESTROY;
0200 
0201     uhid_write(fd, &ev);
0202 }
0203 
0204 /* This parses raw output reports sent by the kernel to the device. A normal
0205  * uhid program shouldn't do this but instead just forward the raw report.
0206  * However, for ducomentational purposes, we try to detect LED events here and
0207  * print debug messages for it. */
0208 static void handle_output(struct uhid_event *ev)
0209 {
0210     /* LED messages are adverised via OUTPUT reports; ignore the rest */
0211     if (ev->u.output.rtype != UHID_OUTPUT_REPORT)
0212         return;
0213     /* LED reports have length 2 bytes */
0214     if (ev->u.output.size != 2)
0215         return;
0216     /* first byte is report-id which is 0x02 for LEDs in our rdesc */
0217     if (ev->u.output.data[0] != 0x2)
0218         return;
0219 
0220     /* print flags payload */
0221     fprintf(stderr, "LED output report received with flags %x\n",
0222         ev->u.output.data[1]);
0223 }
0224 
0225 static int event(int fd)
0226 {
0227     struct uhid_event ev;
0228     ssize_t ret;
0229 
0230     memset(&ev, 0, sizeof(ev));
0231     ret = read(fd, &ev, sizeof(ev));
0232     if (ret == 0) {
0233         fprintf(stderr, "Read HUP on uhid-cdev\n");
0234         return -EFAULT;
0235     } else if (ret < 0) {
0236         fprintf(stderr, "Cannot read uhid-cdev: %m\n");
0237         return -errno;
0238     } else if (ret != sizeof(ev)) {
0239         fprintf(stderr, "Invalid size read from uhid-dev: %zd != %zu\n",
0240             ret, sizeof(ev));
0241         return -EFAULT;
0242     }
0243 
0244     switch (ev.type) {
0245     case UHID_START:
0246         fprintf(stderr, "UHID_START from uhid-dev\n");
0247         break;
0248     case UHID_STOP:
0249         fprintf(stderr, "UHID_STOP from uhid-dev\n");
0250         break;
0251     case UHID_OPEN:
0252         fprintf(stderr, "UHID_OPEN from uhid-dev\n");
0253         break;
0254     case UHID_CLOSE:
0255         fprintf(stderr, "UHID_CLOSE from uhid-dev\n");
0256         break;
0257     case UHID_OUTPUT:
0258         fprintf(stderr, "UHID_OUTPUT from uhid-dev\n");
0259         handle_output(&ev);
0260         break;
0261     case UHID_OUTPUT_EV:
0262         fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n");
0263         break;
0264     default:
0265         fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type);
0266     }
0267 
0268     return 0;
0269 }
0270 
0271 static bool btn1_down;
0272 static bool btn2_down;
0273 static bool btn3_down;
0274 static signed char abs_hor;
0275 static signed char abs_ver;
0276 static signed char wheel;
0277 
0278 static int send_event(int fd)
0279 {
0280     struct uhid_event ev;
0281 
0282     memset(&ev, 0, sizeof(ev));
0283     ev.type = UHID_INPUT;
0284     ev.u.input.size = 5;
0285 
0286     ev.u.input.data[0] = 0x1;
0287     if (btn1_down)
0288         ev.u.input.data[1] |= 0x1;
0289     if (btn2_down)
0290         ev.u.input.data[1] |= 0x2;
0291     if (btn3_down)
0292         ev.u.input.data[1] |= 0x4;
0293 
0294     ev.u.input.data[2] = abs_hor;
0295     ev.u.input.data[3] = abs_ver;
0296     ev.u.input.data[4] = wheel;
0297 
0298     return uhid_write(fd, &ev);
0299 }
0300 
0301 static int keyboard(int fd)
0302 {
0303     char buf[128];
0304     ssize_t ret, i;
0305 
0306     ret = read(STDIN_FILENO, buf, sizeof(buf));
0307     if (ret == 0) {
0308         fprintf(stderr, "Read HUP on stdin\n");
0309         return -EFAULT;
0310     } else if (ret < 0) {
0311         fprintf(stderr, "Cannot read stdin: %m\n");
0312         return -errno;
0313     }
0314 
0315     for (i = 0; i < ret; ++i) {
0316         switch (buf[i]) {
0317         case '1':
0318             btn1_down = !btn1_down;
0319             ret = send_event(fd);
0320             if (ret)
0321                 return ret;
0322             break;
0323         case '2':
0324             btn2_down = !btn2_down;
0325             ret = send_event(fd);
0326             if (ret)
0327                 return ret;
0328             break;
0329         case '3':
0330             btn3_down = !btn3_down;
0331             ret = send_event(fd);
0332             if (ret)
0333                 return ret;
0334             break;
0335         case 'a':
0336             abs_hor = -20;
0337             ret = send_event(fd);
0338             abs_hor = 0;
0339             if (ret)
0340                 return ret;
0341             break;
0342         case 'd':
0343             abs_hor = 20;
0344             ret = send_event(fd);
0345             abs_hor = 0;
0346             if (ret)
0347                 return ret;
0348             break;
0349         case 'w':
0350             abs_ver = -20;
0351             ret = send_event(fd);
0352             abs_ver = 0;
0353             if (ret)
0354                 return ret;
0355             break;
0356         case 's':
0357             abs_ver = 20;
0358             ret = send_event(fd);
0359             abs_ver = 0;
0360             if (ret)
0361                 return ret;
0362             break;
0363         case 'r':
0364             wheel = 1;
0365             ret = send_event(fd);
0366             wheel = 0;
0367             if (ret)
0368                 return ret;
0369             break;
0370         case 'f':
0371             wheel = -1;
0372             ret = send_event(fd);
0373             wheel = 0;
0374             if (ret)
0375                 return ret;
0376             break;
0377         case 'q':
0378             return -ECANCELED;
0379         default:
0380             fprintf(stderr, "Invalid input: %c\n", buf[i]);
0381         }
0382     }
0383 
0384     return 0;
0385 }
0386 
0387 int main(int argc, char **argv)
0388 {
0389     int fd;
0390     const char *path = "/dev/uhid";
0391     struct pollfd pfds[2];
0392     int ret;
0393     struct termios state;
0394 
0395     ret = tcgetattr(STDIN_FILENO, &state);
0396     if (ret) {
0397         fprintf(stderr, "Cannot get tty state\n");
0398     } else {
0399         state.c_lflag &= ~ICANON;
0400         state.c_cc[VMIN] = 1;
0401         ret = tcsetattr(STDIN_FILENO, TCSANOW, &state);
0402         if (ret)
0403             fprintf(stderr, "Cannot set tty state\n");
0404     }
0405 
0406     if (argc >= 2) {
0407         if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
0408             fprintf(stderr, "Usage: %s [%s]\n", argv[0], path);
0409             return EXIT_SUCCESS;
0410         } else {
0411             path = argv[1];
0412         }
0413     }
0414 
0415     fprintf(stderr, "Open uhid-cdev %s\n", path);
0416     fd = open(path, O_RDWR | O_CLOEXEC);
0417     if (fd < 0) {
0418         fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path);
0419         return EXIT_FAILURE;
0420     }
0421 
0422     fprintf(stderr, "Create uhid device\n");
0423     ret = create(fd);
0424     if (ret) {
0425         close(fd);
0426         return EXIT_FAILURE;
0427     }
0428 
0429     pfds[0].fd = STDIN_FILENO;
0430     pfds[0].events = POLLIN;
0431     pfds[1].fd = fd;
0432     pfds[1].events = POLLIN;
0433 
0434     fprintf(stderr, "Press 'q' to quit...\n");
0435     while (1) {
0436         ret = poll(pfds, 2, -1);
0437         if (ret < 0) {
0438             fprintf(stderr, "Cannot poll for fds: %m\n");
0439             break;
0440         }
0441         if (pfds[0].revents & POLLHUP) {
0442             fprintf(stderr, "Received HUP on stdin\n");
0443             break;
0444         }
0445         if (pfds[1].revents & POLLHUP) {
0446             fprintf(stderr, "Received HUP on uhid-cdev\n");
0447             break;
0448         }
0449 
0450         if (pfds[0].revents & POLLIN) {
0451             ret = keyboard(fd);
0452             if (ret)
0453                 break;
0454         }
0455         if (pfds[1].revents & POLLIN) {
0456             ret = event(fd);
0457             if (ret)
0458                 break;
0459         }
0460     }
0461 
0462     fprintf(stderr, "Destroy uhid device\n");
0463     destroy(fd);
0464     return EXIT_SUCCESS;
0465 }