File Index
Files and directories
Sign in
FILE

hidraw.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/hid/hidraw.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright (c) 2022-2024 Red Hat */
  3
  4#include "hid_common.h"
  5#include <linux/input.h>
  6#include <string.h>
  7#include <sys/ioctl.h>
  8
  9/* for older kernels */
 10#ifndef HIDIOCREVOKE
 11#define HIDIOCREVOKE	      _IOW('H', 0x0D, int) /* Revoke device access */
 12#endif /* HIDIOCREVOKE */
 13
 14FIXTURE(hidraw) {
 15	struct uhid_device hid;
 16	int hidraw_fd;
 17};
 18static 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
 25FIXTURE_TEARDOWN(hidraw) {
 26	void *uhid_err;
 27
 28	uhid_destroy(_metadata, &self->hid);
 29
 30	close_hidraw(self);
 31	pthread_join(self->hid.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
 38FIXTURE_SETUP(hidraw)
 39{
 40	int err;
 41
 42	err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a37, rdesc, sizeof(rdesc));
 43	ASSERT_OK(err);
 44
 45	self->hidraw_fd = open_hidraw(&self->hid);
 46	ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
 47}
 48
 49/*
 50 * A simple test to see if the fixture is working fine.
 51 * If this fails, none of the other tests will pass.
 52 */
 53TEST_F(hidraw, test_create_uhid)
 54{
 55}
 56
 57/*
 58 * Inject one event in the uhid device,
 59 * check that we get the same data through hidraw
 60 */
 61TEST_F(hidraw, raw_event)
 62{
 63	__u8 buf[10] = {0};
 64	int err;
 65
 66	/* inject one event */
 67	buf[0] = 1;
 68	buf[1] = 42;
 69	uhid_send_event(_metadata, &self->hid, buf, 6);
 70
 71	/* read the data from hidraw */
 72	memset(buf, 0, sizeof(buf));
 73	err = read(self->hidraw_fd, buf, sizeof(buf));
 74	ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
 75	ASSERT_EQ(buf[0], 1);
 76	ASSERT_EQ(buf[1], 42);
 77}
 78
 79/*
 80 * After initial opening/checks of hidraw, revoke the hidraw
 81 * node and check that we can not read any more data.
 82 */
 83TEST_F(hidraw, raw_event_revoked)
 84{
 85	__u8 buf[10] = {0};
 86	int err;
 87
 88	/* inject one event */
 89	buf[0] = 1;
 90	buf[1] = 42;
 91	uhid_send_event(_metadata, &self->hid, buf, 6);
 92
 93	/* read the data from hidraw */
 94	memset(buf, 0, sizeof(buf));
 95	err = read(self->hidraw_fd, buf, sizeof(buf));
 96	ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
 97	ASSERT_EQ(buf[0], 1);
 98	ASSERT_EQ(buf[1], 42);
 99
100	/* call the revoke ioctl */
101	err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
102	ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
103
104	/* inject one other event */
105	buf[0] = 1;
106	buf[1] = 43;
107	uhid_send_event(_metadata, &self->hid, buf, 6);
108
109	/* read the data from hidraw */
110	memset(buf, 0, sizeof(buf));
111	err = read(self->hidraw_fd, buf, sizeof(buf));
112	ASSERT_EQ(err, -1) TH_LOG("read_hidraw");
113	ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while reading the hidraw node: %d",
114					errno);
115}
116
117/*
118 * Revoke the hidraw node and check that we can not do any ioctl.
119 */
120TEST_F(hidraw, ioctl_revoked)
121{
122	int err, desc_size = 0;
123
124	/* call the revoke ioctl */
125	err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
126	ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
127
128	/* do an ioctl */
129	err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size);
130	ASSERT_EQ(err, -1) TH_LOG("ioctl_hidraw");
131	ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while doing an ioctl: %d",
132					errno);
133}
134
135/*
136 * Setup polling of the fd, and check that revoke works properly.
137 */
138TEST_F(hidraw, poll_revoked)
139{
140	struct pollfd pfds[1];
141	__u8 buf[10] = {0};
142	int err, ready;
143
144	/* setup polling */
145	pfds[0].fd = self->hidraw_fd;
146	pfds[0].events = POLLIN;
147
148	/* inject one event */
149	buf[0] = 1;
150	buf[1] = 42;
151	uhid_send_event(_metadata, &self->hid, buf, 6);
152
153	while (true) {
154		ready = poll(pfds, 1, 5000);
155		ASSERT_EQ(ready, 1) TH_LOG("poll return value");
156
157		if (pfds[0].revents & POLLIN) {
158			memset(buf, 0, sizeof(buf));
159			err = read(self->hidraw_fd, buf, sizeof(buf));
160			ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
161			ASSERT_EQ(buf[0], 1);
162			ASSERT_EQ(buf[1], 42);
163
164			/* call the revoke ioctl */
165			err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
166			ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
167		} else {
168			break;
169		}
170	}
171
172	ASSERT_TRUE(pfds[0].revents & POLLHUP);
173}
174
175/*
176 * After initial opening/checks of hidraw, revoke the hidraw
177 * node and check that we can not read any more data.
178 */
179TEST_F(hidraw, write_event_revoked)
180{
181	struct timespec time_to_wait;
182	__u8 buf[10] = {0};
183	int err;
184
185	/* inject one event from hidraw */
186	buf[0] = 1; /* report ID */
187	buf[1] = 2;
188	buf[2] = 42;
189
190	pthread_mutex_lock(&uhid_output_mtx);
191
192	memset(output_report, 0, sizeof(output_report));
193	clock_gettime(CLOCK_REALTIME, &time_to_wait);
194	time_to_wait.tv_sec += 2;
195
196	err = write(self->hidraw_fd, buf, 3);
197	ASSERT_EQ(err, 3) TH_LOG("unexpected error while writing to hidraw node: %d", err);
198
199	err = pthread_cond_timedwait(&uhid_output_cond, &uhid_output_mtx, &time_to_wait);
200	ASSERT_OK(err) TH_LOG("error while calling waiting for the condition");
201
202	ASSERT_EQ(output_report[0], 1);
203	ASSERT_EQ(output_report[1], 2);
204	ASSERT_EQ(output_report[2], 42);
205
206	/* call the revoke ioctl */
207	err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
208	ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
209
210	/* inject one other event */
211	buf[0] = 1;
212	buf[1] = 43;
213	err = write(self->hidraw_fd, buf, 3);
214	ASSERT_LT(err, 0) TH_LOG("unexpected success while writing to hidraw node: %d", err);
215	ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while writing to hidraw node: %d",
216					errno);
217
218	pthread_mutex_unlock(&uhid_output_mtx);
219}
220
221/*
222 * Test HIDIOCGRDESCSIZE ioctl to get report descriptor size
223 */
224TEST_F(hidraw, ioctl_rdescsize)
225{
226	int desc_size = 0;
227	int err;
228
229	/* call HIDIOCGRDESCSIZE ioctl */
230	err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size);
231	ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESCSIZE ioctl failed");
232
233	/* verify the size matches our test report descriptor */
234	ASSERT_EQ(desc_size, sizeof(rdesc))
235		TH_LOG("expected size %zu, got %d", sizeof(rdesc), desc_size);
236}
237
238/*
239 * Test HIDIOCGRDESC ioctl to get report descriptor data
240 */
241TEST_F(hidraw, ioctl_rdesc)
242{
243	struct hidraw_report_descriptor desc;
244	int err;
245
246	/* get the full report descriptor */
247	desc.size = sizeof(rdesc);
248	err = ioctl(self->hidraw_fd, HIDIOCGRDESC, &desc);
249	ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESC ioctl failed");
250
251	/* verify the descriptor data matches our test descriptor */
252	ASSERT_EQ(memcmp(desc.value, rdesc, sizeof(rdesc)), 0)
253		TH_LOG("report descriptor data mismatch");
254}
255
256/*
257 * Test HIDIOCGRDESC ioctl with smaller buffer size
258 */
259TEST_F(hidraw, ioctl_rdesc_small_buffer)
260{
261	struct hidraw_report_descriptor desc;
262	int err;
263	size_t small_size = sizeof(rdesc) / 2; /* request half the descriptor size */
264
265	/* get partial report descriptor */
266	desc.size = small_size;
267	err = ioctl(self->hidraw_fd, HIDIOCGRDESC, &desc);
268	ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESC ioctl failed with small buffer");
269
270	/* verify we got the first part of the descriptor */
271	ASSERT_EQ(memcmp(desc.value, rdesc, small_size), 0)
272		TH_LOG("partial report descriptor data mismatch");
273}
274
275/*
276 * Test HIDIOCGRAWINFO ioctl to get device information
277 */
278TEST_F(hidraw, ioctl_rawinfo)
279{
280	struct hidraw_devinfo devinfo;
281	int err;
282
283	/* get device info */
284	err = ioctl(self->hidraw_fd, HIDIOCGRAWINFO, &devinfo);
285	ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRAWINFO ioctl failed");
286
287	/* verify device info matches our test setup */
288	ASSERT_EQ(devinfo.bustype, BUS_USB)
289		TH_LOG("expected bustype 0x03, got 0x%x", devinfo.bustype);
290	ASSERT_EQ(devinfo.vendor, 0x0001)
291		TH_LOG("expected vendor 0x0001, got 0x%x", devinfo.vendor);
292	ASSERT_EQ(devinfo.product, 0x0a37)
293		TH_LOG("expected product 0x0a37, got 0x%x", devinfo.product);
294}
295
296/*
297 * Test HIDIOCGFEATURE ioctl to get feature report
298 */
299TEST_F(hidraw, ioctl_gfeature)
300{
301	__u8 buf[10] = {0};
302	int err;
303
304	/* set report ID 1 in first byte */
305	buf[0] = 1;
306
307	/* get feature report */
308	err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
309	ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGFEATURE ioctl failed, got %d", err);
310
311	/* verify we got the expected feature data */
312	ASSERT_EQ(buf[0], feature_data[0])
313		TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]);
314	ASSERT_EQ(buf[1], feature_data[1])
315		TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]);
316}
317
318/*
319 * Test HIDIOCGFEATURE ioctl with invalid report ID
320 */
321TEST_F(hidraw, ioctl_gfeature_invalid)
322{
323	__u8 buf[10] = {0};
324	int err;
325
326	/* set invalid report ID (not 1) */
327	buf[0] = 2;
328
329	/* try to get feature report */
330	err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
331	ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE should have failed with invalid report ID");
332	ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno);
333}
334
335/*
336 * Test ioctl with incorrect nr bits
337 */
338TEST_F(hidraw, ioctl_invalid_nr)
339{
340	char buf[256] = {0};
341	int err;
342	unsigned int bad_cmd;
343
344	/*
345	 * craft an ioctl command with wrong _IOC_NR bits
346	 */
347	bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x00, sizeof(buf)); /* 0 is not valid */
348
349	/* test the ioctl */
350	err = ioctl(self->hidraw_fd, bad_cmd, buf);
351	ASSERT_LT(err, 0) TH_LOG("ioctl read-write with wrong _IOC_NR (0) should have failed");
352	ASSERT_EQ(errno, ENOTTY)
353		TH_LOG("expected ENOTTY for wrong read-write _IOC_NR (0), got errno %d", errno);
354
355	/*
356	 * craft an ioctl command with wrong _IOC_NR bits
357	 */
358	bad_cmd = _IOC(_IOC_READ, 'H', 0x00, sizeof(buf)); /* 0 is not valid */
359
360	/* test the ioctl */
361	err = ioctl(self->hidraw_fd, bad_cmd, buf);
362	ASSERT_LT(err, 0) TH_LOG("ioctl read-only with wrong _IOC_NR (0) should have failed");
363	ASSERT_EQ(errno, ENOTTY)
364		TH_LOG("expected ENOTTY for wrong read-only _IOC_NR (0), got errno %d", errno);
365
366	/* also test with bigger number */
367	bad_cmd = _IOC(_IOC_READ, 'H', 0x42, sizeof(buf)); /* 0x42 is not valid as well */
368
369	err = ioctl(self->hidraw_fd, bad_cmd, buf);
370	ASSERT_LT(err, 0) TH_LOG("ioctl read-only with wrong _IOC_NR (0x42) should have failed");
371	ASSERT_EQ(errno, ENOTTY)
372		TH_LOG("expected ENOTTY for wrong read-only _IOC_NR (0x42), got errno %d", errno);
373
374	/* also test with bigger number: 0x42 is not valid as well */
375	bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x42, sizeof(buf));
376
377	err = ioctl(self->hidraw_fd, bad_cmd, buf);
378	ASSERT_LT(err, 0) TH_LOG("ioctl read-write with wrong _IOC_NR (0x42) should have failed");
379	ASSERT_EQ(errno, ENOTTY)
380		TH_LOG("expected ENOTTY for wrong read-write _IOC_NR (0x42), got errno %d", errno);
381}
382
383/*
384 * Test ioctl with incorrect type bits
385 */
386TEST_F(hidraw, ioctl_invalid_type)
387{
388	char buf[256] = {0};
389	int err;
390	unsigned int bad_cmd;
391
392	/*
393	 * craft an ioctl command with wrong _IOC_TYPE bits
394	 */
395	bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'I', 0x01, sizeof(buf)); /* 'I' should be 'H' */
396
397	/* test the ioctl */
398	err = ioctl(self->hidraw_fd, bad_cmd, buf);
399	ASSERT_LT(err, 0) TH_LOG("ioctl with wrong _IOC_TYPE (I) should have failed");
400	ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_NR, got errno %d", errno);
401}
402
403/*
404 * Test HIDIOCGFEATURE ioctl with incorrect _IOC_DIR bits
405 */
406TEST_F(hidraw, ioctl_gfeature_invalid_dir)
407{
408	__u8 buf[10] = {0};
409	int err;
410	unsigned int bad_cmd;
411
412	/* set report ID 1 in first byte */
413	buf[0] = 1;
414
415	/*
416	 * craft an ioctl command with wrong _IOC_DIR bits
417	 * HIDIOCGFEATURE should have _IOC_WRITE|_IOC_READ, let's use only _IOC_WRITE
418	 */
419	bad_cmd = _IOC(_IOC_WRITE, 'H', 0x07, sizeof(buf)); /* should be _IOC_WRITE|_IOC_READ */
420
421	/* try to get feature report with wrong direction bits */
422	err = ioctl(self->hidraw_fd, bad_cmd, buf);
423	ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE with wrong _IOC_DIR should have failed");
424	ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno);
425
426	/* also test with only _IOC_READ */
427	bad_cmd = _IOC(_IOC_READ, 'H', 0x07, sizeof(buf)); /* should be _IOC_WRITE|_IOC_READ */
428
429	err = ioctl(self->hidraw_fd, bad_cmd, buf);
430	ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE with wrong _IOC_DIR should have failed");
431	ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno);
432}
433
434/*
435 * Test read-only ioctl with incorrect _IOC_DIR bits
436 */
437TEST_F(hidraw, ioctl_readonly_invalid_dir)
438{
439	char buf[256] = {0};
440	int err;
441	unsigned int bad_cmd;
442
443	/*
444	 * craft an ioctl command with wrong _IOC_DIR bits
445	 * HIDIOCGRAWNAME should have _IOC_READ, let's use _IOC_WRITE
446	 */
447	bad_cmd = _IOC(_IOC_WRITE, 'H', 0x04, sizeof(buf)); /* should be _IOC_READ */
448
449	/* try to get device name with wrong direction bits */
450	err = ioctl(self->hidraw_fd, bad_cmd, buf);
451	ASSERT_LT(err, 0) TH_LOG("HIDIOCGRAWNAME with wrong _IOC_DIR should have failed");
452	ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno);
453
454	/* also test with _IOC_WRITE|_IOC_READ */
455	bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x04, sizeof(buf)); /* should be only _IOC_READ */
456
457	err = ioctl(self->hidraw_fd, bad_cmd, buf);
458	ASSERT_LT(err, 0) TH_LOG("HIDIOCGRAWNAME with wrong _IOC_DIR should have failed");
459	ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno);
460}
461
462/*
463 * Test HIDIOCSFEATURE ioctl to set feature report
464 */
465TEST_F(hidraw, ioctl_sfeature)
466{
467	__u8 buf[10] = {0};
468	int err;
469
470	/* prepare feature report data */
471	buf[0] = 1; /* report ID */
472	buf[1] = 0x42;
473	buf[2] = 0x24;
474
475	/* set feature report */
476	err = ioctl(self->hidraw_fd, HIDIOCSFEATURE(3), buf);
477	ASSERT_EQ(err, 3) TH_LOG("HIDIOCSFEATURE ioctl failed, got %d", err);
478
479	/*
480	 * Note: The uhid mock doesn't validate the set report data,
481	 * so we just verify the ioctl succeeds
482	 */
483}
484
485/*
486 * Test HIDIOCGINPUT ioctl to get input report
487 */
488TEST_F(hidraw, ioctl_ginput)
489{
490	__u8 buf[10] = {0};
491	int err;
492
493	/* set report ID 1 in first byte */
494	buf[0] = 1;
495
496	/* get input report */
497	err = ioctl(self->hidraw_fd, HIDIOCGINPUT(sizeof(buf)), buf);
498	ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGINPUT ioctl failed, got %d", err);
499
500	/* verify we got the expected input data */
501	ASSERT_EQ(buf[0], feature_data[0])
502		TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]);
503	ASSERT_EQ(buf[1], feature_data[1])
504		TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]);
505}
506
507/*
508 * Test HIDIOCGINPUT ioctl with invalid report ID
509 */
510TEST_F(hidraw, ioctl_ginput_invalid)
511{
512	__u8 buf[10] = {0};
513	int err;
514
515	/* set invalid report ID (not 1) */
516	buf[0] = 2;
517
518	/* try to get input report */
519	err = ioctl(self->hidraw_fd, HIDIOCGINPUT(sizeof(buf)), buf);
520	ASSERT_LT(err, 0) TH_LOG("HIDIOCGINPUT should have failed with invalid report ID");
521	ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno);
522}
523
524/*
525 * Test HIDIOCSINPUT ioctl to set input report
526 */
527TEST_F(hidraw, ioctl_sinput)
528{
529	__u8 buf[10] = {0};
530	int err;
531
532	/* prepare input report data */
533	buf[0] = 1; /* report ID */
534	buf[1] = 0x55;
535	buf[2] = 0xAA;
536
537	/* set input report */
538	err = ioctl(self->hidraw_fd, HIDIOCSINPUT(3), buf);
539	ASSERT_EQ(err, 3) TH_LOG("HIDIOCSINPUT ioctl failed, got %d", err);
540
541	/*
542	 * Note: The uhid mock doesn't validate the set report data,
543	 * so we just verify the ioctl succeeds
544	 */
545}
546
547/*
548 * Test HIDIOCGOUTPUT ioctl to get output report
549 */
550TEST_F(hidraw, ioctl_goutput)
551{
552	__u8 buf[10] = {0};
553	int err;
554
555	/* set report ID 1 in first byte */
556	buf[0] = 1;
557
558	/* get output report */
559	err = ioctl(self->hidraw_fd, HIDIOCGOUTPUT(sizeof(buf)), buf);
560	ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGOUTPUT ioctl failed, got %d", err);
561
562	/* verify we got the expected output data */
563	ASSERT_EQ(buf[0], feature_data[0])
564		TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]);
565	ASSERT_EQ(buf[1], feature_data[1])
566		TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]);
567}
568
569/*
570 * Test HIDIOCGOUTPUT ioctl with invalid report ID
571 */
572TEST_F(hidraw, ioctl_goutput_invalid)
573{
574	__u8 buf[10] = {0};
575	int err;
576
577	/* set invalid report ID (not 1) */
578	buf[0] = 2;
579
580	/* try to get output report */
581	err = ioctl(self->hidraw_fd, HIDIOCGOUTPUT(sizeof(buf)), buf);
582	ASSERT_LT(err, 0) TH_LOG("HIDIOCGOUTPUT should have failed with invalid report ID");
583	ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno);
584}
585
586/*
587 * Test HIDIOCSOUTPUT ioctl to set output report
588 */
589TEST_F(hidraw, ioctl_soutput)
590{
591	__u8 buf[10] = {0};
592	int err;
593
594	/* prepare output report data */
595	buf[0] = 1; /* report ID */
596	buf[1] = 0x33;
597	buf[2] = 0xCC;
598
599	/* set output report */
600	err = ioctl(self->hidraw_fd, HIDIOCSOUTPUT(3), buf);
601	ASSERT_EQ(err, 3) TH_LOG("HIDIOCSOUTPUT ioctl failed, got %d", err);
602
603	/*
604	 * Note: The uhid mock doesn't validate the set report data,
605	 * so we just verify the ioctl succeeds
606	 */
607}
608
609/*
610 * Test HIDIOCGRAWNAME ioctl to get device name string
611 */
612TEST_F(hidraw, ioctl_rawname)
613{
614	char name[256] = {0};
615	char expected_name[64];
616	int err;
617
618	/* get device name */
619	err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(name)), name);
620	ASSERT_GT(err, 0) TH_LOG("HIDIOCGRAWNAME ioctl failed, got %d", err);
621
622	/* construct expected name based on device id */
623	snprintf(expected_name, sizeof(expected_name), "test-uhid-device-%d", self->hid.dev_id);
624
625	/* verify the name matches expected pattern */
626	ASSERT_EQ(strcmp(name, expected_name), 0)
627		TH_LOG("expected name '%s', got '%s'", expected_name, name);
628}
629
630/*
631 * Test HIDIOCGRAWPHYS ioctl to get device physical address string
632 */
633TEST_F(hidraw, ioctl_rawphys)
634{
635	char phys[256] = {0};
636	char expected_phys[64];
637	int err;
638
639	/* get device physical address */
640	err = ioctl(self->hidraw_fd, HIDIOCGRAWPHYS(sizeof(phys)), phys);
641	ASSERT_GT(err, 0) TH_LOG("HIDIOCGRAWPHYS ioctl failed, got %d", err);
642
643	/* construct expected phys based on device id */
644	snprintf(expected_phys, sizeof(expected_phys), "%d", self->hid.dev_id);
645
646	/* verify the phys matches expected value */
647	ASSERT_EQ(strcmp(phys, expected_phys), 0)
648		TH_LOG("expected phys '%s', got '%s'", expected_phys, phys);
649}
650
651/*
652 * Test HIDIOCGRAWUNIQ ioctl to get device unique identifier string
653 */
654TEST_F(hidraw, ioctl_rawuniq)
655{
656	char uniq[256] = {0};
657	int err;
658
659	/* get device unique identifier */
660	err = ioctl(self->hidraw_fd, HIDIOCGRAWUNIQ(sizeof(uniq)), uniq);
661	ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWUNIQ ioctl failed, got %d", err);
662
663	/* uniq is typically empty in our test setup */
664	ASSERT_EQ(strlen(uniq), 0) TH_LOG("expected empty uniq, got '%s'", uniq);
665}
666
667/*
668 * Test device string ioctls with small buffer sizes
669 */
670TEST_F(hidraw, ioctl_strings_small_buffer)
671{
672	char small_buf[8] = {0};
673	char expected_name[64];
674	int err;
675
676	/* test HIDIOCGRAWNAME with small buffer */
677	err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(small_buf)), small_buf);
678	ASSERT_EQ(err, sizeof(small_buf))
679		TH_LOG("HIDIOCGRAWNAME with small buffer failed, got %d", err);
680
681	/* construct expected truncated name */
682	snprintf(expected_name, sizeof(expected_name), "test-uhid-device-%d", self->hid.dev_id);
683
684	/* verify we got truncated name (first 8 chars, no null terminator guaranteed) */
685	ASSERT_EQ(strncmp(small_buf, expected_name, sizeof(small_buf)), 0)
686		TH_LOG("expected truncated name to match first %zu chars", sizeof(small_buf));
687
688	/* Note: hidraw driver doesn't guarantee null termination when buffer is too small */
689}
690
691int main(int argc, char **argv)
692{
693	return test_harness_run(argc, argv);
694}
~/ Read-only file preview.