Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/hid: hidraw: add more coverage for hidraw ioctls

Try to ensure all ioctls are having at least one test.

Most of the scaffholding has been generated by claude-4-sonnet and then
carefully reviewed.

Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Jiri Kosina <jkosina@suse.com>

authored by

Benjamin Tissoires and committed by
Jiri Kosina
bb6c861a 02d6eeed

+352
+6
tools/testing/selftests/hid/hid_common.h
··· 230 230 break; 231 231 case UHID_SET_REPORT: 232 232 UHID_LOG("UHID_SET_REPORT from uhid-dev"); 233 + 234 + answer.type = UHID_SET_REPORT_REPLY; 235 + answer.u.set_report_reply.id = ev.u.set_report.id; 236 + answer.u.set_report_reply.err = 0; /* success */ 237 + 238 + uhid_write(_metadata, fd, &answer); 233 239 break; 234 240 default: 235 241 TH_LOG("Invalid event from uhid-dev: %u", ev.type);
+346
tools/testing/selftests/hid/hidraw.c
··· 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 ··· 216 213 errno); 217 214 218 215 pthread_mutex_unlock(&uhid_output_mtx); 216 + } 217 + 218 + /* 219 + * Test HIDIOCGRDESCSIZE ioctl to get report descriptor size 220 + */ 221 + TEST_F(hidraw, ioctl_rdescsize) 222 + { 223 + int desc_size = 0; 224 + int err; 225 + 226 + /* call HIDIOCGRDESCSIZE ioctl */ 227 + err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size); 228 + ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESCSIZE ioctl failed"); 229 + 230 + /* verify the size matches our test report descriptor */ 231 + ASSERT_EQ(desc_size, sizeof(rdesc)) 232 + TH_LOG("expected size %zu, got %d", sizeof(rdesc), desc_size); 233 + } 234 + 235 + /* 236 + * Test HIDIOCGRDESC ioctl to get report descriptor data 237 + */ 238 + TEST_F(hidraw, ioctl_rdesc) 239 + { 240 + struct hidraw_report_descriptor desc; 241 + int err; 242 + 243 + /* get the full report descriptor */ 244 + desc.size = sizeof(rdesc); 245 + err = ioctl(self->hidraw_fd, HIDIOCGRDESC, &desc); 246 + ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESC ioctl failed"); 247 + 248 + /* verify the descriptor data matches our test descriptor */ 249 + ASSERT_EQ(memcmp(desc.value, rdesc, sizeof(rdesc)), 0) 250 + TH_LOG("report descriptor data mismatch"); 251 + } 252 + 253 + /* 254 + * Test HIDIOCGRDESC ioctl with smaller buffer size 255 + */ 256 + TEST_F(hidraw, ioctl_rdesc_small_buffer) 257 + { 258 + struct hidraw_report_descriptor desc; 259 + int err; 260 + size_t small_size = sizeof(rdesc) / 2; /* request half the descriptor size */ 261 + 262 + /* get partial report descriptor */ 263 + desc.size = small_size; 264 + err = ioctl(self->hidraw_fd, HIDIOCGRDESC, &desc); 265 + ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESC ioctl failed with small buffer"); 266 + 267 + /* verify we got the first part of the descriptor */ 268 + ASSERT_EQ(memcmp(desc.value, rdesc, small_size), 0) 269 + TH_LOG("partial report descriptor data mismatch"); 270 + } 271 + 272 + /* 273 + * Test HIDIOCGRAWINFO ioctl to get device information 274 + */ 275 + TEST_F(hidraw, ioctl_rawinfo) 276 + { 277 + struct hidraw_devinfo devinfo; 278 + int err; 279 + 280 + /* get device info */ 281 + err = ioctl(self->hidraw_fd, HIDIOCGRAWINFO, &devinfo); 282 + ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRAWINFO ioctl failed"); 283 + 284 + /* verify device info matches our test setup */ 285 + ASSERT_EQ(devinfo.bustype, BUS_USB) 286 + TH_LOG("expected bustype 0x03, got 0x%x", devinfo.bustype); 287 + ASSERT_EQ(devinfo.vendor, 0x0001) 288 + TH_LOG("expected vendor 0x0001, got 0x%x", devinfo.vendor); 289 + ASSERT_EQ(devinfo.product, 0x0a37) 290 + TH_LOG("expected product 0x0a37, got 0x%x", devinfo.product); 291 + } 292 + 293 + /* 294 + * Test HIDIOCGFEATURE ioctl to get feature report 295 + */ 296 + TEST_F(hidraw, ioctl_gfeature) 297 + { 298 + __u8 buf[10] = {0}; 299 + int err; 300 + 301 + /* set report ID 1 in first byte */ 302 + buf[0] = 1; 303 + 304 + /* get feature report */ 305 + err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); 306 + ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGFEATURE ioctl failed, got %d", err); 307 + 308 + /* verify we got the expected feature data */ 309 + ASSERT_EQ(buf[0], feature_data[0]) 310 + TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]); 311 + ASSERT_EQ(buf[1], feature_data[1]) 312 + TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]); 313 + } 314 + 315 + /* 316 + * Test HIDIOCGFEATURE ioctl with invalid report ID 317 + */ 318 + TEST_F(hidraw, ioctl_gfeature_invalid) 319 + { 320 + __u8 buf[10] = {0}; 321 + int err; 322 + 323 + /* set invalid report ID (not 1) */ 324 + buf[0] = 2; 325 + 326 + /* try to get feature report */ 327 + err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); 328 + ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE should have failed with invalid report ID"); 329 + ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno); 330 + } 331 + 332 + /* 333 + * Test HIDIOCSFEATURE ioctl to set feature report 334 + */ 335 + TEST_F(hidraw, ioctl_sfeature) 336 + { 337 + __u8 buf[10] = {0}; 338 + int err; 339 + 340 + /* prepare feature report data */ 341 + buf[0] = 1; /* report ID */ 342 + buf[1] = 0x42; 343 + buf[2] = 0x24; 344 + 345 + /* set feature report */ 346 + err = ioctl(self->hidraw_fd, HIDIOCSFEATURE(3), buf); 347 + ASSERT_EQ(err, 3) TH_LOG("HIDIOCSFEATURE ioctl failed, got %d", err); 348 + 349 + /* 350 + * Note: The uhid mock doesn't validate the set report data, 351 + * so we just verify the ioctl succeeds 352 + */ 353 + } 354 + 355 + /* 356 + * Test HIDIOCGINPUT ioctl to get input report 357 + */ 358 + TEST_F(hidraw, ioctl_ginput) 359 + { 360 + __u8 buf[10] = {0}; 361 + int err; 362 + 363 + /* set report ID 1 in first byte */ 364 + buf[0] = 1; 365 + 366 + /* get input report */ 367 + err = ioctl(self->hidraw_fd, HIDIOCGINPUT(sizeof(buf)), buf); 368 + ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGINPUT ioctl failed, got %d", err); 369 + 370 + /* verify we got the expected input data */ 371 + ASSERT_EQ(buf[0], feature_data[0]) 372 + TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]); 373 + ASSERT_EQ(buf[1], feature_data[1]) 374 + TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]); 375 + } 376 + 377 + /* 378 + * Test HIDIOCGINPUT ioctl with invalid report ID 379 + */ 380 + TEST_F(hidraw, ioctl_ginput_invalid) 381 + { 382 + __u8 buf[10] = {0}; 383 + int err; 384 + 385 + /* set invalid report ID (not 1) */ 386 + buf[0] = 2; 387 + 388 + /* try to get input report */ 389 + err = ioctl(self->hidraw_fd, HIDIOCGINPUT(sizeof(buf)), buf); 390 + ASSERT_LT(err, 0) TH_LOG("HIDIOCGINPUT should have failed with invalid report ID"); 391 + ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno); 392 + } 393 + 394 + /* 395 + * Test HIDIOCSINPUT ioctl to set input report 396 + */ 397 + TEST_F(hidraw, ioctl_sinput) 398 + { 399 + __u8 buf[10] = {0}; 400 + int err; 401 + 402 + /* prepare input report data */ 403 + buf[0] = 1; /* report ID */ 404 + buf[1] = 0x55; 405 + buf[2] = 0xAA; 406 + 407 + /* set input report */ 408 + err = ioctl(self->hidraw_fd, HIDIOCSINPUT(3), buf); 409 + ASSERT_EQ(err, 3) TH_LOG("HIDIOCSINPUT ioctl failed, got %d", err); 410 + 411 + /* 412 + * Note: The uhid mock doesn't validate the set report data, 413 + * so we just verify the ioctl succeeds 414 + */ 415 + } 416 + 417 + /* 418 + * Test HIDIOCGOUTPUT ioctl to get output report 419 + */ 420 + TEST_F(hidraw, ioctl_goutput) 421 + { 422 + __u8 buf[10] = {0}; 423 + int err; 424 + 425 + /* set report ID 1 in first byte */ 426 + buf[0] = 1; 427 + 428 + /* get output report */ 429 + err = ioctl(self->hidraw_fd, HIDIOCGOUTPUT(sizeof(buf)), buf); 430 + ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGOUTPUT ioctl failed, got %d", err); 431 + 432 + /* verify we got the expected output data */ 433 + ASSERT_EQ(buf[0], feature_data[0]) 434 + TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]); 435 + ASSERT_EQ(buf[1], feature_data[1]) 436 + TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]); 437 + } 438 + 439 + /* 440 + * Test HIDIOCGOUTPUT ioctl with invalid report ID 441 + */ 442 + TEST_F(hidraw, ioctl_goutput_invalid) 443 + { 444 + __u8 buf[10] = {0}; 445 + int err; 446 + 447 + /* set invalid report ID (not 1) */ 448 + buf[0] = 2; 449 + 450 + /* try to get output report */ 451 + err = ioctl(self->hidraw_fd, HIDIOCGOUTPUT(sizeof(buf)), buf); 452 + ASSERT_LT(err, 0) TH_LOG("HIDIOCGOUTPUT should have failed with invalid report ID"); 453 + ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno); 454 + } 455 + 456 + /* 457 + * Test HIDIOCSOUTPUT ioctl to set output report 458 + */ 459 + TEST_F(hidraw, ioctl_soutput) 460 + { 461 + __u8 buf[10] = {0}; 462 + int err; 463 + 464 + /* prepare output report data */ 465 + buf[0] = 1; /* report ID */ 466 + buf[1] = 0x33; 467 + buf[2] = 0xCC; 468 + 469 + /* set output report */ 470 + err = ioctl(self->hidraw_fd, HIDIOCSOUTPUT(3), buf); 471 + ASSERT_EQ(err, 3) TH_LOG("HIDIOCSOUTPUT ioctl failed, got %d", err); 472 + 473 + /* 474 + * Note: The uhid mock doesn't validate the set report data, 475 + * so we just verify the ioctl succeeds 476 + */ 477 + } 478 + 479 + /* 480 + * Test HIDIOCGRAWNAME ioctl to get device name string 481 + */ 482 + TEST_F(hidraw, ioctl_rawname) 483 + { 484 + char name[256] = {0}; 485 + char expected_name[64]; 486 + int err; 487 + 488 + /* get device name */ 489 + err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(name)), name); 490 + ASSERT_GT(err, 0) TH_LOG("HIDIOCGRAWNAME ioctl failed, got %d", err); 491 + 492 + /* construct expected name based on device id */ 493 + snprintf(expected_name, sizeof(expected_name), "test-uhid-device-%d", self->hid.dev_id); 494 + 495 + /* verify the name matches expected pattern */ 496 + ASSERT_EQ(strcmp(name, expected_name), 0) 497 + TH_LOG("expected name '%s', got '%s'", expected_name, name); 498 + } 499 + 500 + /* 501 + * Test HIDIOCGRAWPHYS ioctl to get device physical address string 502 + */ 503 + TEST_F(hidraw, ioctl_rawphys) 504 + { 505 + char phys[256] = {0}; 506 + char expected_phys[64]; 507 + int err; 508 + 509 + /* get device physical address */ 510 + err = ioctl(self->hidraw_fd, HIDIOCGRAWPHYS(sizeof(phys)), phys); 511 + ASSERT_GT(err, 0) TH_LOG("HIDIOCGRAWPHYS ioctl failed, got %d", err); 512 + 513 + /* construct expected phys based on device id */ 514 + snprintf(expected_phys, sizeof(expected_phys), "%d", self->hid.dev_id); 515 + 516 + /* verify the phys matches expected value */ 517 + ASSERT_EQ(strcmp(phys, expected_phys), 0) 518 + TH_LOG("expected phys '%s', got '%s'", expected_phys, phys); 519 + } 520 + 521 + /* 522 + * Test HIDIOCGRAWUNIQ ioctl to get device unique identifier string 523 + */ 524 + TEST_F(hidraw, ioctl_rawuniq) 525 + { 526 + char uniq[256] = {0}; 527 + int err; 528 + 529 + /* get device unique identifier */ 530 + err = ioctl(self->hidraw_fd, HIDIOCGRAWUNIQ(sizeof(uniq)), uniq); 531 + ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWUNIQ ioctl failed, got %d", err); 532 + 533 + /* uniq is typically empty in our test setup */ 534 + ASSERT_EQ(strlen(uniq), 0) TH_LOG("expected empty uniq, got '%s'", uniq); 535 + } 536 + 537 + /* 538 + * Test device string ioctls with small buffer sizes 539 + */ 540 + TEST_F(hidraw, ioctl_strings_small_buffer) 541 + { 542 + char small_buf[8] = {0}; 543 + char expected_name[64]; 544 + int err; 545 + 546 + /* test HIDIOCGRAWNAME with small buffer */ 547 + err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(small_buf)), small_buf); 548 + ASSERT_EQ(err, sizeof(small_buf)) 549 + TH_LOG("HIDIOCGRAWNAME with small buffer failed, got %d", err); 550 + 551 + /* construct expected truncated name */ 552 + snprintf(expected_name, sizeof(expected_name), "test-uhid-device-%d", self->hid.dev_id); 553 + 554 + /* verify we got truncated name (first 8 chars, no null terminator guaranteed) */ 555 + ASSERT_EQ(strncmp(small_buf, expected_name, sizeof(small_buf)), 0) 556 + TH_LOG("expected truncated name to match first %zu chars", sizeof(small_buf)); 557 + 558 + /* Note: hidraw driver doesn't guarantee null termination when buffer is too small */ 219 559 } 220 560 221 561 int main(int argc, char **argv)