brintos

brintos / linux-shallow public Read only

0
0
Text · 10.6 KiB · f151f15 Raw
437 lines · c
1/* SPDX-License-Identifier: GPL-2.0 */2/* Copyright (c) 2022-2024 Red Hat */3 4#include "../kselftest_harness.h"5 6#include <fcntl.h>7#include <fnmatch.h>8#include <dirent.h>9#include <poll.h>10#include <pthread.h>11#include <stdbool.h>12#include <linux/hidraw.h>13#include <linux/uhid.h>14 15#define SHOW_UHID_DEBUG 016 17#define min(a, b) \18	({ __typeof__(a) _a = (a); \19	__typeof__(b) _b = (b); \20	_a < _b ? _a : _b; })21 22static unsigned char rdesc[] = {23	0x06, 0x00, 0xff,	/* Usage Page (Vendor Defined Page 1) */24	0x09, 0x21,		/* Usage (Vendor Usage 0x21) */25	0xa1, 0x01,		/* COLLECTION (Application) */26	0x09, 0x01,			/* Usage (Vendor Usage 0x01) */27	0xa1, 0x00,			/* COLLECTION (Physical) */28	0x85, 0x02,				/* REPORT_ID (2) */29	0x19, 0x01,				/* USAGE_MINIMUM (1) */30	0x29, 0x08,				/* USAGE_MAXIMUM (3) */31	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */32	0x25, 0xff,				/* LOGICAL_MAXIMUM (255) */33	0x95, 0x08,				/* REPORT_COUNT (8) */34	0x75, 0x08,				/* REPORT_SIZE (8) */35	0x81, 0x02,				/* INPUT (Data,Var,Abs) */36	0xc0,				/* END_COLLECTION */37	0x09, 0x01,			/* Usage (Vendor Usage 0x01) */38	0xa1, 0x00,			/* COLLECTION (Physical) */39	0x85, 0x01,				/* REPORT_ID (1) */40	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */41	0x19, 0x01,				/* USAGE_MINIMUM (1) */42	0x29, 0x03,				/* USAGE_MAXIMUM (3) */43	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */44	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */45	0x95, 0x03,				/* REPORT_COUNT (3) */46	0x75, 0x01,				/* REPORT_SIZE (1) */47	0x81, 0x02,				/* INPUT (Data,Var,Abs) */48	0x95, 0x01,				/* REPORT_COUNT (1) */49	0x75, 0x05,				/* REPORT_SIZE (5) */50	0x81, 0x01,				/* INPUT (Cnst,Var,Abs) */51	0x05, 0x01,				/* USAGE_PAGE (Generic Desktop) */52	0x09, 0x30,				/* USAGE (X) */53	0x09, 0x31,				/* USAGE (Y) */54	0x15, 0x81,				/* LOGICAL_MINIMUM (-127) */55	0x25, 0x7f,				/* LOGICAL_MAXIMUM (127) */56	0x75, 0x10,				/* REPORT_SIZE (16) */57	0x95, 0x02,				/* REPORT_COUNT (2) */58	0x81, 0x06,				/* INPUT (Data,Var,Rel) */59 60	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */61	0x19, 0x01,				/* USAGE_MINIMUM (1) */62	0x29, 0x03,				/* USAGE_MAXIMUM (3) */63	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */64	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */65	0x95, 0x03,				/* REPORT_COUNT (3) */66	0x75, 0x01,				/* REPORT_SIZE (1) */67	0x91, 0x02,				/* Output (Data,Var,Abs) */68	0x95, 0x01,				/* REPORT_COUNT (1) */69	0x75, 0x05,				/* REPORT_SIZE (5) */70	0x91, 0x01,				/* Output (Cnst,Var,Abs) */71 72	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */73	0x19, 0x06,				/* USAGE_MINIMUM (6) */74	0x29, 0x08,				/* USAGE_MAXIMUM (8) */75	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */76	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */77	0x95, 0x03,				/* REPORT_COUNT (3) */78	0x75, 0x01,				/* REPORT_SIZE (1) */79	0xb1, 0x02,				/* Feature (Data,Var,Abs) */80	0x95, 0x01,				/* REPORT_COUNT (1) */81	0x75, 0x05,				/* REPORT_SIZE (5) */82	0x91, 0x01,				/* Output (Cnst,Var,Abs) */83 84	0xc0,				/* END_COLLECTION */85	0xc0,			/* END_COLLECTION */86};87 88static __u8 feature_data[] = { 1, 2 };89 90#define ASSERT_OK(data) ASSERT_FALSE(data)91#define ASSERT_OK_PTR(ptr) ASSERT_NE(NULL, ptr)92 93#define UHID_LOG(fmt, ...) do { \94	if (SHOW_UHID_DEBUG) \95		TH_LOG(fmt, ##__VA_ARGS__); \96} while (0)97 98static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER;99static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER;100 101static pthread_mutex_t uhid_output_mtx = PTHREAD_MUTEX_INITIALIZER;102static pthread_cond_t uhid_output_cond = PTHREAD_COND_INITIALIZER;103static unsigned char output_report[10];104 105/* no need to protect uhid_stopped, only one thread accesses it */106static bool uhid_stopped;107 108static int uhid_write(struct __test_metadata *_metadata, int fd, const struct uhid_event *ev)109{110	ssize_t ret;111 112	ret = write(fd, ev, sizeof(*ev));113	if (ret < 0) {114		TH_LOG("Cannot write to uhid: %m");115		return -errno;116	} else if (ret != sizeof(*ev)) {117		TH_LOG("Wrong size written to uhid: %zd != %zu",118			ret, sizeof(ev));119		return -EFAULT;120	} else {121		return 0;122	}123}124 125static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb)126{127	struct uhid_event ev;128	char buf[25];129 130	sprintf(buf, "test-uhid-device-%d", rand_nb);131 132	memset(&ev, 0, sizeof(ev));133	ev.type = UHID_CREATE;134	strcpy((char *)ev.u.create.name, buf);135	ev.u.create.rd_data = rdesc;136	ev.u.create.rd_size = sizeof(rdesc);137	ev.u.create.bus = BUS_USB;138	ev.u.create.vendor = 0x0001;139	ev.u.create.product = 0x0a37;140	ev.u.create.version = 0;141	ev.u.create.country = 0;142 143	sprintf(buf, "%d", rand_nb);144	strcpy((char *)ev.u.create.phys, buf);145 146	return uhid_write(_metadata, fd, &ev);147}148 149static void uhid_destroy(struct __test_metadata *_metadata, int fd)150{151	struct uhid_event ev;152 153	memset(&ev, 0, sizeof(ev));154	ev.type = UHID_DESTROY;155 156	uhid_write(_metadata, fd, &ev);157}158 159static int uhid_event(struct __test_metadata *_metadata, int fd)160{161	struct uhid_event ev, answer;162	ssize_t ret;163 164	memset(&ev, 0, sizeof(ev));165	ret = read(fd, &ev, sizeof(ev));166	if (ret == 0) {167		UHID_LOG("Read HUP on uhid-cdev");168		return -EFAULT;169	} else if (ret < 0) {170		UHID_LOG("Cannot read uhid-cdev: %m");171		return -errno;172	} else if (ret != sizeof(ev)) {173		UHID_LOG("Invalid size read from uhid-dev: %zd != %zu",174			ret, sizeof(ev));175		return -EFAULT;176	}177 178	switch (ev.type) {179	case UHID_START:180		pthread_mutex_lock(&uhid_started_mtx);181		pthread_cond_signal(&uhid_started);182		pthread_mutex_unlock(&uhid_started_mtx);183 184		UHID_LOG("UHID_START from uhid-dev");185		break;186	case UHID_STOP:187		uhid_stopped = true;188 189		UHID_LOG("UHID_STOP from uhid-dev");190		break;191	case UHID_OPEN:192		UHID_LOG("UHID_OPEN from uhid-dev");193		break;194	case UHID_CLOSE:195		UHID_LOG("UHID_CLOSE from uhid-dev");196		break;197	case UHID_OUTPUT:198		UHID_LOG("UHID_OUTPUT from uhid-dev");199 200		pthread_mutex_lock(&uhid_output_mtx);201		memcpy(output_report,202		       ev.u.output.data,203		       min(ev.u.output.size, sizeof(output_report)));204		pthread_cond_signal(&uhid_output_cond);205		pthread_mutex_unlock(&uhid_output_mtx);206		break;207	case UHID_GET_REPORT:208		UHID_LOG("UHID_GET_REPORT from uhid-dev");209 210		answer.type = UHID_GET_REPORT_REPLY;211		answer.u.get_report_reply.id = ev.u.get_report.id;212		answer.u.get_report_reply.err = ev.u.get_report.rnum == 1 ? 0 : -EIO;213		answer.u.get_report_reply.size = sizeof(feature_data);214		memcpy(answer.u.get_report_reply.data, feature_data, sizeof(feature_data));215 216		uhid_write(_metadata, fd, &answer);217 218		break;219	case UHID_SET_REPORT:220		UHID_LOG("UHID_SET_REPORT from uhid-dev");221		break;222	default:223		TH_LOG("Invalid event from uhid-dev: %u", ev.type);224	}225 226	return 0;227}228 229struct uhid_thread_args {230	int fd;231	struct __test_metadata *_metadata;232};233static void *uhid_read_events_thread(void *arg)234{235	struct uhid_thread_args *args = (struct uhid_thread_args *)arg;236	struct __test_metadata *_metadata = args->_metadata;237	struct pollfd pfds[1];238	int fd = args->fd;239	int ret = 0;240 241	pfds[0].fd = fd;242	pfds[0].events = POLLIN;243 244	uhid_stopped = false;245 246	while (!uhid_stopped) {247		ret = poll(pfds, 1, 100);248		if (ret < 0) {249			TH_LOG("Cannot poll for fds: %m");250			break;251		}252		if (pfds[0].revents & POLLIN) {253			ret = uhid_event(_metadata, fd);254			if (ret)255				break;256		}257	}258 259	return (void *)(long)ret;260}261 262static int uhid_start_listener(struct __test_metadata *_metadata, pthread_t *tid, int uhid_fd)263{264	struct uhid_thread_args args = {265		.fd = uhid_fd,266		._metadata = _metadata,267	};268	int err;269 270	pthread_mutex_lock(&uhid_started_mtx);271	err = pthread_create(tid, NULL, uhid_read_events_thread, (void *)&args);272	ASSERT_EQ(0, err) {273		TH_LOG("Could not start the uhid thread: %d", err);274		pthread_mutex_unlock(&uhid_started_mtx);275		close(uhid_fd);276		return -EIO;277	}278	pthread_cond_wait(&uhid_started, &uhid_started_mtx);279	pthread_mutex_unlock(&uhid_started_mtx);280 281	return 0;282}283 284static int uhid_send_event(struct __test_metadata *_metadata, int fd, __u8 *buf, size_t size)285{286	struct uhid_event ev;287 288	if (size > sizeof(ev.u.input.data))289		return -E2BIG;290 291	memset(&ev, 0, sizeof(ev));292	ev.type = UHID_INPUT2;293	ev.u.input2.size = size;294 295	memcpy(ev.u.input2.data, buf, size);296 297	return uhid_write(_metadata, fd, &ev);298}299 300static int setup_uhid(struct __test_metadata *_metadata, int rand_nb)301{302	int fd;303	const char *path = "/dev/uhid";304	int ret;305 306	fd = open(path, O_RDWR | O_CLOEXEC);307	ASSERT_GE(fd, 0) TH_LOG("open uhid-cdev failed; %d", fd);308 309	ret = uhid_create(_metadata, fd, rand_nb);310	ASSERT_EQ(0, ret) {311		TH_LOG("create uhid device failed: %d", ret);312		close(fd);313	}314 315	return fd;316}317 318static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *dir)319{320	const char *target = "0003:0001:0A37.*";321	char phys[512];322	char uevent[1024];323	char temp[512];324	int fd, nread;325	bool found = false;326 327	if (fnmatch(target, dir->d_name, 0))328		return false;329 330	/* we found the correct VID/PID, now check for phys */331	sprintf(uevent, "%s/%s/uevent", workdir, dir->d_name);332 333	fd = open(uevent, O_RDONLY | O_NONBLOCK);334	if (fd < 0)335		return false;336 337	sprintf(phys, "PHYS=%d", dev_id);338 339	nread = read(fd, temp, ARRAY_SIZE(temp));340	if (nread > 0 && (strstr(temp, phys)) != NULL)341		found = true;342 343	close(fd);344 345	return found;346}347 348static int get_hid_id(int dev_id)349{350	const char *workdir = "/sys/devices/virtual/misc/uhid";351	const char *str_id;352	DIR *d;353	struct dirent *dir;354	int found = -1, attempts = 3;355 356	/* it would be nice to be able to use nftw, but the no_alu32 target doesn't support it */357 358	while (found < 0 && attempts > 0) {359		attempts--;360		d = opendir(workdir);361		if (d) {362			while ((dir = readdir(d)) != NULL) {363				if (!match_sysfs_device(dev_id, workdir, dir))364					continue;365 366				str_id = dir->d_name + sizeof("0003:0001:0A37.");367				found = (int)strtol(str_id, NULL, 16);368 369				break;370			}371			closedir(d);372		}373		if (found < 0)374			usleep(100000);375	}376 377	return found;378}379 380static int get_hidraw(int dev_id)381{382	const char *workdir = "/sys/devices/virtual/misc/uhid";383	char sysfs[1024];384	DIR *d, *subd;385	struct dirent *dir, *subdir;386	int i, found = -1;387 388	/* retry 5 times in case the system is loaded */389	for (i = 5; i > 0; i--) {390		usleep(10);391		d = opendir(workdir);392 393		if (!d)394			continue;395 396		while ((dir = readdir(d)) != NULL) {397			if (!match_sysfs_device(dev_id, workdir, dir))398				continue;399 400			sprintf(sysfs, "%s/%s/hidraw", workdir, dir->d_name);401 402			subd = opendir(sysfs);403			if (!subd)404				continue;405 406			while ((subdir = readdir(subd)) != NULL) {407				if (fnmatch("hidraw*", subdir->d_name, 0))408					continue;409 410				found = atoi(subdir->d_name + strlen("hidraw"));411			}412 413			closedir(subd);414 415			if (found > 0)416				break;417		}418		closedir(d);419	}420 421	return found;422}423 424static int open_hidraw(int dev_id)425{426	int hidraw_number;427	char hidraw_path[64] = { 0 };428 429	hidraw_number = get_hidraw(dev_id);430	if (hidraw_number < 0)431		return hidraw_number;432 433	/* open hidraw node to check the other side of the pipe */434	sprintf(hidraw_path, "/dev/hidraw%d", hidraw_number);435	return open(hidraw_path, O_RDWR | O_NONBLOCK);436}437