diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-14 20:55:53 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-14 20:55:53 -0400 |
commit | 5489375d481c8456c8259b48e107d03b05309d1d (patch) | |
tree | 2bb3c9fe3b68e135444d1e5a47fdf3a1b7adf284 /drivers/hid/hid-debug.c | |
parent | 355bbd8cb82e60a592f6cd86ce6dbe5677615cf4 (diff) | |
parent | 8123e8f7c89a07cb22279b15bf47cdee0205d4a1 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid:
HID: completely remove apple mightymouse from blacklist
HID: support larger reports than 64 bytes in hiddev
HID: local function should be static
HID: ignore Philips IEEE802.15.4 RF Dongle
HID: ignore all recent SoundGraph iMON devices
HID: fix memory leak on error patch in debug code
HID: fix overrun in quirks initialization
HID: Drop NULL test on list_entry result
HID: driver for Twinhan USB 6253:0100 remote control
HID: adding __init/__exit macros to module init/exit functions
HID: add rumble support for Thrustmaster Dual Trigger 3-in-1
HID: ntrig tool separation and pen usages
HID: Avoid double spin_lock_init on usbhid->lock
HID: add force feedback support for Logitech WingMan Formula Force GP
HID: Support new variants of Samsung USB IR receiver (0419:0001)
HID: fix memory leak on error path in debug code
HID: fix debugfs build with !CONFIG_DEBUG_FS
HID: use debugfs for events/reports dumping
HID: use debugfs for report dumping descriptor
Diffstat (limited to 'drivers/hid/hid-debug.c')
-rw-r--r-- | drivers/hid/hid-debug.c | 439 |
1 files changed, 363 insertions, 76 deletions
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 04359ed64b87..6abd0369aedb 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/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,17 @@ | |||
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/uaccess.h> | ||
33 | #include <linux/poll.h> | ||
34 | |||
29 | #include <linux/hid.h> | 35 | #include <linux/hid.h> |
30 | #include <linux/hid-debug.h> | 36 | #include <linux/hid-debug.h> |
31 | 37 | ||
38 | static struct dentry *hid_debug_root; | ||
39 | |||
32 | struct hid_usage_entry { | 40 | struct hid_usage_entry { |
33 | unsigned page; | 41 | unsigned page; |
34 | unsigned usage; | 42 | unsigned usage; |
@@ -339,72 +347,120 @@ static const struct hid_usage_entry hid_usage_table[] = { | |||
339 | { 0, 0, NULL } | 347 | { 0, 0, NULL } |
340 | }; | 348 | }; |
341 | 349 | ||
342 | static void resolv_usage_page(unsigned page) { | 350 | /* Either output directly into simple seq_file, or (if f == NULL) |
351 | * allocate a separate buffer that will then be passed to the 'events' | ||
352 | * ringbuffer. | ||
353 | * | ||
354 | * This is because these functions can be called both for "one-shot" | ||
355 | * "rdesc" while resolving, or for blocking "events". | ||
356 | * | ||
357 | * This holds both for resolv_usage_page() and hid_resolv_usage(). | ||
358 | */ | ||
359 | static char *resolv_usage_page(unsigned page, struct seq_file *f) { | ||
343 | const struct hid_usage_entry *p; | 360 | const struct hid_usage_entry *p; |
361 | char *buf = NULL; | ||
362 | |||
363 | if (!f) { | ||
364 | buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); | ||
365 | if (!buf) | ||
366 | return ERR_PTR(-ENOMEM); | ||
367 | } | ||
344 | 368 | ||
345 | for (p = hid_usage_table; p->description; p++) | 369 | for (p = hid_usage_table; p->description; p++) |
346 | if (p->page == page) { | 370 | if (p->page == page) { |
347 | printk("%s", p->description); | 371 | if (!f) { |
348 | return; | 372 | snprintf(buf, HID_DEBUG_BUFSIZE, "%s", |
373 | p->description); | ||
374 | return buf; | ||
375 | } | ||
376 | else { | ||
377 | seq_printf(f, "%s", p->description); | ||
378 | return NULL; | ||
379 | } | ||
349 | } | 380 | } |
350 | printk("%04x", page); | 381 | if (!f) |
382 | snprintf(buf, HID_DEBUG_BUFSIZE, "%04x", page); | ||
383 | else | ||
384 | seq_printf(f, "%04x", page); | ||
385 | return buf; | ||
351 | } | 386 | } |
352 | 387 | ||
353 | void hid_resolv_usage(unsigned usage) { | 388 | char *hid_resolv_usage(unsigned usage, struct seq_file *f) { |
354 | const struct hid_usage_entry *p; | 389 | const struct hid_usage_entry *p; |
390 | char *buf = NULL; | ||
391 | int len = 0; | ||
392 | |||
393 | buf = resolv_usage_page(usage >> 16, f); | ||
394 | if (IS_ERR(buf)) { | ||
395 | printk(KERN_ERR "error allocating HID debug buffer\n"); | ||
396 | return NULL; | ||
397 | } | ||
355 | 398 | ||
356 | if (!hid_debug) | ||
357 | return; | ||
358 | 399 | ||
359 | resolv_usage_page(usage >> 16); | 400 | if (!f) { |
360 | printk("."); | 401 | len = strlen(buf); |
402 | snprintf(buf+len, max(0, HID_DEBUG_BUFSIZE - len), "."); | ||
403 | len++; | ||
404 | } | ||
405 | else { | ||
406 | seq_printf(f, "."); | ||
407 | } | ||
361 | for (p = hid_usage_table; p->description; p++) | 408 | for (p = hid_usage_table; p->description; p++) |
362 | if (p->page == (usage >> 16)) { | 409 | if (p->page == (usage >> 16)) { |
363 | for(++p; p->description && p->usage != 0; p++) | 410 | for(++p; p->description && p->usage != 0; p++) |
364 | if (p->usage == (usage & 0xffff)) { | 411 | if (p->usage == (usage & 0xffff)) { |
365 | printk("%s", p->description); | 412 | if (!f) |
366 | return; | 413 | snprintf(buf + len, |
414 | max(0,HID_DEBUG_BUFSIZE - len - 1), | ||
415 | "%s", p->description); | ||
416 | else | ||
417 | seq_printf(f, | ||
418 | "%s", | ||
419 | p->description); | ||
420 | return buf; | ||
367 | } | 421 | } |
368 | break; | 422 | break; |
369 | } | 423 | } |
370 | printk("%04x", usage & 0xffff); | 424 | if (!f) |
425 | snprintf(buf + len, max(0, HID_DEBUG_BUFSIZE - len - 1), | ||
426 | "%04x", usage & 0xffff); | ||
427 | else | ||
428 | seq_printf(f, "%04x", usage & 0xffff); | ||
429 | return buf; | ||
371 | } | 430 | } |
372 | EXPORT_SYMBOL_GPL(hid_resolv_usage); | 431 | EXPORT_SYMBOL_GPL(hid_resolv_usage); |
373 | 432 | ||
374 | static void tab(int n) { | 433 | static void tab(int n, struct seq_file *f) { |
375 | printk(KERN_DEBUG "%*s", n, ""); | 434 | seq_printf(f, "%*s", n, ""); |
376 | } | 435 | } |
377 | 436 | ||
378 | void hid_dump_field(struct hid_field *field, int n) { | 437 | void hid_dump_field(struct hid_field *field, int n, struct seq_file *f) { |
379 | int j; | 438 | int j; |
380 | 439 | ||
381 | if (!hid_debug) | ||
382 | return; | ||
383 | |||
384 | if (field->physical) { | 440 | if (field->physical) { |
385 | tab(n); | 441 | tab(n, f); |
386 | printk("Physical("); | 442 | seq_printf(f, "Physical("); |
387 | hid_resolv_usage(field->physical); printk(")\n"); | 443 | hid_resolv_usage(field->physical, f); seq_printf(f, ")\n"); |
388 | } | 444 | } |
389 | if (field->logical) { | 445 | if (field->logical) { |
390 | tab(n); | 446 | tab(n, f); |
391 | printk("Logical("); | 447 | seq_printf(f, "Logical("); |
392 | hid_resolv_usage(field->logical); printk(")\n"); | 448 | hid_resolv_usage(field->logical, f); seq_printf(f, ")\n"); |
393 | } | 449 | } |
394 | tab(n); printk("Usage(%d)\n", field->maxusage); | 450 | tab(n, f); seq_printf(f, "Usage(%d)\n", field->maxusage); |
395 | for (j = 0; j < field->maxusage; j++) { | 451 | for (j = 0; j < field->maxusage; j++) { |
396 | tab(n+2); hid_resolv_usage(field->usage[j].hid); printk("\n"); | 452 | tab(n+2, f); hid_resolv_usage(field->usage[j].hid, f); seq_printf(f, "\n"); |
397 | } | 453 | } |
398 | if (field->logical_minimum != field->logical_maximum) { | 454 | if (field->logical_minimum != field->logical_maximum) { |
399 | tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum); | 455 | tab(n, f); seq_printf(f, "Logical Minimum(%d)\n", field->logical_minimum); |
400 | tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum); | 456 | tab(n, f); seq_printf(f, "Logical Maximum(%d)\n", field->logical_maximum); |
401 | } | 457 | } |
402 | if (field->physical_minimum != field->physical_maximum) { | 458 | if (field->physical_minimum != field->physical_maximum) { |
403 | tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum); | 459 | tab(n, f); seq_printf(f, "Physical Minimum(%d)\n", field->physical_minimum); |
404 | tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum); | 460 | tab(n, f); seq_printf(f, "Physical Maximum(%d)\n", field->physical_maximum); |
405 | } | 461 | } |
406 | if (field->unit_exponent) { | 462 | if (field->unit_exponent) { |
407 | tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent); | 463 | tab(n, f); seq_printf(f, "Unit Exponent(%d)\n", field->unit_exponent); |
408 | } | 464 | } |
409 | if (field->unit) { | 465 | if (field->unit) { |
410 | static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; | 466 | static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; |
@@ -425,77 +481,75 @@ void hid_dump_field(struct hid_field *field, int n) { | |||
425 | data >>= 4; | 481 | data >>= 4; |
426 | 482 | ||
427 | if(sys > 4) { | 483 | if(sys > 4) { |
428 | tab(n); printk("Unit(Invalid)\n"); | 484 | tab(n, f); seq_printf(f, "Unit(Invalid)\n"); |
429 | } | 485 | } |
430 | else { | 486 | else { |
431 | int earlier_unit = 0; | 487 | int earlier_unit = 0; |
432 | 488 | ||
433 | tab(n); printk("Unit(%s : ", systems[sys]); | 489 | tab(n, f); seq_printf(f, "Unit(%s : ", systems[sys]); |
434 | 490 | ||
435 | for (i=1 ; i<sizeof(__u32)*2 ; i++) { | 491 | for (i=1 ; i<sizeof(__u32)*2 ; i++) { |
436 | char nibble = data & 0xf; | 492 | char nibble = data & 0xf; |
437 | data >>= 4; | 493 | data >>= 4; |
438 | if (nibble != 0) { | 494 | if (nibble != 0) { |
439 | if(earlier_unit++ > 0) | 495 | if(earlier_unit++ > 0) |
440 | printk("*"); | 496 | seq_printf(f, "*"); |
441 | printk("%s", units[sys][i]); | 497 | seq_printf(f, "%s", units[sys][i]); |
442 | if(nibble != 1) { | 498 | if(nibble != 1) { |
443 | /* This is a _signed_ nibble(!) */ | 499 | /* This is a _signed_ nibble(!) */ |
444 | 500 | ||
445 | int val = nibble & 0x7; | 501 | int val = nibble & 0x7; |
446 | if(nibble & 0x08) | 502 | if(nibble & 0x08) |
447 | val = -((0x7 & ~val) +1); | 503 | val = -((0x7 & ~val) +1); |
448 | printk("^%d", val); | 504 | seq_printf(f, "^%d", val); |
449 | } | 505 | } |
450 | } | 506 | } |
451 | } | 507 | } |
452 | printk(")\n"); | 508 | seq_printf(f, ")\n"); |
453 | } | 509 | } |
454 | } | 510 | } |
455 | tab(n); printk("Report Size(%u)\n", field->report_size); | 511 | tab(n, f); seq_printf(f, "Report Size(%u)\n", field->report_size); |
456 | tab(n); printk("Report Count(%u)\n", field->report_count); | 512 | tab(n, f); seq_printf(f, "Report Count(%u)\n", field->report_count); |
457 | tab(n); printk("Report Offset(%u)\n", field->report_offset); | 513 | tab(n, f); seq_printf(f, "Report Offset(%u)\n", field->report_offset); |
458 | 514 | ||
459 | tab(n); printk("Flags( "); | 515 | tab(n, f); seq_printf(f, "Flags( "); |
460 | j = field->flags; | 516 | j = field->flags; |
461 | printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); | 517 | seq_printf(f, "%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); |
462 | printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); | 518 | seq_printf(f, "%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); |
463 | printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); | 519 | seq_printf(f, "%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); |
464 | printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); | 520 | seq_printf(f, "%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); |
465 | printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); | 521 | seq_printf(f, "%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); |
466 | printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : ""); | 522 | seq_printf(f, "%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : ""); |
467 | printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); | 523 | seq_printf(f, "%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); |
468 | printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); | 524 | seq_printf(f, "%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); |
469 | printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); | 525 | seq_printf(f, "%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); |
470 | printk(")\n"); | 526 | seq_printf(f, ")\n"); |
471 | } | 527 | } |
472 | EXPORT_SYMBOL_GPL(hid_dump_field); | 528 | EXPORT_SYMBOL_GPL(hid_dump_field); |
473 | 529 | ||
474 | void hid_dump_device(struct hid_device *device) { | 530 | void hid_dump_device(struct hid_device *device, struct seq_file *f) |
531 | { | ||
475 | struct hid_report_enum *report_enum; | 532 | struct hid_report_enum *report_enum; |
476 | struct hid_report *report; | 533 | struct hid_report *report; |
477 | struct list_head *list; | 534 | struct list_head *list; |
478 | unsigned i,k; | 535 | unsigned i,k; |
479 | static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; | 536 | static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; |
480 | 537 | ||
481 | if (!hid_debug) | ||
482 | return; | ||
483 | |||
484 | for (i = 0; i < HID_REPORT_TYPES; i++) { | 538 | for (i = 0; i < HID_REPORT_TYPES; i++) { |
485 | report_enum = device->report_enum + i; | 539 | report_enum = device->report_enum + i; |
486 | list = report_enum->report_list.next; | 540 | list = report_enum->report_list.next; |
487 | while (list != &report_enum->report_list) { | 541 | while (list != &report_enum->report_list) { |
488 | report = (struct hid_report *) list; | 542 | report = (struct hid_report *) list; |
489 | tab(2); | 543 | tab(2, f); |
490 | printk("%s", table[i]); | 544 | seq_printf(f, "%s", table[i]); |
491 | if (report->id) | 545 | if (report->id) |
492 | printk("(%d)", report->id); | 546 | seq_printf(f, "(%d)", report->id); |
493 | printk("[%s]", table[report->type]); | 547 | seq_printf(f, "[%s]", table[report->type]); |
494 | printk("\n"); | 548 | seq_printf(f, "\n"); |
495 | for (k = 0; k < report->maxfield; k++) { | 549 | for (k = 0; k < report->maxfield; k++) { |
496 | tab(4); | 550 | tab(4, f); |
497 | printk("Field(%d)\n", k); | 551 | seq_printf(f, "Field(%d)\n", k); |
498 | hid_dump_field(report->field[k], 6); | 552 | hid_dump_field(report->field[k], 6, f); |
499 | } | 553 | } |
500 | list = list->next; | 554 | list = list->next; |
501 | } | 555 | } |
@@ -503,13 +557,37 @@ void hid_dump_device(struct hid_device *device) { | |||
503 | } | 557 | } |
504 | EXPORT_SYMBOL_GPL(hid_dump_device); | 558 | EXPORT_SYMBOL_GPL(hid_dump_device); |
505 | 559 | ||
506 | void hid_dump_input(struct hid_usage *usage, __s32 value) { | 560 | /* enqueue string to 'events' ring buffer */ |
507 | if (hid_debug < 2) | 561 | void hid_debug_event(struct hid_device *hdev, char *buf) |
562 | { | ||
563 | int i; | ||
564 | struct hid_debug_list *list; | ||
565 | |||
566 | list_for_each_entry(list, &hdev->debug_list, node) { | ||
567 | for (i = 0; i <= strlen(buf); i++) | ||
568 | list->hid_debug_buf[(list->tail + i) % (HID_DEBUG_BUFSIZE - 1)] = | ||
569 | buf[i]; | ||
570 | list->tail = (list->tail + i) % (HID_DEBUG_BUFSIZE - 1); | ||
571 | } | ||
572 | } | ||
573 | EXPORT_SYMBOL_GPL(hid_debug_event); | ||
574 | |||
575 | void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 value) | ||
576 | { | ||
577 | char *buf; | ||
578 | int len; | ||
579 | |||
580 | buf = hid_resolv_usage(usage->hid, NULL); | ||
581 | if (!buf) | ||
508 | return; | 582 | return; |
583 | len = strlen(buf); | ||
584 | snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", value); | ||
585 | |||
586 | hid_debug_event(hdev, buf); | ||
587 | |||
588 | kfree(buf); | ||
589 | wake_up_interruptible(&hdev->debug_wait); | ||
509 | 590 | ||
510 | printk(KERN_DEBUG "hid-debug: input "); | ||
511 | hid_resolv_usage(usage->hid); | ||
512 | printk(" = %d\n", value); | ||
513 | } | 591 | } |
514 | EXPORT_SYMBOL_GPL(hid_dump_input); | 592 | EXPORT_SYMBOL_GPL(hid_dump_input); |
515 | 593 | ||
@@ -786,12 +864,221 @@ static const char **names[EV_MAX + 1] = { | |||
786 | [EV_SND] = sounds, [EV_REP] = repeats, | 864 | [EV_SND] = sounds, [EV_REP] = repeats, |
787 | }; | 865 | }; |
788 | 866 | ||
789 | void hid_resolv_event(__u8 type, __u16 code) { | 867 | void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f) { |
790 | 868 | ||
791 | if (!hid_debug) | 869 | seq_printf(f, "%s.%s", events[type] ? events[type] : "?", |
792 | return; | ||
793 | |||
794 | printk("%s.%s", events[type] ? events[type] : "?", | ||
795 | names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); | 870 | names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); |
796 | } | 871 | } |
797 | EXPORT_SYMBOL_GPL(hid_resolv_event); | 872 | |
873 | void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f) | ||
874 | { | ||
875 | int i, j, k; | ||
876 | struct hid_report *report; | ||
877 | struct hid_usage *usage; | ||
878 | |||
879 | for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { | ||
880 | list_for_each_entry(report, &hid->report_enum[k].report_list, list) { | ||
881 | for (i = 0; i < report->maxfield; i++) { | ||
882 | for ( j = 0; j < report->field[i]->maxusage; j++) { | ||
883 | usage = report->field[i]->usage + j; | ||
884 | hid_resolv_usage(usage->hid, f); | ||
885 | seq_printf(f, " ---> "); | ||
886 | hid_resolv_event(usage->type, usage->code, f); | ||
887 | seq_printf(f, "\n"); | ||
888 | } | ||
889 | } | ||
890 | } | ||
891 | } | ||
892 | |||
893 | } | ||
894 | |||
895 | |||
896 | static int hid_debug_rdesc_show(struct seq_file *f, void *p) | ||
897 | { | ||
898 | struct hid_device *hdev = f->private; | ||
899 | int i; | ||
900 | |||
901 | /* dump HID report descriptor */ | ||
902 | for (i = 0; i < hdev->rsize; i++) | ||
903 | seq_printf(f, "%02x ", hdev->rdesc[i]); | ||
904 | seq_printf(f, "\n\n"); | ||
905 | |||
906 | /* dump parsed data and input mappings */ | ||
907 | hid_dump_device(hdev, f); | ||
908 | seq_printf(f, "\n"); | ||
909 | hid_dump_input_mapping(hdev, f); | ||
910 | |||
911 | return 0; | ||
912 | } | ||
913 | |||
914 | static int hid_debug_rdesc_open(struct inode *inode, struct file *file) | ||
915 | { | ||
916 | return single_open(file, hid_debug_rdesc_show, inode->i_private); | ||
917 | } | ||
918 | |||
919 | static int hid_debug_events_open(struct inode *inode, struct file *file) | ||
920 | { | ||
921 | int err = 0; | ||
922 | struct hid_debug_list *list; | ||
923 | |||
924 | if (!(list = kzalloc(sizeof(struct hid_debug_list), GFP_KERNEL))) { | ||
925 | err = -ENOMEM; | ||
926 | goto out; | ||
927 | } | ||
928 | |||
929 | if (!(list->hid_debug_buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_KERNEL))) { | ||
930 | err = -ENOMEM; | ||
931 | kfree(list); | ||
932 | goto out; | ||
933 | } | ||
934 | list->hdev = (struct hid_device *) inode->i_private; | ||
935 | file->private_data = list; | ||
936 | mutex_init(&list->read_mutex); | ||
937 | |||
938 | list_add_tail(&list->node, &list->hdev->debug_list); | ||
939 | |||
940 | out: | ||
941 | return err; | ||
942 | } | ||
943 | |||
944 | static ssize_t hid_debug_events_read(struct file *file, char __user *buffer, | ||
945 | size_t count, loff_t *ppos) | ||
946 | { | ||
947 | struct hid_debug_list *list = file->private_data; | ||
948 | int ret = 0, len; | ||
949 | DECLARE_WAITQUEUE(wait, current); | ||
950 | |||
951 | while (ret == 0) { | ||
952 | mutex_lock(&list->read_mutex); | ||
953 | if (list->head == list->tail) { | ||
954 | add_wait_queue(&list->hdev->debug_wait, &wait); | ||
955 | set_current_state(TASK_INTERRUPTIBLE); | ||
956 | |||
957 | while (list->head == list->tail) { | ||
958 | if (file->f_flags & O_NONBLOCK) { | ||
959 | ret = -EAGAIN; | ||
960 | break; | ||
961 | } | ||
962 | if (signal_pending(current)) { | ||
963 | ret = -ERESTARTSYS; | ||
964 | break; | ||
965 | } | ||
966 | |||
967 | if (!list->hdev || !list->hdev->debug) { | ||
968 | ret = -EIO; | ||
969 | break; | ||
970 | } | ||
971 | |||
972 | /* allow O_NONBLOCK from other threads */ | ||
973 | mutex_unlock(&list->read_mutex); | ||
974 | schedule(); | ||
975 | mutex_lock(&list->read_mutex); | ||
976 | set_current_state(TASK_INTERRUPTIBLE); | ||
977 | } | ||
978 | |||
979 | set_current_state(TASK_RUNNING); | ||
980 | remove_wait_queue(&list->hdev->debug_wait, &wait); | ||
981 | } | ||
982 | |||
983 | if (ret) | ||
984 | goto out; | ||
985 | |||
986 | /* pass the ringbuffer contents to userspace */ | ||
987 | copy_rest: | ||
988 | if (list->tail == list->head) | ||
989 | goto out; | ||
990 | if (list->tail > list->head) { | ||
991 | len = list->tail - list->head; | ||
992 | |||
993 | if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) { | ||
994 | ret = -EFAULT; | ||
995 | goto out; | ||
996 | } | ||
997 | ret += len; | ||
998 | list->head += len; | ||
999 | } else { | ||
1000 | len = HID_DEBUG_BUFSIZE - list->head; | ||
1001 | |||
1002 | if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) { | ||
1003 | ret = -EFAULT; | ||
1004 | goto out; | ||
1005 | } | ||
1006 | list->head = 0; | ||
1007 | ret += len; | ||
1008 | goto copy_rest; | ||
1009 | } | ||
1010 | |||
1011 | } | ||
1012 | out: | ||
1013 | mutex_unlock(&list->read_mutex); | ||
1014 | return ret; | ||
1015 | } | ||
1016 | |||
1017 | static unsigned int hid_debug_events_poll(struct file *file, poll_table *wait) | ||
1018 | { | ||
1019 | struct hid_debug_list *list = file->private_data; | ||
1020 | |||
1021 | poll_wait(file, &list->hdev->debug_wait, wait); | ||
1022 | if (list->head != list->tail) | ||
1023 | return POLLIN | POLLRDNORM; | ||
1024 | if (!list->hdev->debug) | ||
1025 | return POLLERR | POLLHUP; | ||
1026 | return 0; | ||
1027 | } | ||
1028 | |||
1029 | static int hid_debug_events_release(struct inode *inode, struct file *file) | ||
1030 | { | ||
1031 | struct hid_debug_list *list = file->private_data; | ||
1032 | |||
1033 | list_del(&list->node); | ||
1034 | kfree(list->hid_debug_buf); | ||
1035 | kfree(list); | ||
1036 | |||
1037 | return 0; | ||
1038 | } | ||
1039 | |||
1040 | static const struct file_operations hid_debug_rdesc_fops = { | ||
1041 | .open = hid_debug_rdesc_open, | ||
1042 | .read = seq_read, | ||
1043 | .llseek = seq_lseek, | ||
1044 | .release = single_release, | ||
1045 | }; | ||
1046 | |||
1047 | static const struct file_operations hid_debug_events_fops = { | ||
1048 | .owner = THIS_MODULE, | ||
1049 | .open = hid_debug_events_open, | ||
1050 | .read = hid_debug_events_read, | ||
1051 | .poll = hid_debug_events_poll, | ||
1052 | .release = hid_debug_events_release, | ||
1053 | }; | ||
1054 | |||
1055 | |||
1056 | void hid_debug_register(struct hid_device *hdev, const char *name) | ||
1057 | { | ||
1058 | hdev->debug_dir = debugfs_create_dir(name, hid_debug_root); | ||
1059 | hdev->debug_rdesc = debugfs_create_file("rdesc", 0400, | ||
1060 | hdev->debug_dir, hdev, &hid_debug_rdesc_fops); | ||
1061 | hdev->debug_events = debugfs_create_file("events", 0400, | ||
1062 | hdev->debug_dir, hdev, &hid_debug_events_fops); | ||
1063 | hdev->debug = 1; | ||
1064 | } | ||
1065 | |||
1066 | void hid_debug_unregister(struct hid_device *hdev) | ||
1067 | { | ||
1068 | hdev->debug = 0; | ||
1069 | wake_up_interruptible(&hdev->debug_wait); | ||
1070 | debugfs_remove(hdev->debug_rdesc); | ||
1071 | debugfs_remove(hdev->debug_events); | ||
1072 | debugfs_remove(hdev->debug_dir); | ||
1073 | } | ||
1074 | |||
1075 | void hid_debug_init(void) | ||
1076 | { | ||
1077 | hid_debug_root = debugfs_create_dir("hid", NULL); | ||
1078 | } | ||
1079 | |||
1080 | void hid_debug_exit(void) | ||
1081 | { | ||
1082 | debugfs_remove_recursive(hid_debug_root); | ||
1083 | } | ||
1084 | |||