diff options
Diffstat (limited to 'usr/src/dkms_source_tree/hid-debug.c')
| -rw-r--r-- | usr/src/dkms_source_tree/hid-debug.c | 465 |
1 files changed, 386 insertions, 79 deletions
diff --git a/usr/src/dkms_source_tree/hid-debug.c b/usr/src/dkms_source_tree/hid-debug.c index 47ac1a7..850d02a 100644 --- a/usr/src/dkms_source_tree/hid-debug.c +++ b/usr/src/dkms_source_tree/hid-debug.c | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de> | 2 | * (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de> |
| 3 | * (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz> | 3 | * (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz> |
| 4 | * (c) 2007 Jiri Kosina | 4 | * (c) 2007-2009 Jiri Kosina |
| 5 | * | 5 | * |
| 6 | * Some debug stuff for the HID parser. | 6 | * HID debugging support |
| 7 | */ | 7 | */ |
| 8 | 8 | ||
| 9 | /* | 9 | /* |
| @@ -26,9 +26,18 @@ | |||
| 26 | * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic | 26 | * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic |
| 27 | */ | 27 | */ |
| 28 | 28 | ||
| 29 | #include <linux/debugfs.h> | ||
| 30 | #include <linux/seq_file.h> | ||
| 31 | #include <linux/sched.h> | ||
| 32 | #include <linux/slab.h> | ||
| 33 | #include <linux/uaccess.h> | ||
| 34 | #include <linux/poll.h> | ||
| 35 | |||
| 29 | #include <linux/hid.h> | 36 | #include <linux/hid.h> |
| 30 | #include <linux/hid-debug.h> | 37 | #include <linux/hid-debug.h> |
| 31 | 38 | ||
| 39 | static struct dentry *hid_debug_root; | ||
| 40 | |||
| 32 | struct hid_usage_entry { | 41 | struct hid_usage_entry { |
| 33 | unsigned page; | 42 | unsigned page; |
| 34 | unsigned usage; | 43 | unsigned usage; |
| @@ -137,6 +146,14 @@ static const struct hid_usage_entry hid_usage_table[] = { | |||
| 137 | {0, 0x44, "BarrelSwitch"}, | 146 | {0, 0x44, "BarrelSwitch"}, |
| 138 | {0, 0x45, "Eraser"}, | 147 | {0, 0x45, "Eraser"}, |
| 139 | {0, 0x46, "TabletPick"}, | 148 | {0, 0x46, "TabletPick"}, |
| 149 | {0, 0x47, "Confidence"}, | ||
| 150 | {0, 0x48, "Width"}, | ||
| 151 | {0, 0x49, "Height"}, | ||
| 152 | {0, 0x51, "ContactID"}, | ||
| 153 | {0, 0x52, "InputMode"}, | ||
| 154 | {0, 0x53, "DeviceIndex"}, | ||
| 155 | {0, 0x54, "ContactCount"}, | ||
| 156 | {0, 0x55, "ContactMaximumNumber"}, | ||
| 140 | { 15, 0, "PhysicalInterfaceDevice" }, | 157 | { 15, 0, "PhysicalInterfaceDevice" }, |
| 141 | {0, 0x00, "Undefined"}, | 158 | {0, 0x00, "Undefined"}, |
| 142 | {0, 0x01, "Physical_Interface_Device"}, | 159 | {0, 0x01, "Physical_Interface_Device"}, |
| @@ -331,72 +348,120 @@ static const struct hid_usage_entry hid_usage_table[] = { | |||
| 331 | { 0, 0, NULL } | 348 | { 0, 0, NULL } |
| 332 | }; | 349 | }; |
| 333 | 350 | ||
| 334 | static void resolv_usage_page(unsigned page) { | 351 | /* Either output directly into simple seq_file, or (if f == NULL) |
| 352 | * allocate a separate buffer that will then be passed to the 'events' | ||
| 353 | * ringbuffer. | ||
| 354 | * | ||
| 355 | * This is because these functions can be called both for "one-shot" | ||
| 356 | * "rdesc" while resolving, or for blocking "events". | ||
| 357 | * | ||
| 358 | * This holds both for resolv_usage_page() and hid_resolv_usage(). | ||
| 359 | */ | ||
| 360 | static char *resolv_usage_page(unsigned page, struct seq_file *f) { | ||
| 335 | const struct hid_usage_entry *p; | 361 | const struct hid_usage_entry *p; |
| 362 | char *buf = NULL; | ||
| 363 | |||
| 364 | if (!f) { | ||
| 365 | buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); | ||
| 366 | if (!buf) | ||
| 367 | return ERR_PTR(-ENOMEM); | ||
| 368 | } | ||
| 336 | 369 | ||
| 337 | for (p = hid_usage_table; p->description; p++) | 370 | for (p = hid_usage_table; p->description; p++) |
| 338 | if (p->page == page) { | 371 | if (p->page == page) { |
| 339 | printk("%s", p->description); | 372 | if (!f) { |
| 340 | return; | 373 | snprintf(buf, HID_DEBUG_BUFSIZE, "%s", |
| 374 | p->description); | ||
| 375 | return buf; | ||
| 376 | } | ||
| 377 | else { | ||
| 378 | seq_printf(f, "%s", p->description); | ||
| 379 | return NULL; | ||
| 380 | } | ||
| 341 | } | 381 | } |
| 342 | printk("%04x", page); | 382 | if (!f) |
| 383 | snprintf(buf, HID_DEBUG_BUFSIZE, "%04x", page); | ||
| 384 | else | ||
| 385 | seq_printf(f, "%04x", page); | ||
| 386 | return buf; | ||
| 343 | } | 387 | } |
| 344 | 388 | ||
| 345 | void hid_resolv_usage(unsigned usage) { | 389 | char *hid_resolv_usage(unsigned usage, struct seq_file *f) { |
| 346 | const struct hid_usage_entry *p; | 390 | const struct hid_usage_entry *p; |
| 391 | char *buf = NULL; | ||
| 392 | int len = 0; | ||
| 393 | |||
| 394 | buf = resolv_usage_page(usage >> 16, f); | ||
| 395 | if (IS_ERR(buf)) { | ||
| 396 | printk(KERN_ERR "error allocating HID debug buffer\n"); | ||
| 397 | return NULL; | ||
| 398 | } | ||
| 347 | 399 | ||
| 348 | if (!hid_debug) | ||
| 349 | return; | ||
| 350 | 400 | ||
| 351 | resolv_usage_page(usage >> 16); | 401 | if (!f) { |
| 352 | printk("."); | 402 | len = strlen(buf); |
| 403 | snprintf(buf+len, max(0, HID_DEBUG_BUFSIZE - len), "."); | ||
| 404 | len++; | ||
| 405 | } | ||
| 406 | else { | ||
| 407 | seq_printf(f, "."); | ||
| 408 | } | ||
| 353 | for (p = hid_usage_table; p->description; p++) | 409 | for (p = hid_usage_table; p->description; p++) |
| 354 | if (p->page == (usage >> 16)) { | 410 | if (p->page == (usage >> 16)) { |
| 355 | for(++p; p->description && p->usage != 0; p++) | 411 | for(++p; p->description && p->usage != 0; p++) |
| 356 | if (p->usage == (usage & 0xffff)) { | 412 | if (p->usage == (usage & 0xffff)) { |
| 357 | printk("%s", p->description); | 413 | if (!f) |
| 358 | return; | 414 | snprintf(buf + len, |
| 415 | max(0,HID_DEBUG_BUFSIZE - len - 1), | ||
| 416 | "%s", p->description); | ||
| 417 | else | ||
| 418 | seq_printf(f, | ||
| 419 | "%s", | ||
| 420 | p->description); | ||
| 421 | return buf; | ||
| 359 | } | 422 | } |
| 360 | break; | 423 | break; |
| 361 | } | 424 | } |
| 362 | printk("%04x", usage & 0xffff); | 425 | if (!f) |
| 426 | snprintf(buf + len, max(0, HID_DEBUG_BUFSIZE - len - 1), | ||
| 427 | "%04x", usage & 0xffff); | ||
| 428 | else | ||
| 429 | seq_printf(f, "%04x", usage & 0xffff); | ||
| 430 | return buf; | ||
| 363 | } | 431 | } |
| 364 | EXPORT_SYMBOL_GPL(hid_resolv_usage); | 432 | EXPORT_SYMBOL_GPL(hid_resolv_usage); |
| 365 | 433 | ||
| 366 | static void tab(int n) { | 434 | static void tab(int n, struct seq_file *f) { |
| 367 | printk(KERN_DEBUG "%*s", n, ""); | 435 | seq_printf(f, "%*s", n, ""); |
| 368 | } | 436 | } |
| 369 | 437 | ||
| 370 | void hid_dump_field(struct hid_field *field, int n) { | 438 | void hid_dump_field(struct hid_field *field, int n, struct seq_file *f) { |
| 371 | int j; | 439 | int j; |
| 372 | 440 | ||
| 373 | if (!hid_debug) | ||
| 374 | return; | ||
| 375 | |||
| 376 | if (field->physical) { | 441 | if (field->physical) { |
| 377 | tab(n); | 442 | tab(n, f); |
| 378 | printk("Physical("); | 443 | seq_printf(f, "Physical("); |
| 379 | hid_resolv_usage(field->physical); printk(")\n"); | 444 | hid_resolv_usage(field->physical, f); seq_printf(f, ")\n"); |
| 380 | } | 445 | } |
| 381 | if (field->logical) { | 446 | if (field->logical) { |
| 382 | tab(n); | 447 | tab(n, f); |
| 383 | printk("Logical("); | 448 | seq_printf(f, "Logical("); |
| 384 | hid_resolv_usage(field->logical); printk(")\n"); | 449 | hid_resolv_usage(field->logical, f); seq_printf(f, ")\n"); |
| 385 | } | 450 | } |
| 386 | tab(n); printk("Usage(%d)\n", field->maxusage); | 451 | tab(n, f); seq_printf(f, "Usage(%d)\n", field->maxusage); |
| 387 | for (j = 0; j < field->maxusage; j++) { | 452 | for (j = 0; j < field->maxusage; j++) { |
| 388 | tab(n+2); hid_resolv_usage(field->usage[j].hid); printk("\n"); | 453 | tab(n+2, f); hid_resolv_usage(field->usage[j].hid, f); seq_printf(f, "\n"); |
| 389 | } | 454 | } |
| 390 | if (field->logical_minimum != field->logical_maximum) { | 455 | if (field->logical_minimum != field->logical_maximum) { |
| 391 | tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum); | 456 | tab(n, f); seq_printf(f, "Logical Minimum(%d)\n", field->logical_minimum); |
| 392 | tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum); | 457 | tab(n, f); seq_printf(f, "Logical Maximum(%d)\n", field->logical_maximum); |
| 393 | } | 458 | } |
| 394 | if (field->physical_minimum != field->physical_maximum) { | 459 | if (field->physical_minimum != field->physical_maximum) { |
| 395 | tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum); | 460 | tab(n, f); seq_printf(f, "Physical Minimum(%d)\n", field->physical_minimum); |
| 396 | tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum); | 461 | tab(n, f); seq_printf(f, "Physical Maximum(%d)\n", field->physical_maximum); |
| 397 | } | 462 | } |
| 398 | if (field->unit_exponent) { | 463 | if (field->unit_exponent) { |
| 399 | tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent); | 464 | tab(n, f); seq_printf(f, "Unit Exponent(%d)\n", field->unit_exponent); |
| 400 | } | 465 | } |
| 401 | if (field->unit) { | 466 | if (field->unit) { |
| 402 | static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; | 467 | static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; |
| @@ -417,77 +482,75 @@ void hid_dump_field(struct hid_field *field, int n) { | |||
| 417 | data >>= 4; | 482 | data >>= 4; |
| 418 | 483 | ||
| 419 | if(sys > 4) { | 484 | if(sys > 4) { |
| 420 | tab(n); printk("Unit(Invalid)\n"); | 485 | tab(n, f); seq_printf(f, "Unit(Invalid)\n"); |
| 421 | } | 486 | } |
| 422 | else { | 487 | else { |
| 423 | int earlier_unit = 0; | 488 | int earlier_unit = 0; |
| 424 | 489 | ||
| 425 | tab(n); printk("Unit(%s : ", systems[sys]); | 490 | tab(n, f); seq_printf(f, "Unit(%s : ", systems[sys]); |
| 426 | 491 | ||
| 427 | for (i=1 ; i<sizeof(__u32)*2 ; i++) { | 492 | for (i=1 ; i<sizeof(__u32)*2 ; i++) { |
| 428 | char nibble = data & 0xf; | 493 | char nibble = data & 0xf; |
| 429 | data >>= 4; | 494 | data >>= 4; |
| 430 | if (nibble != 0) { | 495 | if (nibble != 0) { |
| 431 | if(earlier_unit++ > 0) | 496 | if(earlier_unit++ > 0) |
| 432 | printk("*"); | 497 | seq_printf(f, "*"); |
| 433 | printk("%s", units[sys][i]); | 498 | seq_printf(f, "%s", units[sys][i]); |
| 434 | if(nibble != 1) { | 499 | if(nibble != 1) { |
| 435 | /* This is a _signed_ nibble(!) */ | 500 | /* This is a _signed_ nibble(!) */ |
| 436 | 501 | ||
| 437 | int val = nibble & 0x7; | 502 | int val = nibble & 0x7; |
| 438 | if(nibble & 0x08) | 503 | if(nibble & 0x08) |
| 439 | val = -((0x7 & ~val) +1); | 504 | val = -((0x7 & ~val) +1); |
| 440 | printk("^%d", val); | 505 | seq_printf(f, "^%d", val); |
| 441 | } | 506 | } |
| 442 | } | 507 | } |
| 443 | } | 508 | } |
| 444 | printk(")\n"); | 509 | seq_printf(f, ")\n"); |
| 445 | } | 510 | } |
| 446 | } | 511 | } |
| 447 | tab(n); printk("Report Size(%u)\n", field->report_size); | 512 | tab(n, f); seq_printf(f, "Report Size(%u)\n", field->report_size); |
| 448 | tab(n); printk("Report Count(%u)\n", field->report_count); | 513 | tab(n, f); seq_printf(f, "Report Count(%u)\n", field->report_count); |
| 449 | tab(n); printk("Report Offset(%u)\n", field->report_offset); | 514 | tab(n, f); seq_printf(f, "Report Offset(%u)\n", field->report_offset); |
| 450 | 515 | ||
| 451 | tab(n); printk("Flags( "); | 516 | tab(n, f); seq_printf(f, "Flags( "); |
| 452 | j = field->flags; | 517 | j = field->flags; |
| 453 | printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); | 518 | seq_printf(f, "%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); |
| 454 | printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); | 519 | seq_printf(f, "%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); |
| 455 | printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); | 520 | seq_printf(f, "%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); |
| 456 | printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); | 521 | seq_printf(f, "%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); |
| 457 | printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); | 522 | seq_printf(f, "%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); |
| 458 | printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : ""); | 523 | seq_printf(f, "%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : ""); |
| 459 | printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); | 524 | seq_printf(f, "%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); |
| 460 | printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); | 525 | seq_printf(f, "%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); |
| 461 | printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); | 526 | seq_printf(f, "%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); |
| 462 | printk(")\n"); | 527 | seq_printf(f, ")\n"); |
| 463 | } | 528 | } |
| 464 | EXPORT_SYMBOL_GPL(hid_dump_field); | 529 | EXPORT_SYMBOL_GPL(hid_dump_field); |
| 465 | 530 | ||
| 466 | void hid_dump_device(struct hid_device *device) { | 531 | void hid_dump_device(struct hid_device *device, struct seq_file *f) |
| 532 | { | ||
| 467 | struct hid_report_enum *report_enum; | 533 | struct hid_report_enum *report_enum; |
| 468 | struct hid_report *report; | 534 | struct hid_report *report; |
| 469 | struct list_head *list; | 535 | struct list_head *list; |
| 470 | unsigned i,k; | 536 | unsigned i,k; |
| 471 | static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; | 537 | static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; |
| 472 | 538 | ||
| 473 | if (!hid_debug) | ||
| 474 | return; | ||
| 475 | |||
| 476 | for (i = 0; i < HID_REPORT_TYPES; i++) { | 539 | for (i = 0; i < HID_REPORT_TYPES; i++) { |
| 477 | report_enum = device->report_enum + i; | 540 | report_enum = device->report_enum + i; |
| 478 | list = report_enum->report_list.next; | 541 | list = report_enum->report_list.next; |
| 479 | while (list != &report_enum->report_list) { | 542 | while (list != &report_enum->report_list) { |
| 480 | report = (struct hid_report *) list; | 543 | report = (struct hid_report *) list; |
| 481 | tab(2); | 544 | tab(2, f); |
| 482 | printk("%s", table[i]); | 545 | seq_printf(f, "%s", table[i]); |
| 483 | if (report->id) | 546 | if (report->id) |
| 484 | printk("(%d)", report->id); | 547 | seq_printf(f, "(%d)", report->id); |
| 485 | printk("[%s]", table[report->type]); | 548 | seq_printf(f, "[%s]", table[report->type]); |
| 486 | printk("\n"); | 549 | seq_printf(f, "\n"); |
| 487 | for (k = 0; k < report->maxfield; k++) { | 550 | for (k = 0; k < report->maxfield; k++) { |
| 488 | tab(4); | 551 | tab(4, f); |
| 489 | printk("Field(%d)\n", k); | 552 | seq_printf(f, "Field(%d)\n", k); |
| 490 | hid_dump_field(report->field[k], 6); | 553 | hid_dump_field(report->field[k], 6, f); |
| 491 | } | 554 | } |
| 492 | list = list->next; | 555 | list = list->next; |
| 493 | } | 556 | } |
| @@ -495,13 +558,37 @@ void hid_dump_device(struct hid_device *device) { | |||
| 495 | } | 558 | } |
| 496 | EXPORT_SYMBOL_GPL(hid_dump_device); | 559 | EXPORT_SYMBOL_GPL(hid_dump_device); |
| 497 | 560 | ||
| 498 | void hid_dump_input(struct hid_usage *usage, __s32 value) { | 561 | /* enqueue string to 'events' ring buffer */ |
| 499 | if (hid_debug < 2) | 562 | void hid_debug_event(struct hid_device *hdev, char *buf) |
| 563 | { | ||
| 564 | int i; | ||
| 565 | struct hid_debug_list *list; | ||
| 566 | |||
| 567 | list_for_each_entry(list, &hdev->debug_list, node) { | ||
| 568 | for (i = 0; i < strlen(buf); i++) | ||
| 569 | list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] = | ||
| 570 | buf[i]; | ||
| 571 | list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE; | ||
| 572 | } | ||
| 573 | } | ||
| 574 | EXPORT_SYMBOL_GPL(hid_debug_event); | ||
| 575 | |||
| 576 | void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 value) | ||
| 577 | { | ||
| 578 | char *buf; | ||
| 579 | int len; | ||
| 580 | |||
| 581 | buf = hid_resolv_usage(usage->hid, NULL); | ||
| 582 | if (!buf) | ||
| 500 | return; | 583 | return; |
| 584 | len = strlen(buf); | ||
| 585 | snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", value); | ||
| 586 | |||
| 587 | hid_debug_event(hdev, buf); | ||
| 588 | |||
| 589 | kfree(buf); | ||
| 590 | wake_up_interruptible(&hdev->debug_wait); | ||
| 501 | 591 | ||
| 502 | printk(KERN_DEBUG "hid-debug: input "); | ||
| 503 | hid_resolv_usage(usage->hid); | ||
| 504 | printk(" = %d\n", value); | ||
| 505 | } | 592 | } |
| 506 | EXPORT_SYMBOL_GPL(hid_dump_input); | 593 | EXPORT_SYMBOL_GPL(hid_dump_input); |
| 507 | 594 | ||
| @@ -514,9 +601,11 @@ static const char *events[EV_MAX + 1] = { | |||
| 514 | [EV_FF_STATUS] = "ForceFeedbackStatus", | 601 | [EV_FF_STATUS] = "ForceFeedbackStatus", |
| 515 | }; | 602 | }; |
| 516 | 603 | ||
| 517 | static const char *syncs[2] = { | 604 | static const char *syncs[3] = { |
| 518 | [SYN_REPORT] = "Report", [SYN_CONFIG] = "Config", | 605 | [SYN_REPORT] = "Report", [SYN_CONFIG] = "Config", |
| 606 | [SYN_MT_REPORT] = "MT Report", | ||
| 519 | }; | 607 | }; |
| 608 | |||
| 520 | static const char *keys[KEY_MAX + 1] = { | 609 | static const char *keys[KEY_MAX + 1] = { |
| 521 | [KEY_RESERVED] = "Reserved", [KEY_ESC] = "Esc", | 610 | [KEY_RESERVED] = "Reserved", [KEY_ESC] = "Esc", |
| 522 | [KEY_1] = "1", [KEY_2] = "2", | 611 | [KEY_1] = "1", [KEY_2] = "2", |
| @@ -722,7 +811,7 @@ static const char *relatives[REL_MAX + 1] = { | |||
| 722 | [REL_WHEEL] = "Wheel", [REL_MISC] = "Misc", | 811 | [REL_WHEEL] = "Wheel", [REL_MISC] = "Misc", |
| 723 | }; | 812 | }; |
| 724 | 813 | ||
| 725 | static const char *absolutes[ABS_MAX + 1] = { | 814 | static const char *absolutes[ABS_CNT] = { |
| 726 | [ABS_X] = "X", [ABS_Y] = "Y", | 815 | [ABS_X] = "X", [ABS_Y] = "Y", |
| 727 | [ABS_Z] = "Z", [ABS_RX] = "Rx", | 816 | [ABS_Z] = "Z", [ABS_RX] = "Rx", |
| 728 | [ABS_RY] = "Ry", [ABS_RZ] = "Rz", | 817 | [ABS_RY] = "Ry", [ABS_RZ] = "Rz", |
| @@ -734,8 +823,17 @@ static const char *absolutes[ABS_MAX + 1] = { | |||
| 734 | [ABS_HAT2Y] = "Hat2Y", [ABS_HAT3X] = "Hat3X", | 823 | [ABS_HAT2Y] = "Hat2Y", [ABS_HAT3X] = "Hat3X", |
| 735 | [ABS_HAT3Y] = "Hat 3Y", [ABS_PRESSURE] = "Pressure", | 824 | [ABS_HAT3Y] = "Hat 3Y", [ABS_PRESSURE] = "Pressure", |
| 736 | [ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt", | 825 | [ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt", |
| 737 | [ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "Tool Width", | 826 | [ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "ToolWidth", |
| 738 | [ABS_VOLUME] = "Volume", [ABS_MISC] = "Misc", | 827 | [ABS_VOLUME] = "Volume", [ABS_MISC] = "Misc", |
| 828 | [ABS_MT_TOUCH_MAJOR] = "MTMajor", | ||
| 829 | [ABS_MT_TOUCH_MINOR] = "MTMinor", | ||
| 830 | [ABS_MT_WIDTH_MAJOR] = "MTMajorW", | ||
| 831 | [ABS_MT_WIDTH_MINOR] = "MTMinorW", | ||
| 832 | [ABS_MT_ORIENTATION] = "MTOrientation", | ||
| 833 | [ABS_MT_POSITION_X] = "MTPositionX", | ||
| 834 | [ABS_MT_POSITION_Y] = "MTPositionY", | ||
| 835 | [ABS_MT_TOOL_TYPE] = "MTToolType", | ||
| 836 | [ABS_MT_BLOB_ID] = "MTBlobID", | ||
| 739 | }; | 837 | }; |
| 740 | 838 | ||
| 741 | static const char *misc[MSC_MAX + 1] = { | 839 | static const char *misc[MSC_MAX + 1] = { |
| @@ -767,12 +865,221 @@ static const char **names[EV_MAX + 1] = { | |||
| 767 | [EV_SND] = sounds, [EV_REP] = repeats, | 865 | [EV_SND] = sounds, [EV_REP] = repeats, |
| 768 | }; | 866 | }; |
| 769 | 867 | ||
| 770 | void hid_resolv_event(__u8 type, __u16 code) { | 868 | static void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f) |
| 869 | { | ||
| 870 | seq_printf(f, "%s.%s", events[type] ? events[type] : "?", | ||
| 871 | names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); | ||
| 872 | } | ||
| 771 | 873 | ||
| 772 | if (!hid_debug) | 874 | static void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f) |
| 773 | return; | 875 | { |
| 876 | int i, j, k; | ||
| 877 | struct hid_report *report; | ||
| 878 | struct hid_usage *usage; | ||
| 879 | |||
| 880 | for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { | ||
| 881 | list_for_each_entry(report, &hid->report_enum[k].report_list, list) { | ||
| 882 | for (i = 0; i < report->maxfield; i++) { | ||
| 883 | for ( j = 0; j < report->field[i]->maxusage; j++) { | ||
| 884 | usage = report->field[i]->usage + j; | ||
| 885 | hid_resolv_usage(usage->hid, f); | ||
| 886 | seq_printf(f, " ---> "); | ||
| 887 | hid_resolv_event(usage->type, usage->code, f); | ||
| 888 | seq_printf(f, "\n"); | ||
| 889 | } | ||
| 890 | } | ||
| 891 | } | ||
| 892 | } | ||
| 774 | 893 | ||
| 775 | printk("%s.%s", events[type] ? events[type] : "?", | ||
| 776 | names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); | ||
| 777 | } | 894 | } |
| 778 | EXPORT_SYMBOL_GPL(hid_resolv_event); | 895 | |
| 896 | |||
| 897 | static int hid_debug_rdesc_show(struct seq_file *f, void *p) | ||
| 898 | { | ||
| 899 | struct hid_device *hdev = f->private; | ||
| 900 | int i; | ||
| 901 | |||
| 902 | /* dump HID report descriptor */ | ||
| 903 | for (i = 0; i < hdev->rsize; i++) | ||
| 904 | seq_printf(f, "%02x ", hdev->rdesc[i]); | ||
| 905 | seq_printf(f, "\n\n"); | ||
| 906 | |||
| 907 | /* dump parsed data and input mappings */ | ||
| 908 | hid_dump_device(hdev, f); | ||
| 909 | seq_printf(f, "\n"); | ||
| 910 | hid_dump_input_mapping(hdev, f); | ||
| 911 | |||
| 912 | return 0; | ||
| 913 | } | ||
| 914 | |||
| 915 | static int hid_debug_rdesc_open(struct inode *inode, struct file *file) | ||
| 916 | { | ||
| 917 | return single_open(file, hid_debug_rdesc_show, inode->i_private); | ||
| 918 | } | ||
| 919 | |||
| 920 | static int hid_debug_events_open(struct inode *inode, struct file *file) | ||
| 921 | { | ||
| 922 | int err = 0; | ||
| 923 | struct hid_debug_list *list; | ||
| 924 | |||
| 925 | if (!(list = kzalloc(sizeof(struct hid_debug_list), GFP_KERNEL))) { | ||
| 926 | err = -ENOMEM; | ||
| 927 | goto out; | ||
| 928 | } | ||
| 929 | |||
| 930 | if (!(list->hid_debug_buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_KERNEL))) { | ||
| 931 | err = -ENOMEM; | ||
| 932 | kfree(list); | ||
| 933 | goto out; | ||
| 934 | } | ||
| 935 | list->hdev = (struct hid_device *) inode->i_private; | ||
| 936 | file->private_data = list; | ||
| 937 | mutex_init(&list->read_mutex); | ||
| 938 | |||
| 939 | list_add_tail(&list->node, &list->hdev->debug_list); | ||
| 940 | |||
| 941 | out: | ||
| 942 | return err; | ||
| 943 | } | ||
| 944 | |||
| 945 | static ssize_t hid_debug_events_read(struct file *file, char __user *buffer, | ||
| 946 | size_t count, loff_t *ppos) | ||
| 947 | { | ||
| 948 | struct hid_debug_list *list = file->private_data; | ||
| 949 | int ret = 0, len; | ||
| 950 | DECLARE_WAITQUEUE(wait, current); | ||
| 951 | |||
| 952 | mutex_lock(&list->read_mutex); | ||
| 953 | while (ret == 0) { | ||
| 954 | if (list->head == list->tail) { | ||
| 955 | add_wait_queue(&list->hdev->debug_wait, &wait); | ||
| 956 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 957 | |||
| 958 | while (list->head == list->tail) { | ||
| 959 | if (file->f_flags & O_NONBLOCK) { | ||
| 960 | ret = -EAGAIN; | ||
| 961 | break; | ||
| 962 | } | ||
| 963 | if (signal_pending(current)) { | ||
| 964 | ret = -ERESTARTSYS; | ||
| 965 | break; | ||
| 966 | } | ||
| 967 | |||
| 968 | if (!list->hdev || !list->hdev->debug) { | ||
| 969 | ret = -EIO; | ||
| 970 | break; | ||
| 971 | } | ||
| 972 | |||
| 973 | /* allow O_NONBLOCK from other threads */ | ||
| 974 | mutex_unlock(&list->read_mutex); | ||
| 975 | schedule(); | ||
| 976 | mutex_lock(&list->read_mutex); | ||
| 977 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 978 | } | ||
| 979 | |||
| 980 | set_current_state(TASK_RUNNING); | ||
| 981 | remove_wait_queue(&list->hdev->debug_wait, &wait); | ||
| 982 | } | ||
| 983 | |||
| 984 | if (ret) | ||
| 985 | goto out; | ||
| 986 | |||
| 987 | /* pass the ringbuffer contents to userspace */ | ||
| 988 | copy_rest: | ||
| 989 | if (list->tail == list->head) | ||
| 990 | goto out; | ||
| 991 | if (list->tail > list->head) { | ||
| 992 | len = list->tail - list->head; | ||
| 993 | |||
| 994 | if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) { | ||
| 995 | ret = -EFAULT; | ||
| 996 | goto out; | ||
| 997 | } | ||
| 998 | ret += len; | ||
| 999 | list->head += len; | ||
| 1000 | } else { | ||
| 1001 | len = HID_DEBUG_BUFSIZE - list->head; | ||
| 1002 | |||
| 1003 | if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) { | ||
| 1004 | ret = -EFAULT; | ||
| 1005 | goto out; | ||
| 1006 | } | ||
| 1007 | list->head = 0; | ||
| 1008 | ret += len; | ||
| 1009 | goto copy_rest; | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | } | ||
| 1013 | out: | ||
| 1014 | mutex_unlock(&list->read_mutex); | ||
| 1015 | return ret; | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | static unsigned int hid_debug_events_poll(struct file *file, poll_table *wait) | ||
| 1019 | { | ||
| 1020 | struct hid_debug_list *list = file->private_data; | ||
| 1021 | |||
| 1022 | poll_wait(file, &list->hdev->debug_wait, wait); | ||
| 1023 | if (list->head != list->tail) | ||
| 1024 | return POLLIN | POLLRDNORM; | ||
| 1025 | if (!list->hdev->debug) | ||
| 1026 | return POLLERR | POLLHUP; | ||
| 1027 | return 0; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | static int hid_debug_events_release(struct inode *inode, struct file *file) | ||
| 1031 | { | ||
| 1032 | struct hid_debug_list *list = file->private_data; | ||
| 1033 | |||
| 1034 | list_del(&list->node); | ||
| 1035 | kfree(list->hid_debug_buf); | ||
| 1036 | kfree(list); | ||
| 1037 | |||
| 1038 | return 0; | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | static const struct file_operations hid_debug_rdesc_fops = { | ||
| 1042 | .open = hid_debug_rdesc_open, | ||
| 1043 | .read = seq_read, | ||
| 1044 | .llseek = seq_lseek, | ||
| 1045 | .release = single_release, | ||
| 1046 | }; | ||
| 1047 | |||
| 1048 | static const struct file_operations hid_debug_events_fops = { | ||
| 1049 | .owner = THIS_MODULE, | ||
| 1050 | .open = hid_debug_events_open, | ||
| 1051 | .read = hid_debug_events_read, | ||
| 1052 | .poll = hid_debug_events_poll, | ||
| 1053 | .release = hid_debug_events_release, | ||
| 1054 | }; | ||
| 1055 | |||
| 1056 | |||
| 1057 | void hid_debug_register(struct hid_device *hdev, const char *name) | ||
| 1058 | { | ||
| 1059 | hdev->debug_dir = debugfs_create_dir(name, hid_debug_root); | ||
| 1060 | hdev->debug_rdesc = debugfs_create_file("rdesc", 0400, | ||
| 1061 | hdev->debug_dir, hdev, &hid_debug_rdesc_fops); | ||
| 1062 | hdev->debug_events = debugfs_create_file("events", 0400, | ||
| 1063 | hdev->debug_dir, hdev, &hid_debug_events_fops); | ||
| 1064 | hdev->debug = 1; | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | void hid_debug_unregister(struct hid_device *hdev) | ||
| 1068 | { | ||
| 1069 | hdev->debug = 0; | ||
| 1070 | wake_up_interruptible(&hdev->debug_wait); | ||
| 1071 | debugfs_remove(hdev->debug_rdesc); | ||
| 1072 | debugfs_remove(hdev->debug_events); | ||
| 1073 | debugfs_remove(hdev->debug_dir); | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | void hid_debug_init(void) | ||
| 1077 | { | ||
| 1078 | hid_debug_root = debugfs_create_dir("hid", NULL); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | void hid_debug_exit(void) | ||
| 1082 | { | ||
| 1083 | debugfs_remove_recursive(hid_debug_root); | ||
| 1084 | } | ||
| 1085 | |||
