Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/hid/hidraw.c
29524 views
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
14
FIXTURE(hidraw) {
15
struct uhid_device hid;
16
int hidraw_fd;
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->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
38
FIXTURE_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
*/
53
TEST_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
*/
61
TEST_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
*/
83
TEST_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
*/
120
TEST_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
*/
138
TEST_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
*/
179
TEST_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
*/
224
TEST_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
*/
241
TEST_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
*/
259
TEST_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
*/
278
TEST_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
*/
299
TEST_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
*/
321
TEST_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
*/
338
TEST_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
*/
386
TEST_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
*/
406
TEST_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
*/
437
TEST_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
*/
465
TEST_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
*/
488
TEST_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
*/
510
TEST_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
*/
527
TEST_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
*/
550
TEST_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
*/
572
TEST_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
*/
589
TEST_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
*/
612
TEST_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
*/
633
TEST_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
*/
654
TEST_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
*/
670
TEST_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
691
int main(int argc, char **argv)
692
{
693
return test_harness_run(argc, argv);
694
}
695
696