Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
11FIXTURE(hidraw) {
12 struct uhid_device hid;
13 int hidraw_fd;
14};
15static 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
22FIXTURE_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
35FIXTURE_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 */
50TEST_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 */
58TEST_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 */
80TEST_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 */
117TEST_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 */
135TEST_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 */
176TEST_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
218int main(int argc, char **argv)
219{
220 return test_harness_run(argc, argv);
221}