1c0abf9e6Sopenharmony_ci// SPDX-License-Identifier: MIT
2c0abf9e6Sopenharmony_ci/*
3c0abf9e6Sopenharmony_ci * Copyright © 2013 Red Hat, Inc.
4c0abf9e6Sopenharmony_ci */
5c0abf9e6Sopenharmony_ci
6c0abf9e6Sopenharmony_ci#include "config.h"
7c0abf9e6Sopenharmony_ci#include <fcntl.h>
8c0abf9e6Sopenharmony_ci#include <poll.h>
9c0abf9e6Sopenharmony_ci#include <unistd.h>
10c0abf9e6Sopenharmony_ci#include <string.h>
11c0abf9e6Sopenharmony_ci#include <stdio.h>
12c0abf9e6Sopenharmony_ci#include <errno.h>
13c0abf9e6Sopenharmony_ci#include <linux/uinput.h>
14c0abf9e6Sopenharmony_ci#include <dirent.h>
15c0abf9e6Sopenharmony_ci
16c0abf9e6Sopenharmony_ci#include <libevdev/libevdev.h>
17c0abf9e6Sopenharmony_ci#include <libevdev/libevdev-int.h>
18c0abf9e6Sopenharmony_ci#include <libevdev/libevdev-util.h>
19c0abf9e6Sopenharmony_ci#include <libevdev/libevdev-uinput.h>
20c0abf9e6Sopenharmony_ci
21c0abf9e6Sopenharmony_ci#include "test-common-uinput.h"
22c0abf9e6Sopenharmony_ci
23c0abf9e6Sopenharmony_ci#define SYS_INPUT_DIR "/sys/class/input"
24c0abf9e6Sopenharmony_ci#define DEV_INPUT_DIR "/dev/input/"
25c0abf9e6Sopenharmony_ci
26c0abf9e6Sopenharmony_cistruct uinput_device
27c0abf9e6Sopenharmony_ci{
28c0abf9e6Sopenharmony_ci	struct libevdev *d; /* lazy, it has all the accessors */
29c0abf9e6Sopenharmony_ci	struct libevdev_uinput *uidev;
30c0abf9e6Sopenharmony_ci	int dev_fd; /* open fd to the devnode */
31c0abf9e6Sopenharmony_ci	int uinput_fd;
32c0abf9e6Sopenharmony_ci};
33c0abf9e6Sopenharmony_ci
34c0abf9e6Sopenharmony_cistruct uinput_device*
35c0abf9e6Sopenharmony_ciuinput_device_new(const char *name)
36c0abf9e6Sopenharmony_ci{
37c0abf9e6Sopenharmony_ci	struct uinput_device *dev;
38c0abf9e6Sopenharmony_ci
39c0abf9e6Sopenharmony_ci	dev = calloc(1, sizeof(*dev));
40c0abf9e6Sopenharmony_ci	if (!dev)
41c0abf9e6Sopenharmony_ci		return NULL;
42c0abf9e6Sopenharmony_ci
43c0abf9e6Sopenharmony_ci	dev->d = libevdev_new();
44c0abf9e6Sopenharmony_ci	dev->dev_fd = -1;
45c0abf9e6Sopenharmony_ci	dev->uinput_fd = -1;
46c0abf9e6Sopenharmony_ci
47c0abf9e6Sopenharmony_ci	if (name)
48c0abf9e6Sopenharmony_ci		libevdev_set_name(dev->d, name);
49c0abf9e6Sopenharmony_ci
50c0abf9e6Sopenharmony_ci	return dev;
51c0abf9e6Sopenharmony_ci}
52c0abf9e6Sopenharmony_ci
53c0abf9e6Sopenharmony_ciint
54c0abf9e6Sopenharmony_ciuinput_device_new_with_events_v(struct uinput_device **d, const char *name, const struct input_id *id, va_list args)
55c0abf9e6Sopenharmony_ci{
56c0abf9e6Sopenharmony_ci	int rc;
57c0abf9e6Sopenharmony_ci	struct uinput_device *dev;
58c0abf9e6Sopenharmony_ci
59c0abf9e6Sopenharmony_ci	dev = uinput_device_new(name);
60c0abf9e6Sopenharmony_ci	if (!dev)
61c0abf9e6Sopenharmony_ci		return -ENOMEM;
62c0abf9e6Sopenharmony_ci	if (id != DEFAULT_IDS)
63c0abf9e6Sopenharmony_ci		uinput_device_set_ids(dev, id);
64c0abf9e6Sopenharmony_ci
65c0abf9e6Sopenharmony_ci	rc = uinput_device_set_event_bits_v(dev, args);
66c0abf9e6Sopenharmony_ci
67c0abf9e6Sopenharmony_ci	if (rc == 0)
68c0abf9e6Sopenharmony_ci		rc = uinput_device_create(dev);
69c0abf9e6Sopenharmony_ci
70c0abf9e6Sopenharmony_ci	if (rc != 0) {
71c0abf9e6Sopenharmony_ci		uinput_device_free(dev);
72c0abf9e6Sopenharmony_ci		dev = NULL;
73c0abf9e6Sopenharmony_ci	} else
74c0abf9e6Sopenharmony_ci		*d = dev;
75c0abf9e6Sopenharmony_ci
76c0abf9e6Sopenharmony_ci	return rc;
77c0abf9e6Sopenharmony_ci}
78c0abf9e6Sopenharmony_ci
79c0abf9e6Sopenharmony_ciint
80c0abf9e6Sopenharmony_ciuinput_device_new_with_events(struct uinput_device **d, const char *name, const struct input_id *id, ...)
81c0abf9e6Sopenharmony_ci{
82c0abf9e6Sopenharmony_ci	int rc;
83c0abf9e6Sopenharmony_ci	va_list args;
84c0abf9e6Sopenharmony_ci
85c0abf9e6Sopenharmony_ci	va_start(args, id);
86c0abf9e6Sopenharmony_ci	rc = uinput_device_new_with_events_v(d, name, id, args);
87c0abf9e6Sopenharmony_ci	va_end(args);
88c0abf9e6Sopenharmony_ci
89c0abf9e6Sopenharmony_ci	return rc;
90c0abf9e6Sopenharmony_ci}
91c0abf9e6Sopenharmony_ci
92c0abf9e6Sopenharmony_civoid
93c0abf9e6Sopenharmony_ciuinput_device_free(struct uinput_device *dev)
94c0abf9e6Sopenharmony_ci{
95c0abf9e6Sopenharmony_ci	if (!dev)
96c0abf9e6Sopenharmony_ci		return;
97c0abf9e6Sopenharmony_ci
98c0abf9e6Sopenharmony_ci	if (dev->uinput_fd != -1) {
99c0abf9e6Sopenharmony_ci		(void)ioctl(dev->uinput_fd, UI_DEV_DESTROY, NULL);
100c0abf9e6Sopenharmony_ci		close(dev->uinput_fd);
101c0abf9e6Sopenharmony_ci	}
102c0abf9e6Sopenharmony_ci	if (dev->dev_fd != -1)
103c0abf9e6Sopenharmony_ci		close(dev->dev_fd);
104c0abf9e6Sopenharmony_ci	libevdev_free(dev->d);
105c0abf9e6Sopenharmony_ci	libevdev_uinput_destroy(dev->uidev);
106c0abf9e6Sopenharmony_ci	free(dev);
107c0abf9e6Sopenharmony_ci}
108c0abf9e6Sopenharmony_ci
109c0abf9e6Sopenharmony_ciint
110c0abf9e6Sopenharmony_ciuinput_device_get_fd(const struct uinput_device *dev)
111c0abf9e6Sopenharmony_ci{
112c0abf9e6Sopenharmony_ci	return dev->dev_fd;
113c0abf9e6Sopenharmony_ci}
114c0abf9e6Sopenharmony_ci
115c0abf9e6Sopenharmony_ciconst char*
116c0abf9e6Sopenharmony_ciuinput_device_get_devnode(const struct uinput_device *dev)
117c0abf9e6Sopenharmony_ci{
118c0abf9e6Sopenharmony_ci	return libevdev_uinput_get_devnode(dev->uidev);
119c0abf9e6Sopenharmony_ci}
120c0abf9e6Sopenharmony_ci
121c0abf9e6Sopenharmony_ciint
122c0abf9e6Sopenharmony_ciuinput_device_create(struct uinput_device* d)
123c0abf9e6Sopenharmony_ci{
124c0abf9e6Sopenharmony_ci	int rc;
125c0abf9e6Sopenharmony_ci	int fd;
126c0abf9e6Sopenharmony_ci	const char *devnode;
127c0abf9e6Sopenharmony_ci
128c0abf9e6Sopenharmony_ci	fd = open("/dev/uinput", O_RDWR);
129c0abf9e6Sopenharmony_ci	if (fd < 0)
130c0abf9e6Sopenharmony_ci		goto error;
131c0abf9e6Sopenharmony_ci
132c0abf9e6Sopenharmony_ci	d->uinput_fd = fd;
133c0abf9e6Sopenharmony_ci
134c0abf9e6Sopenharmony_ci	rc = libevdev_uinput_create_from_device(d->d, fd, &d->uidev);
135c0abf9e6Sopenharmony_ci	if (rc != 0)
136c0abf9e6Sopenharmony_ci		goto error;
137c0abf9e6Sopenharmony_ci
138c0abf9e6Sopenharmony_ci	devnode = libevdev_uinput_get_devnode(d->uidev);
139c0abf9e6Sopenharmony_ci	if (devnode == NULL)
140c0abf9e6Sopenharmony_ci		goto error;
141c0abf9e6Sopenharmony_ci
142c0abf9e6Sopenharmony_ci	d->dev_fd = open(devnode, O_RDWR);
143c0abf9e6Sopenharmony_ci	if (d->dev_fd == -1)
144c0abf9e6Sopenharmony_ci		goto error;
145c0abf9e6Sopenharmony_ci
146c0abf9e6Sopenharmony_ci	/* write abs resolution now */
147c0abf9e6Sopenharmony_ci	if (libevdev_has_event_type(d->d, EV_ABS)) {
148c0abf9e6Sopenharmony_ci		int  code;
149c0abf9e6Sopenharmony_ci		for (code = 0; code < ABS_CNT; code++) {
150c0abf9e6Sopenharmony_ci			const struct input_absinfo *abs;
151c0abf9e6Sopenharmony_ci
152c0abf9e6Sopenharmony_ci			/* can't change slots */
153c0abf9e6Sopenharmony_ci			if (code == ABS_MT_SLOT)
154c0abf9e6Sopenharmony_ci				continue;
155c0abf9e6Sopenharmony_ci
156c0abf9e6Sopenharmony_ci			abs = libevdev_get_abs_info(d->d, code);
157c0abf9e6Sopenharmony_ci			if (!abs)
158c0abf9e6Sopenharmony_ci				continue;
159c0abf9e6Sopenharmony_ci
160c0abf9e6Sopenharmony_ci			rc = ioctl(d->dev_fd, EVIOCSABS(code), abs);
161c0abf9e6Sopenharmony_ci			if (rc < 0) {
162c0abf9e6Sopenharmony_ci				printf("error %s for code %d\n", strerror(-rc), code);
163c0abf9e6Sopenharmony_ci				goto error;
164c0abf9e6Sopenharmony_ci			}
165c0abf9e6Sopenharmony_ci		}
166c0abf9e6Sopenharmony_ci	}
167c0abf9e6Sopenharmony_ci
168c0abf9e6Sopenharmony_ci	return 0;
169c0abf9e6Sopenharmony_ci
170c0abf9e6Sopenharmony_cierror:
171c0abf9e6Sopenharmony_ci	if (d->dev_fd != -1)
172c0abf9e6Sopenharmony_ci		close(d->dev_fd);
173c0abf9e6Sopenharmony_ci	if (d->uinput_fd != -1)
174c0abf9e6Sopenharmony_ci		close(d->uinput_fd);
175c0abf9e6Sopenharmony_ci	return -errno;
176c0abf9e6Sopenharmony_ci
177c0abf9e6Sopenharmony_ci}
178c0abf9e6Sopenharmony_ci
179c0abf9e6Sopenharmony_ciint uinput_device_set_name(struct uinput_device *dev, const char *name)
180c0abf9e6Sopenharmony_ci{
181c0abf9e6Sopenharmony_ci	libevdev_set_name(dev->d, name);
182c0abf9e6Sopenharmony_ci	return 0;
183c0abf9e6Sopenharmony_ci}
184c0abf9e6Sopenharmony_ci
185c0abf9e6Sopenharmony_ciint uinput_device_set_ids(struct uinput_device *dev, const struct input_id *ids)
186c0abf9e6Sopenharmony_ci{
187c0abf9e6Sopenharmony_ci	libevdev_set_id_product(dev->d, ids->product);
188c0abf9e6Sopenharmony_ci	libevdev_set_id_vendor(dev->d, ids->vendor);
189c0abf9e6Sopenharmony_ci	libevdev_set_id_bustype(dev->d, ids->bustype);
190c0abf9e6Sopenharmony_ci	libevdev_set_id_version(dev->d, ids->version);
191c0abf9e6Sopenharmony_ci	return 0;
192c0abf9e6Sopenharmony_ci}
193c0abf9e6Sopenharmony_ci
194c0abf9e6Sopenharmony_ciint
195c0abf9e6Sopenharmony_ciuinput_device_set_bit(struct uinput_device* dev, unsigned int bit)
196c0abf9e6Sopenharmony_ci{
197c0abf9e6Sopenharmony_ci	return libevdev_enable_event_type(dev->d, bit);
198c0abf9e6Sopenharmony_ci}
199c0abf9e6Sopenharmony_ci
200c0abf9e6Sopenharmony_ciint
201c0abf9e6Sopenharmony_ciuinput_device_set_prop(struct uinput_device *dev, unsigned int prop)
202c0abf9e6Sopenharmony_ci{
203c0abf9e6Sopenharmony_ci	return libevdev_enable_property(dev->d, prop);
204c0abf9e6Sopenharmony_ci}
205c0abf9e6Sopenharmony_ci
206c0abf9e6Sopenharmony_ciint
207c0abf9e6Sopenharmony_ciuinput_device_set_event_bit(struct uinput_device* dev, unsigned int type, unsigned int code)
208c0abf9e6Sopenharmony_ci{
209c0abf9e6Sopenharmony_ci	return libevdev_enable_event_code(dev->d, type, code, NULL);
210c0abf9e6Sopenharmony_ci}
211c0abf9e6Sopenharmony_ci
212c0abf9e6Sopenharmony_ciint
213c0abf9e6Sopenharmony_ciuinput_device_set_event_bits_v(struct uinput_device *dev, va_list args)
214c0abf9e6Sopenharmony_ci{
215c0abf9e6Sopenharmony_ci	int type, code;
216c0abf9e6Sopenharmony_ci	int rc = 0;
217c0abf9e6Sopenharmony_ci
218c0abf9e6Sopenharmony_ci	do {
219c0abf9e6Sopenharmony_ci		type = va_arg(args, int);
220c0abf9e6Sopenharmony_ci		if (type == -1)
221c0abf9e6Sopenharmony_ci			break;
222c0abf9e6Sopenharmony_ci		code = va_arg(args, int);
223c0abf9e6Sopenharmony_ci		if (code == -1)
224c0abf9e6Sopenharmony_ci			break;
225c0abf9e6Sopenharmony_ci		rc = libevdev_enable_event_code(dev->d, type, code, NULL);
226c0abf9e6Sopenharmony_ci	} while (rc == 0);
227c0abf9e6Sopenharmony_ci
228c0abf9e6Sopenharmony_ci	return rc;
229c0abf9e6Sopenharmony_ci}
230c0abf9e6Sopenharmony_ci
231c0abf9e6Sopenharmony_ciint
232c0abf9e6Sopenharmony_ciuinput_device_set_event_bits(struct uinput_device *dev, ...)
233c0abf9e6Sopenharmony_ci{
234c0abf9e6Sopenharmony_ci	int rc;
235c0abf9e6Sopenharmony_ci	va_list args;
236c0abf9e6Sopenharmony_ci	va_start(args, dev);
237c0abf9e6Sopenharmony_ci	rc = uinput_device_set_event_bits_v(dev, args);
238c0abf9e6Sopenharmony_ci	va_end(args);
239c0abf9e6Sopenharmony_ci
240c0abf9e6Sopenharmony_ci	return rc;
241c0abf9e6Sopenharmony_ci}
242c0abf9e6Sopenharmony_ci
243c0abf9e6Sopenharmony_ciint
244c0abf9e6Sopenharmony_ciuinput_device_set_abs_bit(struct uinput_device* dev, unsigned int code, const struct input_absinfo *absinfo)
245c0abf9e6Sopenharmony_ci{
246c0abf9e6Sopenharmony_ci	return libevdev_enable_event_code(dev->d, EV_ABS, code, absinfo);
247c0abf9e6Sopenharmony_ci}
248c0abf9e6Sopenharmony_ci
249c0abf9e6Sopenharmony_ciint
250c0abf9e6Sopenharmony_ciuinput_device_event(const struct uinput_device *dev, unsigned int type, unsigned int code, int value)
251c0abf9e6Sopenharmony_ci{
252c0abf9e6Sopenharmony_ci	return libevdev_uinput_write_event(dev->uidev, type, code, value);
253c0abf9e6Sopenharmony_ci}
254c0abf9e6Sopenharmony_ci
255c0abf9e6Sopenharmony_ciint uinput_device_event_multiple_v(const struct uinput_device* dev, va_list args)
256c0abf9e6Sopenharmony_ci{
257c0abf9e6Sopenharmony_ci	int type, code, value;
258c0abf9e6Sopenharmony_ci	int rc = 0;
259c0abf9e6Sopenharmony_ci
260c0abf9e6Sopenharmony_ci	do {
261c0abf9e6Sopenharmony_ci		type = va_arg(args, int);
262c0abf9e6Sopenharmony_ci		if (type == -1)
263c0abf9e6Sopenharmony_ci			break;
264c0abf9e6Sopenharmony_ci		code = va_arg(args, int);
265c0abf9e6Sopenharmony_ci		if (code == -1)
266c0abf9e6Sopenharmony_ci			break;
267c0abf9e6Sopenharmony_ci		value = va_arg(args, int);
268c0abf9e6Sopenharmony_ci		rc = uinput_device_event(dev, type, code, value);
269c0abf9e6Sopenharmony_ci	} while (rc == 0);
270c0abf9e6Sopenharmony_ci
271c0abf9e6Sopenharmony_ci	return rc;
272c0abf9e6Sopenharmony_ci}
273c0abf9e6Sopenharmony_ci
274c0abf9e6Sopenharmony_ciint uinput_device_event_multiple(const struct uinput_device* dev, ...)
275c0abf9e6Sopenharmony_ci{
276c0abf9e6Sopenharmony_ci	int rc;
277c0abf9e6Sopenharmony_ci	va_list args;
278c0abf9e6Sopenharmony_ci	va_start(args, dev);
279c0abf9e6Sopenharmony_ci	rc = uinput_device_event_multiple_v(dev, args);
280c0abf9e6Sopenharmony_ci	va_end(args);
281c0abf9e6Sopenharmony_ci	return rc;
282c0abf9e6Sopenharmony_ci}
283