aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-debug.c
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2009-06-12 09:20:55 -0400
committerJiri Kosina <jkosina@suse.cz>2009-06-12 09:20:55 -0400
commita635f9dd83f3382577f4544a96df12356e951a40 (patch)
treec6fa27df6d01c34e304a32c9f423d569f7358346 /drivers/hid/hid-debug.c
parent8ebf975608aaebd7feb33d77f07ba21a6380e086 (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>
Diffstat (limited to 'drivers/hid/hid-debug.c')
-rw-r--r--drivers/hid/hid-debug.c227
1 files changed, 156 insertions, 71 deletions
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
34static struct dentry *hid_debug_root;
35
32struct hid_usage_entry { 36struct 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
334static void resolv_usage_page(unsigned page) { 338static 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
345void hid_resolv_usage(unsigned usage) { 355void 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}
364EXPORT_SYMBOL_GPL(hid_resolv_usage); 382EXPORT_SYMBOL_GPL(hid_resolv_usage);
365 383
366static void tab(int n) { 384static void tab(int n, struct seq_file *f) {
367 printk(KERN_DEBUG "%*s", n, ""); 385 seq_printf(f, "%*s", n, "");
368} 386}
369 387
370void hid_dump_field(struct hid_field *field, int n) { 388void 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}
464EXPORT_SYMBOL_GPL(hid_dump_field); 479EXPORT_SYMBOL_GPL(hid_dump_field);
465 480
466void hid_dump_device(struct hid_device *device) { 481void 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}
506EXPORT_SYMBOL_GPL(hid_dump_input); 519EXPORT_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
770void hid_resolv_event(__u8 type, __u16 code) { 783void 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}
778EXPORT_SYMBOL_GPL(hid_resolv_event); 788
789void 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
811static 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
829static 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
834static 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
841void 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
848void hid_debug_unregister(struct hid_device *hdev)
849{
850 debugfs_remove(hdev->debug_rdesc);
851 debugfs_remove(hdev->debug_dir);
852}
853
854void hid_debug_init(void)
855{
856 hid_debug_root = debugfs_create_dir("hid", NULL);
857}
858
859void hid_debug_exit(void)
860{
861 debugfs_remove_recursive(hid_debug_root);
862}
863