xref: /linux/tools/testing/selftests/hid/hidraw.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2022-2024 Red Hat */
3 
4 #include "hid_common.h"
5 
6 /* for older kernels */
7 #ifndef HIDIOCREVOKE
8 #define HIDIOCREVOKE	      _IOW('H', 0x0D, int) /* Revoke device access */
9 #endif /* HIDIOCREVOKE */
10 
11 FIXTURE(hidraw) {
12 	int dev_id;
13 	int uhid_fd;
14 	int hidraw_fd;
15 	int hid_id;
16 	pthread_t tid;
17 };
18 static void close_hidraw(FIXTURE_DATA(hidraw) * self)
19 {
20 	if (self->hidraw_fd)
21 		close(self->hidraw_fd);
22 	self->hidraw_fd = 0;
23 }
24 
25 FIXTURE_TEARDOWN(hidraw) {
26 	void *uhid_err;
27 
28 	uhid_destroy(_metadata, self->uhid_fd);
29 
30 	close_hidraw(self);
31 	pthread_join(self->tid, &uhid_err);
32 }
33 #define TEARDOWN_LOG(fmt, ...) do { \
34 	TH_LOG(fmt, ##__VA_ARGS__); \
35 	hidraw_teardown(_metadata, self, variant); \
36 } while (0)
37 
38 FIXTURE_SETUP(hidraw)
39 {
40 	time_t t;
41 	int err;
42 
43 	/* initialize random number generator */
44 	srand((unsigned int)time(&t));
45 
46 	self->dev_id = rand() % 1024;
47 
48 	self->uhid_fd = setup_uhid(_metadata, self->dev_id);
49 
50 	/* locate the uev, self, variant);ent file of the created device */
51 	self->hid_id = get_hid_id(self->dev_id);
52 	ASSERT_GT(self->hid_id, 0)
53 		TEARDOWN_LOG("Could not locate uhid device id: %d", self->hid_id);
54 
55 	err = uhid_start_listener(_metadata, &self->tid, self->uhid_fd);
56 	ASSERT_EQ(0, err) TEARDOWN_LOG("could not start udev listener: %d", err);
57 
58 	self->hidraw_fd = open_hidraw(self->dev_id);
59 	ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
60 }
61 
62 /*
63  * A simple test to see if the fixture is working fine.
64  * If this fails, none of the other tests will pass.
65  */
66 TEST_F(hidraw, test_create_uhid)
67 {
68 }
69 
70 /*
71  * Inject one event in the uhid device,
72  * check that we get the same data through hidraw
73  */
74 TEST_F(hidraw, raw_event)
75 {
76 	__u8 buf[10] = {0};
77 	int err;
78 
79 	/* inject one event */
80 	buf[0] = 1;
81 	buf[1] = 42;
82 	uhid_send_event(_metadata, self->uhid_fd, buf, 6);
83 
84 	/* read the data from hidraw */
85 	memset(buf, 0, sizeof(buf));
86 	err = read(self->hidraw_fd, buf, sizeof(buf));
87 	ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
88 	ASSERT_EQ(buf[0], 1);
89 	ASSERT_EQ(buf[1], 42);
90 }
91 
92 /*
93  * After initial opening/checks of hidraw, revoke the hidraw
94  * node and check that we can not read any more data.
95  */
96 TEST_F(hidraw, raw_event_revoked)
97 {
98 	__u8 buf[10] = {0};
99 	int err;
100 
101 	/* inject one event */
102 	buf[0] = 1;
103 	buf[1] = 42;
104 	uhid_send_event(_metadata, self->uhid_fd, buf, 6);
105 
106 	/* read the data from hidraw */
107 	memset(buf, 0, sizeof(buf));
108 	err = read(self->hidraw_fd, buf, sizeof(buf));
109 	ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
110 	ASSERT_EQ(buf[0], 1);
111 	ASSERT_EQ(buf[1], 42);
112 
113 	/* call the revoke ioctl */
114 	err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
115 	ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
116 
117 	/* inject one other event */
118 	buf[0] = 1;
119 	buf[1] = 43;
120 	uhid_send_event(_metadata, self->uhid_fd, buf, 6);
121 
122 	/* read the data from hidraw */
123 	memset(buf, 0, sizeof(buf));
124 	err = read(self->hidraw_fd, buf, sizeof(buf));
125 	ASSERT_EQ(err, -1) TH_LOG("read_hidraw");
126 	ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while reading the hidraw node: %d",
127 					errno);
128 }
129 
130 /*
131  * Revoke the hidraw node and check that we can not do any ioctl.
132  */
133 TEST_F(hidraw, ioctl_revoked)
134 {
135 	int err, desc_size = 0;
136 
137 	/* call the revoke ioctl */
138 	err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
139 	ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
140 
141 	/* do an ioctl */
142 	err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size);
143 	ASSERT_EQ(err, -1) TH_LOG("ioctl_hidraw");
144 	ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while doing an ioctl: %d",
145 					errno);
146 }
147 
148 /*
149  * Setup polling of the fd, and check that revoke works properly.
150  */
151 TEST_F(hidraw, poll_revoked)
152 {
153 	struct pollfd pfds[1];
154 	__u8 buf[10] = {0};
155 	int err, ready;
156 
157 	/* setup polling */
158 	pfds[0].fd = self->hidraw_fd;
159 	pfds[0].events = POLLIN;
160 
161 	/* inject one event */
162 	buf[0] = 1;
163 	buf[1] = 42;
164 	uhid_send_event(_metadata, self->uhid_fd, buf, 6);
165 
166 	while (true) {
167 		ready = poll(pfds, 1, 5000);
168 		ASSERT_EQ(ready, 1) TH_LOG("poll return value");
169 
170 		if (pfds[0].revents & POLLIN) {
171 			memset(buf, 0, sizeof(buf));
172 			err = read(self->hidraw_fd, buf, sizeof(buf));
173 			ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
174 			ASSERT_EQ(buf[0], 1);
175 			ASSERT_EQ(buf[1], 42);
176 
177 			/* call the revoke ioctl */
178 			err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
179 			ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
180 		} else {
181 			break;
182 		}
183 	}
184 
185 	ASSERT_TRUE(pfds[0].revents & POLLHUP);
186 }
187 
188 /*
189  * After initial opening/checks of hidraw, revoke the hidraw
190  * node and check that we can not read any more data.
191  */
192 TEST_F(hidraw, write_event_revoked)
193 {
194 	struct timespec time_to_wait;
195 	__u8 buf[10] = {0};
196 	int err;
197 
198 	/* inject one event from hidraw */
199 	buf[0] = 1; /* report ID */
200 	buf[1] = 2;
201 	buf[2] = 42;
202 
203 	pthread_mutex_lock(&uhid_output_mtx);
204 
205 	memset(output_report, 0, sizeof(output_report));
206 	clock_gettime(CLOCK_REALTIME, &time_to_wait);
207 	time_to_wait.tv_sec += 2;
208 
209 	err = write(self->hidraw_fd, buf, 3);
210 	ASSERT_EQ(err, 3) TH_LOG("unexpected error while writing to hidraw node: %d", err);
211 
212 	err = pthread_cond_timedwait(&uhid_output_cond, &uhid_output_mtx, &time_to_wait);
213 	ASSERT_OK(err) TH_LOG("error while calling waiting for the condition");
214 
215 	ASSERT_EQ(output_report[0], 1);
216 	ASSERT_EQ(output_report[1], 2);
217 	ASSERT_EQ(output_report[2], 42);
218 
219 	/* call the revoke ioctl */
220 	err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
221 	ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
222 
223 	/* inject one other event */
224 	buf[0] = 1;
225 	buf[1] = 43;
226 	err = write(self->hidraw_fd, buf, 3);
227 	ASSERT_LT(err, 0) TH_LOG("unexpected success while writing to hidraw node: %d", err);
228 	ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while writing to hidraw node: %d",
229 					errno);
230 
231 	pthread_mutex_unlock(&uhid_output_mtx);
232 }
233 
234 int main(int argc, char **argv)
235 {
236 	return test_harness_run(argc, argv);
237 }
238