|
2 | 2 | /* Copyright (c) 2022-2024 Red Hat */ |
3 | 3 |
|
4 | 4 | #include "hid_common.h" |
| 5 | +#include <linux/input.h> |
| 6 | +#include <string.h> |
| 7 | +#include <sys/ioctl.h> |
5 | 8 |
|
6 | 9 | /* for older kernels */ |
7 | 10 | #ifndef HIDIOCREVOKE |
@@ -215,6 +218,349 @@ TEST_F(hidraw, write_event_revoked) |
215 | 218 | pthread_mutex_unlock(&uhid_output_mtx); |
216 | 219 | } |
217 | 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 HIDIOCSFEATURE ioctl to set feature report |
| 337 | + */ |
| 338 | +TEST_F(hidraw, ioctl_sfeature) |
| 339 | +{ |
| 340 | + __u8 buf[10] = {0}; |
| 341 | + int err; |
| 342 | + |
| 343 | + /* prepare feature report data */ |
| 344 | + buf[0] = 1; /* report ID */ |
| 345 | + buf[1] = 0x42; |
| 346 | + buf[2] = 0x24; |
| 347 | + |
| 348 | + /* set feature report */ |
| 349 | + err = ioctl(self->hidraw_fd, HIDIOCSFEATURE(3), buf); |
| 350 | + ASSERT_EQ(err, 3) TH_LOG("HIDIOCSFEATURE ioctl failed, got %d", err); |
| 351 | + |
| 352 | + /* |
| 353 | + * Note: The uhid mock doesn't validate the set report data, |
| 354 | + * so we just verify the ioctl succeeds |
| 355 | + */ |
| 356 | +} |
| 357 | + |
| 358 | +/* |
| 359 | + * Test HIDIOCGINPUT ioctl to get input report |
| 360 | + */ |
| 361 | +TEST_F(hidraw, ioctl_ginput) |
| 362 | +{ |
| 363 | + __u8 buf[10] = {0}; |
| 364 | + int err; |
| 365 | + |
| 366 | + /* set report ID 1 in first byte */ |
| 367 | + buf[0] = 1; |
| 368 | + |
| 369 | + /* get input report */ |
| 370 | + err = ioctl(self->hidraw_fd, HIDIOCGINPUT(sizeof(buf)), buf); |
| 371 | + ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGINPUT ioctl failed, got %d", err); |
| 372 | + |
| 373 | + /* verify we got the expected input data */ |
| 374 | + ASSERT_EQ(buf[0], feature_data[0]) |
| 375 | + TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]); |
| 376 | + ASSERT_EQ(buf[1], feature_data[1]) |
| 377 | + TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]); |
| 378 | +} |
| 379 | + |
| 380 | +/* |
| 381 | + * Test HIDIOCGINPUT ioctl with invalid report ID |
| 382 | + */ |
| 383 | +TEST_F(hidraw, ioctl_ginput_invalid) |
| 384 | +{ |
| 385 | + __u8 buf[10] = {0}; |
| 386 | + int err; |
| 387 | + |
| 388 | + /* set invalid report ID (not 1) */ |
| 389 | + buf[0] = 2; |
| 390 | + |
| 391 | + /* try to get input report */ |
| 392 | + err = ioctl(self->hidraw_fd, HIDIOCGINPUT(sizeof(buf)), buf); |
| 393 | + ASSERT_LT(err, 0) TH_LOG("HIDIOCGINPUT should have failed with invalid report ID"); |
| 394 | + ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno); |
| 395 | +} |
| 396 | + |
| 397 | +/* |
| 398 | + * Test HIDIOCSINPUT ioctl to set input report |
| 399 | + */ |
| 400 | +TEST_F(hidraw, ioctl_sinput) |
| 401 | +{ |
| 402 | + __u8 buf[10] = {0}; |
| 403 | + int err; |
| 404 | + |
| 405 | + /* prepare input report data */ |
| 406 | + buf[0] = 1; /* report ID */ |
| 407 | + buf[1] = 0x55; |
| 408 | + buf[2] = 0xAA; |
| 409 | + |
| 410 | + /* set input report */ |
| 411 | + err = ioctl(self->hidraw_fd, HIDIOCSINPUT(3), buf); |
| 412 | + ASSERT_EQ(err, 3) TH_LOG("HIDIOCSINPUT ioctl failed, got %d", err); |
| 413 | + |
| 414 | + /* |
| 415 | + * Note: The uhid mock doesn't validate the set report data, |
| 416 | + * so we just verify the ioctl succeeds |
| 417 | + */ |
| 418 | +} |
| 419 | + |
| 420 | +/* |
| 421 | + * Test HIDIOCGOUTPUT ioctl to get output report |
| 422 | + */ |
| 423 | +TEST_F(hidraw, ioctl_goutput) |
| 424 | +{ |
| 425 | + __u8 buf[10] = {0}; |
| 426 | + int err; |
| 427 | + |
| 428 | + /* set report ID 1 in first byte */ |
| 429 | + buf[0] = 1; |
| 430 | + |
| 431 | + /* get output report */ |
| 432 | + err = ioctl(self->hidraw_fd, HIDIOCGOUTPUT(sizeof(buf)), buf); |
| 433 | + ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGOUTPUT ioctl failed, got %d", err); |
| 434 | + |
| 435 | + /* verify we got the expected output data */ |
| 436 | + ASSERT_EQ(buf[0], feature_data[0]) |
| 437 | + TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]); |
| 438 | + ASSERT_EQ(buf[1], feature_data[1]) |
| 439 | + TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]); |
| 440 | +} |
| 441 | + |
| 442 | +/* |
| 443 | + * Test HIDIOCGOUTPUT ioctl with invalid report ID |
| 444 | + */ |
| 445 | +TEST_F(hidraw, ioctl_goutput_invalid) |
| 446 | +{ |
| 447 | + __u8 buf[10] = {0}; |
| 448 | + int err; |
| 449 | + |
| 450 | + /* set invalid report ID (not 1) */ |
| 451 | + buf[0] = 2; |
| 452 | + |
| 453 | + /* try to get output report */ |
| 454 | + err = ioctl(self->hidraw_fd, HIDIOCGOUTPUT(sizeof(buf)), buf); |
| 455 | + ASSERT_LT(err, 0) TH_LOG("HIDIOCGOUTPUT should have failed with invalid report ID"); |
| 456 | + ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno); |
| 457 | +} |
| 458 | + |
| 459 | +/* |
| 460 | + * Test HIDIOCSOUTPUT ioctl to set output report |
| 461 | + */ |
| 462 | +TEST_F(hidraw, ioctl_soutput) |
| 463 | +{ |
| 464 | + __u8 buf[10] = {0}; |
| 465 | + int err; |
| 466 | + |
| 467 | + /* prepare output report data */ |
| 468 | + buf[0] = 1; /* report ID */ |
| 469 | + buf[1] = 0x33; |
| 470 | + buf[2] = 0xCC; |
| 471 | + |
| 472 | + /* set output report */ |
| 473 | + err = ioctl(self->hidraw_fd, HIDIOCSOUTPUT(3), buf); |
| 474 | + ASSERT_EQ(err, 3) TH_LOG("HIDIOCSOUTPUT ioctl failed, got %d", err); |
| 475 | + |
| 476 | + /* |
| 477 | + * Note: The uhid mock doesn't validate the set report data, |
| 478 | + * so we just verify the ioctl succeeds |
| 479 | + */ |
| 480 | +} |
| 481 | + |
| 482 | +/* |
| 483 | + * Test HIDIOCGRAWNAME ioctl to get device name string |
| 484 | + */ |
| 485 | +TEST_F(hidraw, ioctl_rawname) |
| 486 | +{ |
| 487 | + char name[256] = {0}; |
| 488 | + char expected_name[64]; |
| 489 | + int err; |
| 490 | + |
| 491 | + /* get device name */ |
| 492 | + err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(name)), name); |
| 493 | + ASSERT_GT(err, 0) TH_LOG("HIDIOCGRAWNAME ioctl failed, got %d", err); |
| 494 | + |
| 495 | + /* construct expected name based on device id */ |
| 496 | + snprintf(expected_name, sizeof(expected_name), "test-uhid-device-%d", self->hid.dev_id); |
| 497 | + |
| 498 | + /* verify the name matches expected pattern */ |
| 499 | + ASSERT_EQ(strcmp(name, expected_name), 0) |
| 500 | + TH_LOG("expected name '%s', got '%s'", expected_name, name); |
| 501 | +} |
| 502 | + |
| 503 | +/* |
| 504 | + * Test HIDIOCGRAWPHYS ioctl to get device physical address string |
| 505 | + */ |
| 506 | +TEST_F(hidraw, ioctl_rawphys) |
| 507 | +{ |
| 508 | + char phys[256] = {0}; |
| 509 | + char expected_phys[64]; |
| 510 | + int err; |
| 511 | + |
| 512 | + /* get device physical address */ |
| 513 | + err = ioctl(self->hidraw_fd, HIDIOCGRAWPHYS(sizeof(phys)), phys); |
| 514 | + ASSERT_GT(err, 0) TH_LOG("HIDIOCGRAWPHYS ioctl failed, got %d", err); |
| 515 | + |
| 516 | + /* construct expected phys based on device id */ |
| 517 | + snprintf(expected_phys, sizeof(expected_phys), "%d", self->hid.dev_id); |
| 518 | + |
| 519 | + /* verify the phys matches expected value */ |
| 520 | + ASSERT_EQ(strcmp(phys, expected_phys), 0) |
| 521 | + TH_LOG("expected phys '%s', got '%s'", expected_phys, phys); |
| 522 | +} |
| 523 | + |
| 524 | +/* |
| 525 | + * Test HIDIOCGRAWUNIQ ioctl to get device unique identifier string |
| 526 | + */ |
| 527 | +TEST_F(hidraw, ioctl_rawuniq) |
| 528 | +{ |
| 529 | + char uniq[256] = {0}; |
| 530 | + int err; |
| 531 | + |
| 532 | + /* get device unique identifier */ |
| 533 | + err = ioctl(self->hidraw_fd, HIDIOCGRAWUNIQ(sizeof(uniq)), uniq); |
| 534 | + ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWUNIQ ioctl failed, got %d", err); |
| 535 | + |
| 536 | + /* uniq is typically empty in our test setup */ |
| 537 | + ASSERT_EQ(strlen(uniq), 0) TH_LOG("expected empty uniq, got '%s'", uniq); |
| 538 | +} |
| 539 | + |
| 540 | +/* |
| 541 | + * Test device string ioctls with small buffer sizes |
| 542 | + */ |
| 543 | +TEST_F(hidraw, ioctl_strings_small_buffer) |
| 544 | +{ |
| 545 | + char small_buf[8] = {0}; |
| 546 | + char expected_name[64]; |
| 547 | + int err; |
| 548 | + |
| 549 | + /* test HIDIOCGRAWNAME with small buffer */ |
| 550 | + err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(small_buf)), small_buf); |
| 551 | + ASSERT_EQ(err, sizeof(small_buf)) |
| 552 | + TH_LOG("HIDIOCGRAWNAME with small buffer failed, got %d", err); |
| 553 | + |
| 554 | + /* construct expected truncated name */ |
| 555 | + snprintf(expected_name, sizeof(expected_name), "test-uhid-device-%d", self->hid.dev_id); |
| 556 | + |
| 557 | + /* verify we got truncated name (first 8 chars, no null terminator guaranteed) */ |
| 558 | + ASSERT_EQ(strncmp(small_buf, expected_name, sizeof(small_buf)), 0) |
| 559 | + TH_LOG("expected truncated name to match first %zu chars", sizeof(small_buf)); |
| 560 | + |
| 561 | + /* Note: hidraw driver doesn't guarantee null termination when buffer is too small */ |
| 562 | +} |
| 563 | + |
218 | 564 | int main(int argc, char **argv) |
219 | 565 | { |
220 | 566 | return test_harness_run(argc, argv); |
|
0 commit comments