diff options
author | Jiri Kosina <jkosina@suse.cz> | 2009-06-12 09:20:55 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2009-06-12 09:20:55 -0400 |
commit | a635f9dd83f3382577f4544a96df12356e951a40 (patch) | |
tree | c6fa27df6d01c34e304a32c9f423d569f7358346 | |
parent | 8ebf975608aaebd7feb33d77f07ba21a6380e086 (diff) |
HID: use debugfs for report dumping descriptor
It is a little bit inconvenient for people who have some non-standard
HID hardware (usually violating the HID specification) to have to
recompile kernel with CONFIG_HID_DEBUG to be able to see kernel's perspective
of the HID report descriptor and observe the parsed events. Plus the messages
are then mixed up inconveniently with the rest of the dmesg stuff.
This patch implements /sys/kernel/debug/hid/<device>/rdesc file, which
represents the kernel's view of report descriptor (both the raw report
descriptor data and parsed contents).
With all the device-specific debug data being available through debugfs, there
is no need for keeping CONFIG_HID_DEBUG, as the 'debug' parameter to the
hid module will now only output only driver-specific debugging options, which has
absolutely minimal memory footprint, just a few error messages and one global
flag (hid_debug).
We use the current set of output formatting functions. The ones that need to be
used both for one-shot rdesc seq_file and also for continuous flow of data
(individual reports, as being sent by the device) distinguish according to the
passed seq_file parameter, and if it is NULL, it still output to kernel ringbuffer,
otherwise the corresponding seq_file is used for output.
The format of the output is preserved.
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/Kconfig | 15 | ||||
-rw-r--r-- | drivers/hid/Makefile | 5 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 13 | ||||
-rw-r--r-- | drivers/hid/hid-debug.c | 227 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 13 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 8 | ||||
-rw-r--r-- | include/linux/hid-debug.h | 32 | ||||
-rw-r--r-- | include/linux/hid.h | 4 |
8 files changed, 195 insertions, 122 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 7e67dcb3d4f6..05950a783560 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -31,21 +31,6 @@ config HID | |||
31 | 31 | ||
32 | If unsure, say Y. | 32 | If unsure, say Y. |
33 | 33 | ||
34 | config HID_DEBUG | ||
35 | bool "HID debugging support" | ||
36 | default y | ||
37 | depends on HID | ||
38 | ---help--- | ||
39 | This option lets the HID layer output diagnostics about its internal | ||
40 | state, resolve HID usages, dump HID fields, etc. Individual HID drivers | ||
41 | use this debugging facility to output information about individual HID | ||
42 | devices, etc. | ||
43 | |||
44 | This feature is useful for those who are either debugging the HID parser | ||
45 | or any HID hardware device. | ||
46 | |||
47 | If unsure, say Y. | ||
48 | |||
49 | config HIDRAW | 34 | config HIDRAW |
50 | bool "/dev/hidraw raw HID device support" | 35 | bool "/dev/hidraw raw HID device support" |
51 | depends on HID | 36 | depends on HID |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 1f7cb0fd4505..cf3687d1ed63 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -3,9 +3,12 @@ | |||
3 | # | 3 | # |
4 | hid-objs := hid-core.o hid-input.o | 4 | hid-objs := hid-core.o hid-input.o |
5 | 5 | ||
6 | ifdef CONFIG_DEBUG_FS | ||
7 | hid-objs += hid-debug.o | ||
8 | endif | ||
9 | |||
6 | obj-$(CONFIG_HID) += hid.o | 10 | obj-$(CONFIG_HID) += hid.o |
7 | 11 | ||
8 | hid-$(CONFIG_HID_DEBUG) += hid-debug.o | ||
9 | hid-$(CONFIG_HIDRAW) += hidraw.o | 12 | hid-$(CONFIG_HIDRAW) += hidraw.o |
10 | 13 | ||
11 | hid-logitech-objs := hid-lg.o | 14 | hid-logitech-objs := hid-lg.o |
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8551693d645f..d4317db85b54 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -44,12 +44,10 @@ | |||
44 | #define DRIVER_DESC "HID core driver" | 44 | #define DRIVER_DESC "HID core driver" |
45 | #define DRIVER_LICENSE "GPL" | 45 | #define DRIVER_LICENSE "GPL" |
46 | 46 | ||
47 | #ifdef CONFIG_HID_DEBUG | ||
48 | int hid_debug = 0; | 47 | int hid_debug = 0; |
49 | module_param_named(debug, hid_debug, int, 0600); | 48 | module_param_named(debug, hid_debug, int, 0600); |
50 | MODULE_PARM_DESC(debug, "HID debugging (0=off, 1=probing info, 2=continuous data dumping)"); | 49 | MODULE_PARM_DESC(debug, "HID debugging (0=off, 1=probing info, 2=continuous data dumping)"); |
51 | EXPORT_SYMBOL_GPL(hid_debug); | 50 | EXPORT_SYMBOL_GPL(hid_debug); |
52 | #endif | ||
53 | 51 | ||
54 | /* | 52 | /* |
55 | * Register a new report for a device. | 53 | * Register a new report for a device. |
@@ -987,7 +985,6 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) | |||
987 | 985 | ||
988 | if (offset >= field->report_count) { | 986 | if (offset >= field->report_count) { |
989 | dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count); | 987 | dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count); |
990 | hid_dump_field(field, 8); | ||
991 | return -1; | 988 | return -1; |
992 | } | 989 | } |
993 | if (field->logical_minimum < 0) { | 990 | if (field->logical_minimum < 0) { |
@@ -1721,6 +1718,8 @@ int hid_add_device(struct hid_device *hdev) | |||
1721 | if (!ret) | 1718 | if (!ret) |
1722 | hdev->status |= HID_STAT_ADDED; | 1719 | hdev->status |= HID_STAT_ADDED; |
1723 | 1720 | ||
1721 | hid_debug_register(hdev, dev_name(&hdev->dev)); | ||
1722 | |||
1724 | return ret; | 1723 | return ret; |
1725 | } | 1724 | } |
1726 | EXPORT_SYMBOL_GPL(hid_add_device); | 1725 | EXPORT_SYMBOL_GPL(hid_add_device); |
@@ -1768,6 +1767,7 @@ static void hid_remove_device(struct hid_device *hdev) | |||
1768 | { | 1767 | { |
1769 | if (hdev->status & HID_STAT_ADDED) { | 1768 | if (hdev->status & HID_STAT_ADDED) { |
1770 | device_del(&hdev->dev); | 1769 | device_del(&hdev->dev); |
1770 | hid_debug_unregister(hdev); | ||
1771 | hdev->status &= ~HID_STAT_ADDED; | 1771 | hdev->status &= ~HID_STAT_ADDED; |
1772 | } | 1772 | } |
1773 | } | 1773 | } |
@@ -1843,6 +1843,10 @@ static int __init hid_init(void) | |||
1843 | { | 1843 | { |
1844 | int ret; | 1844 | int ret; |
1845 | 1845 | ||
1846 | if (hid_debug) | ||
1847 | printk(KERN_WARNING "HID: hid_debug parameter has been deprecated. " | ||
1848 | "Debugging data are now provided via debugfs\n"); | ||
1849 | |||
1846 | ret = bus_register(&hid_bus_type); | 1850 | ret = bus_register(&hid_bus_type); |
1847 | if (ret) { | 1851 | if (ret) { |
1848 | printk(KERN_ERR "HID: can't register hid bus\n"); | 1852 | printk(KERN_ERR "HID: can't register hid bus\n"); |
@@ -1853,6 +1857,8 @@ static int __init hid_init(void) | |||
1853 | if (ret) | 1857 | if (ret) |
1854 | goto err_bus; | 1858 | goto err_bus; |
1855 | 1859 | ||
1860 | hid_debug_init(); | ||
1861 | |||
1856 | return 0; | 1862 | return 0; |
1857 | err_bus: | 1863 | err_bus: |
1858 | bus_unregister(&hid_bus_type); | 1864 | bus_unregister(&hid_bus_type); |
@@ -1862,6 +1868,7 @@ err: | |||
1862 | 1868 | ||
1863 | static void __exit hid_exit(void) | 1869 | static void __exit hid_exit(void) |
1864 | { | 1870 | { |
1871 | hid_debug_exit(); | ||
1865 | hidraw_exit(); | 1872 | hidraw_exit(); |
1866 | bus_unregister(&hid_bus_type); | 1873 | bus_unregister(&hid_bus_type); |
1867 | } | 1874 | } |
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 47ac1a7d66e1..067e173aa3e4 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,13 @@ | |||
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> | ||
29 | #include <linux/hid.h> | 31 | #include <linux/hid.h> |
30 | #include <linux/hid-debug.h> | 32 | #include <linux/hid-debug.h> |
31 | 33 | ||
34 | static struct dentry *hid_debug_root; | ||
35 | |||
32 | struct hid_usage_entry { | 36 | struct hid_usage_entry { |
33 | unsigned page; | 37 | unsigned page; |
34 | unsigned usage; | 38 | unsigned usage; |
@@ -331,72 +335,83 @@ static const struct hid_usage_entry hid_usage_table[] = { | |||
331 | { 0, 0, NULL } | 335 | { 0, 0, NULL } |
332 | }; | 336 | }; |
333 | 337 | ||
334 | static void resolv_usage_page(unsigned page) { | 338 | static void resolv_usage_page(unsigned page, struct seq_file *f) { |
335 | const struct hid_usage_entry *p; | 339 | const struct hid_usage_entry *p; |
336 | 340 | ||
337 | for (p = hid_usage_table; p->description; p++) | 341 | for (p = hid_usage_table; p->description; p++) |
338 | if (p->page == page) { | 342 | if (p->page == page) { |
339 | printk("%s", p->description); | 343 | if (!f) |
344 | printk("%s", p->description); | ||
345 | else | ||
346 | seq_printf(f, "%s", p->description); | ||
340 | return; | 347 | return; |
341 | } | 348 | } |
342 | printk("%04x", page); | 349 | if (!f) |
350 | printk("%04x", page); | ||
351 | else | ||
352 | seq_printf(f, "%04x", page); | ||
343 | } | 353 | } |
344 | 354 | ||
345 | void hid_resolv_usage(unsigned usage) { | 355 | void hid_resolv_usage(unsigned usage, struct seq_file *f) { |
346 | const struct hid_usage_entry *p; | 356 | const struct hid_usage_entry *p; |
347 | 357 | ||
348 | if (!hid_debug) | 358 | resolv_usage_page(usage >> 16, f); |
349 | return; | 359 | if (!f) |
350 | 360 | printk("."); | |
351 | resolv_usage_page(usage >> 16); | 361 | else |
352 | printk("."); | 362 | seq_printf(f, "."); |
353 | for (p = hid_usage_table; p->description; p++) | 363 | for (p = hid_usage_table; p->description; p++) |
354 | if (p->page == (usage >> 16)) { | 364 | if (p->page == (usage >> 16)) { |
355 | for(++p; p->description && p->usage != 0; p++) | 365 | for(++p; p->description && p->usage != 0; p++) |
356 | if (p->usage == (usage & 0xffff)) { | 366 | if (p->usage == (usage & 0xffff)) { |
357 | printk("%s", p->description); | 367 | if (!f) |
368 | printk("%s", p->description); | ||
369 | else | ||
370 | seq_printf(f, | ||
371 | "%s", | ||
372 | p->description); | ||
358 | return; | 373 | return; |
359 | } | 374 | } |
360 | break; | 375 | break; |
361 | } | 376 | } |
362 | printk("%04x", usage & 0xffff); | 377 | if (!f) |
378 | printk("%04x", usage & 0xffff); | ||
379 | else | ||
380 | seq_printf(f, "%04x", usage & 0xffff); | ||
363 | } | 381 | } |
364 | EXPORT_SYMBOL_GPL(hid_resolv_usage); | 382 | EXPORT_SYMBOL_GPL(hid_resolv_usage); |
365 | 383 | ||
366 | static void tab(int n) { | 384 | static void tab(int n, struct seq_file *f) { |
367 | printk(KERN_DEBUG "%*s", n, ""); | 385 | seq_printf(f, "%*s", n, ""); |
368 | } | 386 | } |
369 | 387 | ||
370 | void hid_dump_field(struct hid_field *field, int n) { | 388 | void hid_dump_field(struct hid_field *field, int n, struct seq_file *f) { |
371 | int j; | 389 | int j; |
372 | 390 | ||
373 | if (!hid_debug) | ||
374 | return; | ||
375 | |||
376 | if (field->physical) { | 391 | if (field->physical) { |
377 | tab(n); | 392 | tab(n, f); |
378 | printk("Physical("); | 393 | seq_printf(f, "Physical("); |
379 | hid_resolv_usage(field->physical); printk(")\n"); | 394 | hid_resolv_usage(field->physical, f); seq_printf(f, ")\n"); |
380 | } | 395 | } |
381 | if (field->logical) { | 396 | if (field->logical) { |
382 | tab(n); | 397 | tab(n, f); |
383 | printk("Logical("); | 398 | seq_printf(f, "Logical("); |
384 | hid_resolv_usage(field->logical); printk(")\n"); | 399 | hid_resolv_usage(field->logical, f); seq_printf(f, ")\n"); |
385 | } | 400 | } |
386 | tab(n); printk("Usage(%d)\n", field->maxusage); | 401 | tab(n, f); seq_printf(f, "Usage(%d)\n", field->maxusage); |
387 | for (j = 0; j < field->maxusage; j++) { | 402 | for (j = 0; j < field->maxusage; j++) { |
388 | tab(n+2); hid_resolv_usage(field->usage[j].hid); printk("\n"); | 403 | tab(n+2, f); hid_resolv_usage(field->usage[j].hid, f); seq_printf(f, "\n"); |
389 | } | 404 | } |
390 | if (field->logical_minimum != field->logical_maximum) { | 405 | if (field->logical_minimum != field->logical_maximum) { |
391 | tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum); | 406 | tab(n, f); seq_printf(f, "Logical Minimum(%d)\n", field->logical_minimum); |
392 | tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum); | 407 | tab(n, f); seq_printf(f, "Logical Maximum(%d)\n", field->logical_maximum); |
393 | } | 408 | } |
394 | if (field->physical_minimum != field->physical_maximum) { | 409 | if (field->physical_minimum != field->physical_maximum) { |
395 | tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum); | 410 | tab(n, f); seq_printf(f, "Physical Minimum(%d)\n", field->physical_minimum); |
396 | tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum); | 411 | tab(n, f); seq_printf(f, "Physical Maximum(%d)\n", field->physical_maximum); |
397 | } | 412 | } |
398 | if (field->unit_exponent) { | 413 | if (field->unit_exponent) { |
399 | tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent); | 414 | tab(n, f); seq_printf(f, "Unit Exponent(%d)\n", field->unit_exponent); |
400 | } | 415 | } |
401 | if (field->unit) { | 416 | if (field->unit) { |
402 | static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; | 417 | static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; |
@@ -417,77 +432,75 @@ void hid_dump_field(struct hid_field *field, int n) { | |||
417 | data >>= 4; | 432 | data >>= 4; |
418 | 433 | ||
419 | if(sys > 4) { | 434 | if(sys > 4) { |
420 | tab(n); printk("Unit(Invalid)\n"); | 435 | tab(n, f); seq_printf(f, "Unit(Invalid)\n"); |
421 | } | 436 | } |
422 | else { | 437 | else { |
423 | int earlier_unit = 0; | 438 | int earlier_unit = 0; |
424 | 439 | ||
425 | tab(n); printk("Unit(%s : ", systems[sys]); | 440 | tab(n, f); seq_printf(f, "Unit(%s : ", systems[sys]); |
426 | 441 | ||
427 | for (i=1 ; i<sizeof(__u32)*2 ; i++) { | 442 | for (i=1 ; i<sizeof(__u32)*2 ; i++) { |
428 | char nibble = data & 0xf; | 443 | char nibble = data & 0xf; |
429 | data >>= 4; | 444 | data >>= 4; |
430 | if (nibble != 0) { | 445 | if (nibble != 0) { |
431 | if(earlier_unit++ > 0) | 446 | if(earlier_unit++ > 0) |
432 | printk("*"); | 447 | seq_printf(f, "*"); |
433 | printk("%s", units[sys][i]); | 448 | seq_printf(f, "%s", units[sys][i]); |
434 | if(nibble != 1) { | 449 | if(nibble != 1) { |
435 | /* This is a _signed_ nibble(!) */ | 450 | /* This is a _signed_ nibble(!) */ |
436 | 451 | ||
437 | int val = nibble & 0x7; | 452 | int val = nibble & 0x7; |
438 | if(nibble & 0x08) | 453 | if(nibble & 0x08) |
439 | val = -((0x7 & ~val) +1); | 454 | val = -((0x7 & ~val) +1); |
440 | printk("^%d", val); | 455 | seq_printf(f, "^%d", val); |
441 | } | 456 | } |
442 | } | 457 | } |
443 | } | 458 | } |
444 | printk(")\n"); | 459 | seq_printf(f, ")\n"); |
445 | } | 460 | } |
446 | } | 461 | } |
447 | tab(n); printk("Report Size(%u)\n", field->report_size); | 462 | tab(n, f); seq_printf(f, "Report Size(%u)\n", field->report_size); |
448 | tab(n); printk("Report Count(%u)\n", field->report_count); | 463 | tab(n, f); seq_printf(f, "Report Count(%u)\n", field->report_count); |
449 | tab(n); printk("Report Offset(%u)\n", field->report_offset); | 464 | tab(n, f); seq_printf(f, "Report Offset(%u)\n", field->report_offset); |
450 | 465 | ||
451 | tab(n); printk("Flags( "); | 466 | tab(n, f); seq_printf(f, "Flags( "); |
452 | j = field->flags; | 467 | j = field->flags; |
453 | printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); | 468 | seq_printf(f, "%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); |
454 | printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); | 469 | seq_printf(f, "%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); |
455 | printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); | 470 | seq_printf(f, "%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); |
456 | printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); | 471 | seq_printf(f, "%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); |
457 | printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); | 472 | seq_printf(f, "%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); |
458 | printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : ""); | 473 | seq_printf(f, "%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : ""); |
459 | printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); | 474 | seq_printf(f, "%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); |
460 | printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); | 475 | seq_printf(f, "%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); |
461 | printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); | 476 | seq_printf(f, "%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); |
462 | printk(")\n"); | 477 | seq_printf(f, ")\n"); |
463 | } | 478 | } |
464 | EXPORT_SYMBOL_GPL(hid_dump_field); | 479 | EXPORT_SYMBOL_GPL(hid_dump_field); |
465 | 480 | ||
466 | void hid_dump_device(struct hid_device *device) { | 481 | void hid_dump_device(struct hid_device *device, struct seq_file *f) |
482 | { | ||
467 | struct hid_report_enum *report_enum; | 483 | struct hid_report_enum *report_enum; |
468 | struct hid_report *report; | 484 | struct hid_report *report; |
469 | struct list_head *list; | 485 | struct list_head *list; |
470 | unsigned i,k; | 486 | unsigned i,k; |
471 | static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; | 487 | static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; |
472 | 488 | ||
473 | if (!hid_debug) | ||
474 | return; | ||
475 | |||
476 | for (i = 0; i < HID_REPORT_TYPES; i++) { | 489 | for (i = 0; i < HID_REPORT_TYPES; i++) { |
477 | report_enum = device->report_enum + i; | 490 | report_enum = device->report_enum + i; |
478 | list = report_enum->report_list.next; | 491 | list = report_enum->report_list.next; |
479 | while (list != &report_enum->report_list) { | 492 | while (list != &report_enum->report_list) { |
480 | report = (struct hid_report *) list; | 493 | report = (struct hid_report *) list; |
481 | tab(2); | 494 | tab(2, f); |
482 | printk("%s", table[i]); | 495 | seq_printf(f, "%s", table[i]); |
483 | if (report->id) | 496 | if (report->id) |
484 | printk("(%d)", report->id); | 497 | seq_printf(f, "(%d)", report->id); |
485 | printk("[%s]", table[report->type]); | 498 | seq_printf(f, "[%s]", table[report->type]); |
486 | printk("\n"); | 499 | seq_printf(f, "\n"); |
487 | for (k = 0; k < report->maxfield; k++) { | 500 | for (k = 0; k < report->maxfield; k++) { |
488 | tab(4); | 501 | tab(4, f); |
489 | printk("Field(%d)\n", k); | 502 | seq_printf(f, "Field(%d)\n", k); |
490 | hid_dump_field(report->field[k], 6); | 503 | hid_dump_field(report->field[k], 6, f); |
491 | } | 504 | } |
492 | list = list->next; | 505 | list = list->next; |
493 | } | 506 | } |
@@ -500,7 +513,7 @@ void hid_dump_input(struct hid_usage *usage, __s32 value) { | |||
500 | return; | 513 | return; |
501 | 514 | ||
502 | printk(KERN_DEBUG "hid-debug: input "); | 515 | printk(KERN_DEBUG "hid-debug: input "); |
503 | hid_resolv_usage(usage->hid); | 516 | hid_resolv_usage(usage->hid, NULL); |
504 | printk(" = %d\n", value); | 517 | printk(" = %d\n", value); |
505 | } | 518 | } |
506 | EXPORT_SYMBOL_GPL(hid_dump_input); | 519 | EXPORT_SYMBOL_GPL(hid_dump_input); |
@@ -767,12 +780,84 @@ static const char **names[EV_MAX + 1] = { | |||
767 | [EV_SND] = sounds, [EV_REP] = repeats, | 780 | [EV_SND] = sounds, [EV_REP] = repeats, |
768 | }; | 781 | }; |
769 | 782 | ||
770 | void hid_resolv_event(__u8 type, __u16 code) { | 783 | void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f) { |
771 | |||
772 | if (!hid_debug) | ||
773 | return; | ||
774 | 784 | ||
775 | printk("%s.%s", events[type] ? events[type] : "?", | 785 | seq_printf(f, "%s.%s", events[type] ? events[type] : "?", |
776 | names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); | 786 | names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); |
777 | } | 787 | } |
778 | EXPORT_SYMBOL_GPL(hid_resolv_event); | 788 | |
789 | void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f) | ||
790 | { | ||
791 | int i, j, k; | ||
792 | struct hid_report *report; | ||
793 | struct hid_usage *usage; | ||
794 | |||
795 | for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { | ||
796 | list_for_each_entry(report, &hid->report_enum[k].report_list, list) { | ||
797 | for (i = 0; i < report->maxfield; i++) { | ||
798 | for ( j = 0; j < report->field[i]->maxusage; j++) { | ||
799 | usage = report->field[i]->usage + j; | ||
800 | hid_resolv_usage(usage->hid, f); | ||
801 | seq_printf(f, " ---> "); | ||
802 | hid_resolv_event(usage->type, usage->code, f); | ||
803 | seq_printf(f, "\n"); | ||
804 | } | ||
805 | } | ||
806 | } | ||
807 | } | ||
808 | |||
809 | } | ||
810 | |||
811 | static int hid_debug_rdesc_show(struct seq_file *f, void *p) | ||
812 | { | ||
813 | struct hid_device *hdev = f->private; | ||
814 | int i; | ||
815 | |||
816 | /* dump HID report descriptor */ | ||
817 | for (i = 0; i < hdev->rsize; i++) | ||
818 | seq_printf(f, "%02x ", hdev->rdesc[i]); | ||
819 | seq_printf(f, "\n\n"); | ||
820 | |||
821 | /* dump parsed data and input mappings */ | ||
822 | hid_dump_device(hdev, f); | ||
823 | seq_printf(f, "\n"); | ||
824 | hid_dump_input_mapping(hdev, f); | ||
825 | |||
826 | return 0; | ||
827 | } | ||
828 | |||
829 | static int hid_debug_rdesc_open(struct inode *inode, struct file *file) | ||
830 | { | ||
831 | return single_open(file, hid_debug_rdesc_show, inode->i_private); | ||
832 | } | ||
833 | |||
834 | static const struct file_operations hid_debug_rdesc_fops = { | ||
835 | .open = hid_debug_rdesc_open, | ||
836 | .read = seq_read, | ||
837 | .llseek = seq_lseek, | ||
838 | .release = single_release, | ||
839 | }; | ||
840 | |||
841 | void hid_debug_register(struct hid_device *hdev, const char *name) | ||
842 | { | ||
843 | hdev->debug_dir = debugfs_create_dir(name, hid_debug_root); | ||
844 | hdev->debug_rdesc = debugfs_create_file("rdesc", 0400, | ||
845 | hdev->debug_dir, hdev, &hid_debug_rdesc_fops); | ||
846 | } | ||
847 | |||
848 | void hid_debug_unregister(struct hid_device *hdev) | ||
849 | { | ||
850 | debugfs_remove(hdev->debug_rdesc); | ||
851 | debugfs_remove(hdev->debug_dir); | ||
852 | } | ||
853 | |||
854 | void hid_debug_init(void) | ||
855 | { | ||
856 | hid_debug_root = debugfs_create_dir("hid", NULL); | ||
857 | } | ||
858 | |||
859 | void hid_debug_exit(void) | ||
860 | { | ||
861 | debugfs_remove_recursive(hid_debug_root); | ||
862 | } | ||
863 | |||
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f183b7147e1..5862b0f3b55d 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c | |||
@@ -159,17 +159,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel | |||
159 | 159 | ||
160 | field->hidinput = hidinput; | 160 | field->hidinput = hidinput; |
161 | 161 | ||
162 | dbg_hid("Mapping: "); | ||
163 | hid_resolv_usage(usage->hid); | ||
164 | dbg_hid_line(" ---> "); | ||
165 | |||
166 | if (field->flags & HID_MAIN_ITEM_CONSTANT) | 162 | if (field->flags & HID_MAIN_ITEM_CONSTANT) |
167 | goto ignore; | 163 | goto ignore; |
168 | 164 | ||
169 | /* only LED usages are supported in output fields */ | 165 | /* only LED usages are supported in output fields */ |
170 | if (field->report_type == HID_OUTPUT_REPORT && | 166 | if (field->report_type == HID_OUTPUT_REPORT && |
171 | (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { | 167 | (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { |
172 | dbg_hid_line(" [non-LED output field] "); | ||
173 | goto ignore; | 168 | goto ignore; |
174 | } | 169 | } |
175 | 170 | ||
@@ -561,15 +556,9 @@ mapped: | |||
561 | set_bit(MSC_SCAN, input->mscbit); | 556 | set_bit(MSC_SCAN, input->mscbit); |
562 | } | 557 | } |
563 | 558 | ||
564 | hid_resolv_event(usage->type, usage->code); | ||
565 | |||
566 | dbg_hid_line("\n"); | ||
567 | |||
568 | return; | ||
569 | |||
570 | ignore: | 559 | ignore: |
571 | dbg_hid_line("IGNORED\n"); | ||
572 | return; | 560 | return; |
561 | |||
573 | } | 562 | } |
574 | 563 | ||
575 | void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) | 564 | void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) |
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index ac8049b5f1e9..708aa52d0753 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c | |||
@@ -4,8 +4,8 @@ | |||
4 | * Copyright (c) 1999 Andreas Gal | 4 | * Copyright (c) 1999 Andreas Gal |
5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
7 | * Copyright (c) 2006-2008 Jiri Kosina | ||
8 | * Copyright (c) 2007-2008 Oliver Neukum | 7 | * Copyright (c) 2007-2008 Oliver Neukum |
8 | * Copyright (c) 2006-2009 Jiri Kosina | ||
9 | */ | 9 | */ |
10 | 10 | ||
11 | /* | 11 | /* |
@@ -886,11 +886,6 @@ static int usbhid_parse(struct hid_device *hid) | |||
886 | goto err; | 886 | goto err; |
887 | } | 887 | } |
888 | 888 | ||
889 | dbg_hid("report descriptor (size %u, read %d) = ", rsize, n); | ||
890 | for (n = 0; n < rsize; n++) | ||
891 | dbg_hid_line(" %02x", (unsigned char) rdesc[n]); | ||
892 | dbg_hid_line("\n"); | ||
893 | |||
894 | ret = hid_parse_report(hid, rdesc, rsize); | 889 | ret = hid_parse_report(hid, rdesc, rsize); |
895 | kfree(rdesc); | 890 | kfree(rdesc); |
896 | if (ret) { | 891 | if (ret) { |
@@ -1005,7 +1000,6 @@ static int usbhid_start(struct hid_device *hid) | |||
1005 | usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); | 1000 | usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); |
1006 | 1001 | ||
1007 | usbhid_init_reports(hid); | 1002 | usbhid_init_reports(hid); |
1008 | hid_dump_device(hid); | ||
1009 | 1003 | ||
1010 | set_bit(HID_STARTED, &usbhid->iofl); | 1004 | set_bit(HID_STARTED, &usbhid->iofl); |
1011 | 1005 | ||
diff --git a/include/linux/hid-debug.h b/include/linux/hid-debug.h index 50d568ec178a..516e12c33235 100644 --- a/include/linux/hid-debug.h +++ b/include/linux/hid-debug.h | |||
@@ -2,7 +2,7 @@ | |||
2 | #define __HID_DEBUG_H | 2 | #define __HID_DEBUG_H |
3 | 3 | ||
4 | /* | 4 | /* |
5 | * Copyright (c) 2007 Jiri Kosina | 5 | * Copyright (c) 2007-2009 Jiri Kosina |
6 | */ | 6 | */ |
7 | 7 | ||
8 | /* | 8 | /* |
@@ -22,24 +22,30 @@ | |||
22 | * | 22 | * |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #ifdef CONFIG_HID_DEBUG | 25 | #ifdef CONFIG_DEBUG_FS |
26 | 26 | ||
27 | void hid_dump_input(struct hid_usage *, __s32); | 27 | void hid_dump_input(struct hid_usage *, __s32); |
28 | void hid_dump_device(struct hid_device *); | 28 | void hid_dump_device(struct hid_device *, struct seq_file *); |
29 | void hid_dump_field(struct hid_field *, int); | 29 | void hid_dump_field(struct hid_field *, int, struct seq_file *); |
30 | void hid_resolv_usage(unsigned); | 30 | void hid_resolv_usage(unsigned, struct seq_file *); |
31 | void hid_resolv_event(__u8, __u16); | 31 | void hid_debug_register(struct hid_device *, const char *); |
32 | void hid_debug_unregister(struct hid_device *); | ||
33 | void hid_debug_init(void); | ||
34 | void hid_debug_exit(void); | ||
32 | 35 | ||
33 | #else | 36 | #else |
34 | 37 | ||
35 | #define hid_dump_input(a,b) do { } while (0) | 38 | #define hid_dump_input(a,b) do { } while (0) |
36 | #define hid_dump_device(c) do { } while (0) | 39 | #define hid_dump_device(c) do { } while (0) |
37 | #define hid_dump_field(a,b) do { } while (0) | 40 | #define hid_dump_field(a,b) do { } while (0) |
38 | #define hid_resolv_usage(a) do { } while (0) | 41 | #define hid_resolv_usage(a) do { } while (0) |
39 | #define hid_resolv_event(a,b) do { } while (0) | 42 | #define hid_resolv_event(a,b) do { } while (0) |
40 | 43 | #define hid_debug_register(a, b) do { } while (0) | |
41 | #endif /* CONFIG_HID_DEBUG */ | 44 | #define hid_debug_unregister(a) do { } while (0) |
45 | #define hid_debug_init() do { } while (0) | ||
46 | #define hid_debug_exit() do { } while (0) | ||
42 | 47 | ||
48 | #endif | ||
43 | 49 | ||
44 | #endif | 50 | #endif |
45 | 51 | ||
diff --git a/include/linux/hid.h b/include/linux/hid.h index a72876e43589..da09ab140ef1 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h | |||
@@ -451,6 +451,10 @@ struct hid_device { /* device report descriptor */ | |||
451 | char phys[64]; /* Device physical location */ | 451 | char phys[64]; /* Device physical location */ |
452 | char uniq[64]; /* Device unique identifier (serial #) */ | 452 | char uniq[64]; /* Device unique identifier (serial #) */ |
453 | 453 | ||
454 | /* debugfs */ | ||
455 | struct dentry *debug_dir; | ||
456 | struct dentry *debug_rdesc; | ||
457 | |||
454 | void *driver_data; | 458 | void *driver_data; |
455 | 459 | ||
456 | /* temporary hid_ff handling (until moved to the drivers) */ | 460 | /* temporary hid_ff handling (until moved to the drivers) */ |