~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/hid/hid_common.h

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 0
 16 
 17 #define min(a, b) \
 18         ({ __typeof__(a) _a = (a); \
 19         __typeof__(b) _b = (b); \
 20         _a < _b ? _a : _b; })
 21 
 22 static 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 
 88 static __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 
 98 static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER;
 99 static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER;
100 
101 static pthread_mutex_t uhid_output_mtx = PTHREAD_MUTEX_INITIALIZER;
102 static pthread_cond_t uhid_output_cond = PTHREAD_COND_INITIALIZER;
103 static unsigned char output_report[10];
104 
105 /* no need to protect uhid_stopped, only one thread accesses it */
106 static bool uhid_stopped;
107 
108 static 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 
125 static 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 
149 static 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 
159 static 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 
229 struct uhid_thread_args {
230         int fd;
231         struct __test_metadata *_metadata;
232 };
233 static 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 
262 static 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 
284 static 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 
300 static 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 
318 static 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 
348 static 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 
380 static 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 
424 static 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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php