18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * UHID Example
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * The code may be used by anyone for any purpose,
88c2ecf20Sopenharmony_ci * and can serve as a starting point for developing
98c2ecf20Sopenharmony_ci * applications using uhid.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/*
138c2ecf20Sopenharmony_ci * UHID Example
148c2ecf20Sopenharmony_ci * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this
158c2ecf20Sopenharmony_ci * program as root and then use the following keys to control the mouse:
168c2ecf20Sopenharmony_ci *   q: Quit the application
178c2ecf20Sopenharmony_ci *   1: Toggle left button (down, up, ...)
188c2ecf20Sopenharmony_ci *   2: Toggle right button
198c2ecf20Sopenharmony_ci *   3: Toggle middle button
208c2ecf20Sopenharmony_ci *   a: Move mouse left
218c2ecf20Sopenharmony_ci *   d: Move mouse right
228c2ecf20Sopenharmony_ci *   w: Move mouse up
238c2ecf20Sopenharmony_ci *   s: Move mouse down
248c2ecf20Sopenharmony_ci *   r: Move wheel up
258c2ecf20Sopenharmony_ci *   f: Move wheel down
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * Additionally to 3 button mouse, 3 keyboard LEDs are also supported (LED_NUML,
288c2ecf20Sopenharmony_ci * LED_CAPSL and LED_SCROLLL). The device doesn't generate any related keyboard
298c2ecf20Sopenharmony_ci * events, though. You need to manually write the EV_LED/LED_XY/1 activation
308c2ecf20Sopenharmony_ci * input event to the evdev device to see it being sent to this device.
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * If uhid is not available as /dev/uhid, then you can pass a different path as
338c2ecf20Sopenharmony_ci * first argument.
348c2ecf20Sopenharmony_ci * If <linux/uhid.h> is not installed in /usr, then compile this with:
358c2ecf20Sopenharmony_ci *   gcc -o ./uhid_test -Wall -I./include ./samples/uhid/uhid-example.c
368c2ecf20Sopenharmony_ci * And ignore the warning about kernel headers. However, it is recommended to
378c2ecf20Sopenharmony_ci * use the installed uhid.h if available.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include <errno.h>
418c2ecf20Sopenharmony_ci#include <fcntl.h>
428c2ecf20Sopenharmony_ci#include <poll.h>
438c2ecf20Sopenharmony_ci#include <stdbool.h>
448c2ecf20Sopenharmony_ci#include <stdio.h>
458c2ecf20Sopenharmony_ci#include <stdlib.h>
468c2ecf20Sopenharmony_ci#include <string.h>
478c2ecf20Sopenharmony_ci#include <termios.h>
488c2ecf20Sopenharmony_ci#include <unistd.h>
498c2ecf20Sopenharmony_ci#include <linux/uhid.h>
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/*
528c2ecf20Sopenharmony_ci * HID Report Desciptor
538c2ecf20Sopenharmony_ci * We emulate a basic 3 button mouse with wheel and 3 keyboard LEDs. This is
548c2ecf20Sopenharmony_ci * the report-descriptor as the kernel will parse it:
558c2ecf20Sopenharmony_ci *
568c2ecf20Sopenharmony_ci * INPUT(1)[INPUT]
578c2ecf20Sopenharmony_ci *   Field(0)
588c2ecf20Sopenharmony_ci *     Physical(GenericDesktop.Pointer)
598c2ecf20Sopenharmony_ci *     Application(GenericDesktop.Mouse)
608c2ecf20Sopenharmony_ci *     Usage(3)
618c2ecf20Sopenharmony_ci *       Button.0001
628c2ecf20Sopenharmony_ci *       Button.0002
638c2ecf20Sopenharmony_ci *       Button.0003
648c2ecf20Sopenharmony_ci *     Logical Minimum(0)
658c2ecf20Sopenharmony_ci *     Logical Maximum(1)
668c2ecf20Sopenharmony_ci *     Report Size(1)
678c2ecf20Sopenharmony_ci *     Report Count(3)
688c2ecf20Sopenharmony_ci *     Report Offset(0)
698c2ecf20Sopenharmony_ci *     Flags( Variable Absolute )
708c2ecf20Sopenharmony_ci *   Field(1)
718c2ecf20Sopenharmony_ci *     Physical(GenericDesktop.Pointer)
728c2ecf20Sopenharmony_ci *     Application(GenericDesktop.Mouse)
738c2ecf20Sopenharmony_ci *     Usage(3)
748c2ecf20Sopenharmony_ci *       GenericDesktop.X
758c2ecf20Sopenharmony_ci *       GenericDesktop.Y
768c2ecf20Sopenharmony_ci *       GenericDesktop.Wheel
778c2ecf20Sopenharmony_ci *     Logical Minimum(-128)
788c2ecf20Sopenharmony_ci *     Logical Maximum(127)
798c2ecf20Sopenharmony_ci *     Report Size(8)
808c2ecf20Sopenharmony_ci *     Report Count(3)
818c2ecf20Sopenharmony_ci *     Report Offset(8)
828c2ecf20Sopenharmony_ci *     Flags( Variable Relative )
838c2ecf20Sopenharmony_ci * OUTPUT(2)[OUTPUT]
848c2ecf20Sopenharmony_ci *   Field(0)
858c2ecf20Sopenharmony_ci *     Application(GenericDesktop.Keyboard)
868c2ecf20Sopenharmony_ci *     Usage(3)
878c2ecf20Sopenharmony_ci *       LED.NumLock
888c2ecf20Sopenharmony_ci *       LED.CapsLock
898c2ecf20Sopenharmony_ci *       LED.ScrollLock
908c2ecf20Sopenharmony_ci *     Logical Minimum(0)
918c2ecf20Sopenharmony_ci *     Logical Maximum(1)
928c2ecf20Sopenharmony_ci *     Report Size(1)
938c2ecf20Sopenharmony_ci *     Report Count(3)
948c2ecf20Sopenharmony_ci *     Report Offset(0)
958c2ecf20Sopenharmony_ci *     Flags( Variable Absolute )
968c2ecf20Sopenharmony_ci *
978c2ecf20Sopenharmony_ci * This is the mapping that we expect:
988c2ecf20Sopenharmony_ci *   Button.0001 ---> Key.LeftBtn
998c2ecf20Sopenharmony_ci *   Button.0002 ---> Key.RightBtn
1008c2ecf20Sopenharmony_ci *   Button.0003 ---> Key.MiddleBtn
1018c2ecf20Sopenharmony_ci *   GenericDesktop.X ---> Relative.X
1028c2ecf20Sopenharmony_ci *   GenericDesktop.Y ---> Relative.Y
1038c2ecf20Sopenharmony_ci *   GenericDesktop.Wheel ---> Relative.Wheel
1048c2ecf20Sopenharmony_ci *   LED.NumLock ---> LED.NumLock
1058c2ecf20Sopenharmony_ci *   LED.CapsLock ---> LED.CapsLock
1068c2ecf20Sopenharmony_ci *   LED.ScrollLock ---> LED.ScrollLock
1078c2ecf20Sopenharmony_ci *
1088c2ecf20Sopenharmony_ci * This information can be verified by reading /sys/kernel/debug/hid/<dev>/rdesc
1098c2ecf20Sopenharmony_ci * This file should print the same information as showed above.
1108c2ecf20Sopenharmony_ci */
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic unsigned char rdesc[] = {
1138c2ecf20Sopenharmony_ci	0x05, 0x01,	/* USAGE_PAGE (Generic Desktop) */
1148c2ecf20Sopenharmony_ci	0x09, 0x02,	/* USAGE (Mouse) */
1158c2ecf20Sopenharmony_ci	0xa1, 0x01,	/* COLLECTION (Application) */
1168c2ecf20Sopenharmony_ci	0x09, 0x01,		/* USAGE (Pointer) */
1178c2ecf20Sopenharmony_ci	0xa1, 0x00,		/* COLLECTION (Physical) */
1188c2ecf20Sopenharmony_ci	0x85, 0x01,			/* REPORT_ID (1) */
1198c2ecf20Sopenharmony_ci	0x05, 0x09,			/* USAGE_PAGE (Button) */
1208c2ecf20Sopenharmony_ci	0x19, 0x01,			/* USAGE_MINIMUM (Button 1) */
1218c2ecf20Sopenharmony_ci	0x29, 0x03,			/* USAGE_MAXIMUM (Button 3) */
1228c2ecf20Sopenharmony_ci	0x15, 0x00,			/* LOGICAL_MINIMUM (0) */
1238c2ecf20Sopenharmony_ci	0x25, 0x01,			/* LOGICAL_MAXIMUM (1) */
1248c2ecf20Sopenharmony_ci	0x95, 0x03,			/* REPORT_COUNT (3) */
1258c2ecf20Sopenharmony_ci	0x75, 0x01,			/* REPORT_SIZE (1) */
1268c2ecf20Sopenharmony_ci	0x81, 0x02,			/* INPUT (Data,Var,Abs) */
1278c2ecf20Sopenharmony_ci	0x95, 0x01,			/* REPORT_COUNT (1) */
1288c2ecf20Sopenharmony_ci	0x75, 0x05,			/* REPORT_SIZE (5) */
1298c2ecf20Sopenharmony_ci	0x81, 0x01,			/* INPUT (Cnst,Var,Abs) */
1308c2ecf20Sopenharmony_ci	0x05, 0x01,			/* USAGE_PAGE (Generic Desktop) */
1318c2ecf20Sopenharmony_ci	0x09, 0x30,			/* USAGE (X) */
1328c2ecf20Sopenharmony_ci	0x09, 0x31,			/* USAGE (Y) */
1338c2ecf20Sopenharmony_ci	0x09, 0x38,			/* USAGE (WHEEL) */
1348c2ecf20Sopenharmony_ci	0x15, 0x81,			/* LOGICAL_MINIMUM (-127) */
1358c2ecf20Sopenharmony_ci	0x25, 0x7f,			/* LOGICAL_MAXIMUM (127) */
1368c2ecf20Sopenharmony_ci	0x75, 0x08,			/* REPORT_SIZE (8) */
1378c2ecf20Sopenharmony_ci	0x95, 0x03,			/* REPORT_COUNT (3) */
1388c2ecf20Sopenharmony_ci	0x81, 0x06,			/* INPUT (Data,Var,Rel) */
1398c2ecf20Sopenharmony_ci	0xc0,			/* END_COLLECTION */
1408c2ecf20Sopenharmony_ci	0xc0,		/* END_COLLECTION */
1418c2ecf20Sopenharmony_ci	0x05, 0x01,	/* USAGE_PAGE (Generic Desktop) */
1428c2ecf20Sopenharmony_ci	0x09, 0x06,	/* USAGE (Keyboard) */
1438c2ecf20Sopenharmony_ci	0xa1, 0x01,	/* COLLECTION (Application) */
1448c2ecf20Sopenharmony_ci	0x85, 0x02,		/* REPORT_ID (2) */
1458c2ecf20Sopenharmony_ci	0x05, 0x08,		/* USAGE_PAGE (Led) */
1468c2ecf20Sopenharmony_ci	0x19, 0x01,		/* USAGE_MINIMUM (1) */
1478c2ecf20Sopenharmony_ci	0x29, 0x03,		/* USAGE_MAXIMUM (3) */
1488c2ecf20Sopenharmony_ci	0x15, 0x00,		/* LOGICAL_MINIMUM (0) */
1498c2ecf20Sopenharmony_ci	0x25, 0x01,		/* LOGICAL_MAXIMUM (1) */
1508c2ecf20Sopenharmony_ci	0x95, 0x03,		/* REPORT_COUNT (3) */
1518c2ecf20Sopenharmony_ci	0x75, 0x01,		/* REPORT_SIZE (1) */
1528c2ecf20Sopenharmony_ci	0x91, 0x02,		/* Output (Data,Var,Abs) */
1538c2ecf20Sopenharmony_ci	0x95, 0x01,		/* REPORT_COUNT (1) */
1548c2ecf20Sopenharmony_ci	0x75, 0x05,		/* REPORT_SIZE (5) */
1558c2ecf20Sopenharmony_ci	0x91, 0x01,		/* Output (Cnst,Var,Abs) */
1568c2ecf20Sopenharmony_ci	0xc0,		/* END_COLLECTION */
1578c2ecf20Sopenharmony_ci};
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int uhid_write(int fd, const struct uhid_event *ev)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	ssize_t ret;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	ret = write(fd, ev, sizeof(*ev));
1648c2ecf20Sopenharmony_ci	if (ret < 0) {
1658c2ecf20Sopenharmony_ci		fprintf(stderr, "Cannot write to uhid: %m\n");
1668c2ecf20Sopenharmony_ci		return -errno;
1678c2ecf20Sopenharmony_ci	} else if (ret != sizeof(*ev)) {
1688c2ecf20Sopenharmony_ci		fprintf(stderr, "Wrong size written to uhid: %zd != %zu\n",
1698c2ecf20Sopenharmony_ci			ret, sizeof(ev));
1708c2ecf20Sopenharmony_ci		return -EFAULT;
1718c2ecf20Sopenharmony_ci	} else {
1728c2ecf20Sopenharmony_ci		return 0;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int create(int fd)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct uhid_event ev;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	memset(&ev, 0, sizeof(ev));
1818c2ecf20Sopenharmony_ci	ev.type = UHID_CREATE;
1828c2ecf20Sopenharmony_ci	strcpy((char*)ev.u.create.name, "test-uhid-device");
1838c2ecf20Sopenharmony_ci	ev.u.create.rd_data = rdesc;
1848c2ecf20Sopenharmony_ci	ev.u.create.rd_size = sizeof(rdesc);
1858c2ecf20Sopenharmony_ci	ev.u.create.bus = BUS_USB;
1868c2ecf20Sopenharmony_ci	ev.u.create.vendor = 0x15d9;
1878c2ecf20Sopenharmony_ci	ev.u.create.product = 0x0a37;
1888c2ecf20Sopenharmony_ci	ev.u.create.version = 0;
1898c2ecf20Sopenharmony_ci	ev.u.create.country = 0;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return uhid_write(fd, &ev);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic void destroy(int fd)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct uhid_event ev;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	memset(&ev, 0, sizeof(ev));
1998c2ecf20Sopenharmony_ci	ev.type = UHID_DESTROY;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	uhid_write(fd, &ev);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/* This parses raw output reports sent by the kernel to the device. A normal
2058c2ecf20Sopenharmony_ci * uhid program shouldn't do this but instead just forward the raw report.
2068c2ecf20Sopenharmony_ci * However, for ducomentational purposes, we try to detect LED events here and
2078c2ecf20Sopenharmony_ci * print debug messages for it. */
2088c2ecf20Sopenharmony_cistatic void handle_output(struct uhid_event *ev)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	/* LED messages are adverised via OUTPUT reports; ignore the rest */
2118c2ecf20Sopenharmony_ci	if (ev->u.output.rtype != UHID_OUTPUT_REPORT)
2128c2ecf20Sopenharmony_ci		return;
2138c2ecf20Sopenharmony_ci	/* LED reports have length 2 bytes */
2148c2ecf20Sopenharmony_ci	if (ev->u.output.size != 2)
2158c2ecf20Sopenharmony_ci		return;
2168c2ecf20Sopenharmony_ci	/* first byte is report-id which is 0x02 for LEDs in our rdesc */
2178c2ecf20Sopenharmony_ci	if (ev->u.output.data[0] != 0x2)
2188c2ecf20Sopenharmony_ci		return;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/* print flags payload */
2218c2ecf20Sopenharmony_ci	fprintf(stderr, "LED output report received with flags %x\n",
2228c2ecf20Sopenharmony_ci		ev->u.output.data[1]);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int event(int fd)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct uhid_event ev;
2288c2ecf20Sopenharmony_ci	ssize_t ret;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	memset(&ev, 0, sizeof(ev));
2318c2ecf20Sopenharmony_ci	ret = read(fd, &ev, sizeof(ev));
2328c2ecf20Sopenharmony_ci	if (ret == 0) {
2338c2ecf20Sopenharmony_ci		fprintf(stderr, "Read HUP on uhid-cdev\n");
2348c2ecf20Sopenharmony_ci		return -EFAULT;
2358c2ecf20Sopenharmony_ci	} else if (ret < 0) {
2368c2ecf20Sopenharmony_ci		fprintf(stderr, "Cannot read uhid-cdev: %m\n");
2378c2ecf20Sopenharmony_ci		return -errno;
2388c2ecf20Sopenharmony_ci	} else if (ret != sizeof(ev)) {
2398c2ecf20Sopenharmony_ci		fprintf(stderr, "Invalid size read from uhid-dev: %zd != %zu\n",
2408c2ecf20Sopenharmony_ci			ret, sizeof(ev));
2418c2ecf20Sopenharmony_ci		return -EFAULT;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	switch (ev.type) {
2458c2ecf20Sopenharmony_ci	case UHID_START:
2468c2ecf20Sopenharmony_ci		fprintf(stderr, "UHID_START from uhid-dev\n");
2478c2ecf20Sopenharmony_ci		break;
2488c2ecf20Sopenharmony_ci	case UHID_STOP:
2498c2ecf20Sopenharmony_ci		fprintf(stderr, "UHID_STOP from uhid-dev\n");
2508c2ecf20Sopenharmony_ci		break;
2518c2ecf20Sopenharmony_ci	case UHID_OPEN:
2528c2ecf20Sopenharmony_ci		fprintf(stderr, "UHID_OPEN from uhid-dev\n");
2538c2ecf20Sopenharmony_ci		break;
2548c2ecf20Sopenharmony_ci	case UHID_CLOSE:
2558c2ecf20Sopenharmony_ci		fprintf(stderr, "UHID_CLOSE from uhid-dev\n");
2568c2ecf20Sopenharmony_ci		break;
2578c2ecf20Sopenharmony_ci	case UHID_OUTPUT:
2588c2ecf20Sopenharmony_ci		fprintf(stderr, "UHID_OUTPUT from uhid-dev\n");
2598c2ecf20Sopenharmony_ci		handle_output(&ev);
2608c2ecf20Sopenharmony_ci		break;
2618c2ecf20Sopenharmony_ci	case UHID_OUTPUT_EV:
2628c2ecf20Sopenharmony_ci		fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n");
2638c2ecf20Sopenharmony_ci		break;
2648c2ecf20Sopenharmony_ci	default:
2658c2ecf20Sopenharmony_ci		fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type);
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	return 0;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic bool btn1_down;
2728c2ecf20Sopenharmony_cistatic bool btn2_down;
2738c2ecf20Sopenharmony_cistatic bool btn3_down;
2748c2ecf20Sopenharmony_cistatic signed char abs_hor;
2758c2ecf20Sopenharmony_cistatic signed char abs_ver;
2768c2ecf20Sopenharmony_cistatic signed char wheel;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int send_event(int fd)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	struct uhid_event ev;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	memset(&ev, 0, sizeof(ev));
2838c2ecf20Sopenharmony_ci	ev.type = UHID_INPUT;
2848c2ecf20Sopenharmony_ci	ev.u.input.size = 5;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	ev.u.input.data[0] = 0x1;
2878c2ecf20Sopenharmony_ci	if (btn1_down)
2888c2ecf20Sopenharmony_ci		ev.u.input.data[1] |= 0x1;
2898c2ecf20Sopenharmony_ci	if (btn2_down)
2908c2ecf20Sopenharmony_ci		ev.u.input.data[1] |= 0x2;
2918c2ecf20Sopenharmony_ci	if (btn3_down)
2928c2ecf20Sopenharmony_ci		ev.u.input.data[1] |= 0x4;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	ev.u.input.data[2] = abs_hor;
2958c2ecf20Sopenharmony_ci	ev.u.input.data[3] = abs_ver;
2968c2ecf20Sopenharmony_ci	ev.u.input.data[4] = wheel;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return uhid_write(fd, &ev);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic int keyboard(int fd)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	char buf[128];
3048c2ecf20Sopenharmony_ci	ssize_t ret, i;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	ret = read(STDIN_FILENO, buf, sizeof(buf));
3078c2ecf20Sopenharmony_ci	if (ret == 0) {
3088c2ecf20Sopenharmony_ci		fprintf(stderr, "Read HUP on stdin\n");
3098c2ecf20Sopenharmony_ci		return -EFAULT;
3108c2ecf20Sopenharmony_ci	} else if (ret < 0) {
3118c2ecf20Sopenharmony_ci		fprintf(stderr, "Cannot read stdin: %m\n");
3128c2ecf20Sopenharmony_ci		return -errno;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	for (i = 0; i < ret; ++i) {
3168c2ecf20Sopenharmony_ci		switch (buf[i]) {
3178c2ecf20Sopenharmony_ci		case '1':
3188c2ecf20Sopenharmony_ci			btn1_down = !btn1_down;
3198c2ecf20Sopenharmony_ci			ret = send_event(fd);
3208c2ecf20Sopenharmony_ci			if (ret)
3218c2ecf20Sopenharmony_ci				return ret;
3228c2ecf20Sopenharmony_ci			break;
3238c2ecf20Sopenharmony_ci		case '2':
3248c2ecf20Sopenharmony_ci			btn2_down = !btn2_down;
3258c2ecf20Sopenharmony_ci			ret = send_event(fd);
3268c2ecf20Sopenharmony_ci			if (ret)
3278c2ecf20Sopenharmony_ci				return ret;
3288c2ecf20Sopenharmony_ci			break;
3298c2ecf20Sopenharmony_ci		case '3':
3308c2ecf20Sopenharmony_ci			btn3_down = !btn3_down;
3318c2ecf20Sopenharmony_ci			ret = send_event(fd);
3328c2ecf20Sopenharmony_ci			if (ret)
3338c2ecf20Sopenharmony_ci				return ret;
3348c2ecf20Sopenharmony_ci			break;
3358c2ecf20Sopenharmony_ci		case 'a':
3368c2ecf20Sopenharmony_ci			abs_hor = -20;
3378c2ecf20Sopenharmony_ci			ret = send_event(fd);
3388c2ecf20Sopenharmony_ci			abs_hor = 0;
3398c2ecf20Sopenharmony_ci			if (ret)
3408c2ecf20Sopenharmony_ci				return ret;
3418c2ecf20Sopenharmony_ci			break;
3428c2ecf20Sopenharmony_ci		case 'd':
3438c2ecf20Sopenharmony_ci			abs_hor = 20;
3448c2ecf20Sopenharmony_ci			ret = send_event(fd);
3458c2ecf20Sopenharmony_ci			abs_hor = 0;
3468c2ecf20Sopenharmony_ci			if (ret)
3478c2ecf20Sopenharmony_ci				return ret;
3488c2ecf20Sopenharmony_ci			break;
3498c2ecf20Sopenharmony_ci		case 'w':
3508c2ecf20Sopenharmony_ci			abs_ver = -20;
3518c2ecf20Sopenharmony_ci			ret = send_event(fd);
3528c2ecf20Sopenharmony_ci			abs_ver = 0;
3538c2ecf20Sopenharmony_ci			if (ret)
3548c2ecf20Sopenharmony_ci				return ret;
3558c2ecf20Sopenharmony_ci			break;
3568c2ecf20Sopenharmony_ci		case 's':
3578c2ecf20Sopenharmony_ci			abs_ver = 20;
3588c2ecf20Sopenharmony_ci			ret = send_event(fd);
3598c2ecf20Sopenharmony_ci			abs_ver = 0;
3608c2ecf20Sopenharmony_ci			if (ret)
3618c2ecf20Sopenharmony_ci				return ret;
3628c2ecf20Sopenharmony_ci			break;
3638c2ecf20Sopenharmony_ci		case 'r':
3648c2ecf20Sopenharmony_ci			wheel = 1;
3658c2ecf20Sopenharmony_ci			ret = send_event(fd);
3668c2ecf20Sopenharmony_ci			wheel = 0;
3678c2ecf20Sopenharmony_ci			if (ret)
3688c2ecf20Sopenharmony_ci				return ret;
3698c2ecf20Sopenharmony_ci			break;
3708c2ecf20Sopenharmony_ci		case 'f':
3718c2ecf20Sopenharmony_ci			wheel = -1;
3728c2ecf20Sopenharmony_ci			ret = send_event(fd);
3738c2ecf20Sopenharmony_ci			wheel = 0;
3748c2ecf20Sopenharmony_ci			if (ret)
3758c2ecf20Sopenharmony_ci				return ret;
3768c2ecf20Sopenharmony_ci			break;
3778c2ecf20Sopenharmony_ci		case 'q':
3788c2ecf20Sopenharmony_ci			return -ECANCELED;
3798c2ecf20Sopenharmony_ci		default:
3808c2ecf20Sopenharmony_ci			fprintf(stderr, "Invalid input: %c\n", buf[i]);
3818c2ecf20Sopenharmony_ci		}
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return 0;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ciint main(int argc, char **argv)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	int fd;
3908c2ecf20Sopenharmony_ci	const char *path = "/dev/uhid";
3918c2ecf20Sopenharmony_ci	struct pollfd pfds[2];
3928c2ecf20Sopenharmony_ci	int ret;
3938c2ecf20Sopenharmony_ci	struct termios state;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ret = tcgetattr(STDIN_FILENO, &state);
3968c2ecf20Sopenharmony_ci	if (ret) {
3978c2ecf20Sopenharmony_ci		fprintf(stderr, "Cannot get tty state\n");
3988c2ecf20Sopenharmony_ci	} else {
3998c2ecf20Sopenharmony_ci		state.c_lflag &= ~ICANON;
4008c2ecf20Sopenharmony_ci		state.c_cc[VMIN] = 1;
4018c2ecf20Sopenharmony_ci		ret = tcsetattr(STDIN_FILENO, TCSANOW, &state);
4028c2ecf20Sopenharmony_ci		if (ret)
4038c2ecf20Sopenharmony_ci			fprintf(stderr, "Cannot set tty state\n");
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	if (argc >= 2) {
4078c2ecf20Sopenharmony_ci		if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
4088c2ecf20Sopenharmony_ci			fprintf(stderr, "Usage: %s [%s]\n", argv[0], path);
4098c2ecf20Sopenharmony_ci			return EXIT_SUCCESS;
4108c2ecf20Sopenharmony_ci		} else {
4118c2ecf20Sopenharmony_ci			path = argv[1];
4128c2ecf20Sopenharmony_ci		}
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	fprintf(stderr, "Open uhid-cdev %s\n", path);
4168c2ecf20Sopenharmony_ci	fd = open(path, O_RDWR | O_CLOEXEC);
4178c2ecf20Sopenharmony_ci	if (fd < 0) {
4188c2ecf20Sopenharmony_ci		fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path);
4198c2ecf20Sopenharmony_ci		return EXIT_FAILURE;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	fprintf(stderr, "Create uhid device\n");
4238c2ecf20Sopenharmony_ci	ret = create(fd);
4248c2ecf20Sopenharmony_ci	if (ret) {
4258c2ecf20Sopenharmony_ci		close(fd);
4268c2ecf20Sopenharmony_ci		return EXIT_FAILURE;
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	pfds[0].fd = STDIN_FILENO;
4308c2ecf20Sopenharmony_ci	pfds[0].events = POLLIN;
4318c2ecf20Sopenharmony_ci	pfds[1].fd = fd;
4328c2ecf20Sopenharmony_ci	pfds[1].events = POLLIN;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	fprintf(stderr, "Press 'q' to quit...\n");
4358c2ecf20Sopenharmony_ci	while (1) {
4368c2ecf20Sopenharmony_ci		ret = poll(pfds, 2, -1);
4378c2ecf20Sopenharmony_ci		if (ret < 0) {
4388c2ecf20Sopenharmony_ci			fprintf(stderr, "Cannot poll for fds: %m\n");
4398c2ecf20Sopenharmony_ci			break;
4408c2ecf20Sopenharmony_ci		}
4418c2ecf20Sopenharmony_ci		if (pfds[0].revents & POLLHUP) {
4428c2ecf20Sopenharmony_ci			fprintf(stderr, "Received HUP on stdin\n");
4438c2ecf20Sopenharmony_ci			break;
4448c2ecf20Sopenharmony_ci		}
4458c2ecf20Sopenharmony_ci		if (pfds[1].revents & POLLHUP) {
4468c2ecf20Sopenharmony_ci			fprintf(stderr, "Received HUP on uhid-cdev\n");
4478c2ecf20Sopenharmony_ci			break;
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		if (pfds[0].revents & POLLIN) {
4518c2ecf20Sopenharmony_ci			ret = keyboard(fd);
4528c2ecf20Sopenharmony_ci			if (ret)
4538c2ecf20Sopenharmony_ci				break;
4548c2ecf20Sopenharmony_ci		}
4558c2ecf20Sopenharmony_ci		if (pfds[1].revents & POLLIN) {
4568c2ecf20Sopenharmony_ci			ret = event(fd);
4578c2ecf20Sopenharmony_ci			if (ret)
4588c2ecf20Sopenharmony_ci				break;
4598c2ecf20Sopenharmony_ci		}
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	fprintf(stderr, "Destroy uhid device\n");
4638c2ecf20Sopenharmony_ci	destroy(fd);
4648c2ecf20Sopenharmony_ci	return EXIT_SUCCESS;
4658c2ecf20Sopenharmony_ci}
466