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