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>
This commit is contained in:
parent
8ebf975608
commit
a635f9dd83
|
@ -31,21 +31,6 @@ config HID
|
||||||
|
|
||||||
If unsure, say Y.
|
If unsure, say Y.
|
||||||
|
|
||||||
config HID_DEBUG
|
|
||||||
bool "HID debugging support"
|
|
||||||
default y
|
|
||||||
depends on HID
|
|
||||||
---help---
|
|
||||||
This option lets the HID layer output diagnostics about its internal
|
|
||||||
state, resolve HID usages, dump HID fields, etc. Individual HID drivers
|
|
||||||
use this debugging facility to output information about individual HID
|
|
||||||
devices, etc.
|
|
||||||
|
|
||||||
This feature is useful for those who are either debugging the HID parser
|
|
||||||
or any HID hardware device.
|
|
||||||
|
|
||||||
If unsure, say Y.
|
|
||||||
|
|
||||||
config HIDRAW
|
config HIDRAW
|
||||||
bool "/dev/hidraw raw HID device support"
|
bool "/dev/hidraw raw HID device support"
|
||||||
depends on HID
|
depends on HID
|
||||||
|
|
|
@ -3,9 +3,12 @@
|
||||||
#
|
#
|
||||||
hid-objs := hid-core.o hid-input.o
|
hid-objs := hid-core.o hid-input.o
|
||||||
|
|
||||||
|
ifdef CONFIG_DEBUG_FS
|
||||||
|
hid-objs += hid-debug.o
|
||||||
|
endif
|
||||||
|
|
||||||
obj-$(CONFIG_HID) += hid.o
|
obj-$(CONFIG_HID) += hid.o
|
||||||
|
|
||||||
hid-$(CONFIG_HID_DEBUG) += hid-debug.o
|
|
||||||
hid-$(CONFIG_HIDRAW) += hidraw.o
|
hid-$(CONFIG_HIDRAW) += hidraw.o
|
||||||
|
|
||||||
hid-logitech-objs := hid-lg.o
|
hid-logitech-objs := hid-lg.o
|
||||||
|
|
|
@ -44,12 +44,10 @@
|
||||||
#define DRIVER_DESC "HID core driver"
|
#define DRIVER_DESC "HID core driver"
|
||||||
#define DRIVER_LICENSE "GPL"
|
#define DRIVER_LICENSE "GPL"
|
||||||
|
|
||||||
#ifdef CONFIG_HID_DEBUG
|
|
||||||
int hid_debug = 0;
|
int hid_debug = 0;
|
||||||
module_param_named(debug, hid_debug, int, 0600);
|
module_param_named(debug, hid_debug, int, 0600);
|
||||||
MODULE_PARM_DESC(debug, "HID debugging (0=off, 1=probing info, 2=continuous data dumping)");
|
MODULE_PARM_DESC(debug, "HID debugging (0=off, 1=probing info, 2=continuous data dumping)");
|
||||||
EXPORT_SYMBOL_GPL(hid_debug);
|
EXPORT_SYMBOL_GPL(hid_debug);
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register a new report for a device.
|
* Register a new report for a device.
|
||||||
|
@ -987,7 +985,6 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
|
||||||
|
|
||||||
if (offset >= field->report_count) {
|
if (offset >= field->report_count) {
|
||||||
dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count);
|
dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count);
|
||||||
hid_dump_field(field, 8);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (field->logical_minimum < 0) {
|
if (field->logical_minimum < 0) {
|
||||||
|
@ -1721,6 +1718,8 @@ int hid_add_device(struct hid_device *hdev)
|
||||||
if (!ret)
|
if (!ret)
|
||||||
hdev->status |= HID_STAT_ADDED;
|
hdev->status |= HID_STAT_ADDED;
|
||||||
|
|
||||||
|
hid_debug_register(hdev, dev_name(&hdev->dev));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hid_add_device);
|
EXPORT_SYMBOL_GPL(hid_add_device);
|
||||||
|
@ -1768,6 +1767,7 @@ static void hid_remove_device(struct hid_device *hdev)
|
||||||
{
|
{
|
||||||
if (hdev->status & HID_STAT_ADDED) {
|
if (hdev->status & HID_STAT_ADDED) {
|
||||||
device_del(&hdev->dev);
|
device_del(&hdev->dev);
|
||||||
|
hid_debug_unregister(hdev);
|
||||||
hdev->status &= ~HID_STAT_ADDED;
|
hdev->status &= ~HID_STAT_ADDED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1843,6 +1843,10 @@ static int __init hid_init(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (hid_debug)
|
||||||
|
printk(KERN_WARNING "HID: hid_debug parameter has been deprecated. "
|
||||||
|
"Debugging data are now provided via debugfs\n");
|
||||||
|
|
||||||
ret = bus_register(&hid_bus_type);
|
ret = bus_register(&hid_bus_type);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printk(KERN_ERR "HID: can't register hid bus\n");
|
printk(KERN_ERR "HID: can't register hid bus\n");
|
||||||
|
@ -1853,6 +1857,8 @@ static int __init hid_init(void)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_bus;
|
goto err_bus;
|
||||||
|
|
||||||
|
hid_debug_init();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err_bus:
|
err_bus:
|
||||||
bus_unregister(&hid_bus_type);
|
bus_unregister(&hid_bus_type);
|
||||||
|
@ -1862,6 +1868,7 @@ err:
|
||||||
|
|
||||||
static void __exit hid_exit(void)
|
static void __exit hid_exit(void)
|
||||||
{
|
{
|
||||||
|
hid_debug_exit();
|
||||||
hidraw_exit();
|
hidraw_exit();
|
||||||
bus_unregister(&hid_bus_type);
|
bus_unregister(&hid_bus_type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/*
|
/*
|
||||||
* (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de>
|
* (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de>
|
||||||
* (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
|
* (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
|
||||||
* (c) 2007 Jiri Kosina
|
* (c) 2007-2009 Jiri Kosina
|
||||||
*
|
*
|
||||||
* Some debug stuff for the HID parser.
|
* HID debugging support
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -26,9 +26,13 @@
|
||||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/debugfs.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
#include <linux/hid.h>
|
#include <linux/hid.h>
|
||||||
#include <linux/hid-debug.h>
|
#include <linux/hid-debug.h>
|
||||||
|
|
||||||
|
static struct dentry *hid_debug_root;
|
||||||
|
|
||||||
struct hid_usage_entry {
|
struct hid_usage_entry {
|
||||||
unsigned page;
|
unsigned page;
|
||||||
unsigned usage;
|
unsigned usage;
|
||||||
|
@ -331,72 +335,83 @@ static const struct hid_usage_entry hid_usage_table[] = {
|
||||||
{ 0, 0, NULL }
|
{ 0, 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
static void resolv_usage_page(unsigned page) {
|
static void resolv_usage_page(unsigned page, struct seq_file *f) {
|
||||||
const struct hid_usage_entry *p;
|
const struct hid_usage_entry *p;
|
||||||
|
|
||||||
for (p = hid_usage_table; p->description; p++)
|
for (p = hid_usage_table; p->description; p++)
|
||||||
if (p->page == page) {
|
if (p->page == page) {
|
||||||
printk("%s", p->description);
|
if (!f)
|
||||||
|
printk("%s", p->description);
|
||||||
|
else
|
||||||
|
seq_printf(f, "%s", p->description);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
printk("%04x", page);
|
if (!f)
|
||||||
|
printk("%04x", page);
|
||||||
|
else
|
||||||
|
seq_printf(f, "%04x", page);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hid_resolv_usage(unsigned usage) {
|
void hid_resolv_usage(unsigned usage, struct seq_file *f) {
|
||||||
const struct hid_usage_entry *p;
|
const struct hid_usage_entry *p;
|
||||||
|
|
||||||
if (!hid_debug)
|
resolv_usage_page(usage >> 16, f);
|
||||||
return;
|
if (!f)
|
||||||
|
printk(".");
|
||||||
resolv_usage_page(usage >> 16);
|
else
|
||||||
printk(".");
|
seq_printf(f, ".");
|
||||||
for (p = hid_usage_table; p->description; p++)
|
for (p = hid_usage_table; p->description; p++)
|
||||||
if (p->page == (usage >> 16)) {
|
if (p->page == (usage >> 16)) {
|
||||||
for(++p; p->description && p->usage != 0; p++)
|
for(++p; p->description && p->usage != 0; p++)
|
||||||
if (p->usage == (usage & 0xffff)) {
|
if (p->usage == (usage & 0xffff)) {
|
||||||
printk("%s", p->description);
|
if (!f)
|
||||||
|
printk("%s", p->description);
|
||||||
|
else
|
||||||
|
seq_printf(f,
|
||||||
|
"%s",
|
||||||
|
p->description);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
printk("%04x", usage & 0xffff);
|
if (!f)
|
||||||
|
printk("%04x", usage & 0xffff);
|
||||||
|
else
|
||||||
|
seq_printf(f, "%04x", usage & 0xffff);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hid_resolv_usage);
|
EXPORT_SYMBOL_GPL(hid_resolv_usage);
|
||||||
|
|
||||||
static void tab(int n) {
|
static void tab(int n, struct seq_file *f) {
|
||||||
printk(KERN_DEBUG "%*s", n, "");
|
seq_printf(f, "%*s", n, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void hid_dump_field(struct hid_field *field, int n) {
|
void hid_dump_field(struct hid_field *field, int n, struct seq_file *f) {
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
if (!hid_debug)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (field->physical) {
|
if (field->physical) {
|
||||||
tab(n);
|
tab(n, f);
|
||||||
printk("Physical(");
|
seq_printf(f, "Physical(");
|
||||||
hid_resolv_usage(field->physical); printk(")\n");
|
hid_resolv_usage(field->physical, f); seq_printf(f, ")\n");
|
||||||
}
|
}
|
||||||
if (field->logical) {
|
if (field->logical) {
|
||||||
tab(n);
|
tab(n, f);
|
||||||
printk("Logical(");
|
seq_printf(f, "Logical(");
|
||||||
hid_resolv_usage(field->logical); printk(")\n");
|
hid_resolv_usage(field->logical, f); seq_printf(f, ")\n");
|
||||||
}
|
}
|
||||||
tab(n); printk("Usage(%d)\n", field->maxusage);
|
tab(n, f); seq_printf(f, "Usage(%d)\n", field->maxusage);
|
||||||
for (j = 0; j < field->maxusage; j++) {
|
for (j = 0; j < field->maxusage; j++) {
|
||||||
tab(n+2); hid_resolv_usage(field->usage[j].hid); printk("\n");
|
tab(n+2, f); hid_resolv_usage(field->usage[j].hid, f); seq_printf(f, "\n");
|
||||||
}
|
}
|
||||||
if (field->logical_minimum != field->logical_maximum) {
|
if (field->logical_minimum != field->logical_maximum) {
|
||||||
tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum);
|
tab(n, f); seq_printf(f, "Logical Minimum(%d)\n", field->logical_minimum);
|
||||||
tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum);
|
tab(n, f); seq_printf(f, "Logical Maximum(%d)\n", field->logical_maximum);
|
||||||
}
|
}
|
||||||
if (field->physical_minimum != field->physical_maximum) {
|
if (field->physical_minimum != field->physical_maximum) {
|
||||||
tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum);
|
tab(n, f); seq_printf(f, "Physical Minimum(%d)\n", field->physical_minimum);
|
||||||
tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum);
|
tab(n, f); seq_printf(f, "Physical Maximum(%d)\n", field->physical_maximum);
|
||||||
}
|
}
|
||||||
if (field->unit_exponent) {
|
if (field->unit_exponent) {
|
||||||
tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent);
|
tab(n, f); seq_printf(f, "Unit Exponent(%d)\n", field->unit_exponent);
|
||||||
}
|
}
|
||||||
if (field->unit) {
|
if (field->unit) {
|
||||||
static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" };
|
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) {
|
||||||
data >>= 4;
|
data >>= 4;
|
||||||
|
|
||||||
if(sys > 4) {
|
if(sys > 4) {
|
||||||
tab(n); printk("Unit(Invalid)\n");
|
tab(n, f); seq_printf(f, "Unit(Invalid)\n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int earlier_unit = 0;
|
int earlier_unit = 0;
|
||||||
|
|
||||||
tab(n); printk("Unit(%s : ", systems[sys]);
|
tab(n, f); seq_printf(f, "Unit(%s : ", systems[sys]);
|
||||||
|
|
||||||
for (i=1 ; i<sizeof(__u32)*2 ; i++) {
|
for (i=1 ; i<sizeof(__u32)*2 ; i++) {
|
||||||
char nibble = data & 0xf;
|
char nibble = data & 0xf;
|
||||||
data >>= 4;
|
data >>= 4;
|
||||||
if (nibble != 0) {
|
if (nibble != 0) {
|
||||||
if(earlier_unit++ > 0)
|
if(earlier_unit++ > 0)
|
||||||
printk("*");
|
seq_printf(f, "*");
|
||||||
printk("%s", units[sys][i]);
|
seq_printf(f, "%s", units[sys][i]);
|
||||||
if(nibble != 1) {
|
if(nibble != 1) {
|
||||||
/* This is a _signed_ nibble(!) */
|
/* This is a _signed_ nibble(!) */
|
||||||
|
|
||||||
int val = nibble & 0x7;
|
int val = nibble & 0x7;
|
||||||
if(nibble & 0x08)
|
if(nibble & 0x08)
|
||||||
val = -((0x7 & ~val) +1);
|
val = -((0x7 & ~val) +1);
|
||||||
printk("^%d", val);
|
seq_printf(f, "^%d", val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printk(")\n");
|
seq_printf(f, ")\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tab(n); printk("Report Size(%u)\n", field->report_size);
|
tab(n, f); seq_printf(f, "Report Size(%u)\n", field->report_size);
|
||||||
tab(n); printk("Report Count(%u)\n", field->report_count);
|
tab(n, f); seq_printf(f, "Report Count(%u)\n", field->report_count);
|
||||||
tab(n); printk("Report Offset(%u)\n", field->report_offset);
|
tab(n, f); seq_printf(f, "Report Offset(%u)\n", field->report_offset);
|
||||||
|
|
||||||
tab(n); printk("Flags( ");
|
tab(n, f); seq_printf(f, "Flags( ");
|
||||||
j = field->flags;
|
j = field->flags;
|
||||||
printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : "");
|
seq_printf(f, "%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : "");
|
||||||
printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array ");
|
seq_printf(f, "%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array ");
|
||||||
printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
|
seq_printf(f, "%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
|
||||||
printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
|
seq_printf(f, "%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
|
||||||
printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
|
seq_printf(f, "%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
|
||||||
printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : "");
|
seq_printf(f, "%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : "");
|
||||||
printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
|
seq_printf(f, "%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
|
||||||
printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
|
seq_printf(f, "%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
|
||||||
printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
|
seq_printf(f, "%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
|
||||||
printk(")\n");
|
seq_printf(f, ")\n");
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hid_dump_field);
|
EXPORT_SYMBOL_GPL(hid_dump_field);
|
||||||
|
|
||||||
void hid_dump_device(struct hid_device *device) {
|
void hid_dump_device(struct hid_device *device, struct seq_file *f)
|
||||||
|
{
|
||||||
struct hid_report_enum *report_enum;
|
struct hid_report_enum *report_enum;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct list_head *list;
|
struct list_head *list;
|
||||||
unsigned i,k;
|
unsigned i,k;
|
||||||
static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"};
|
static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"};
|
||||||
|
|
||||||
if (!hid_debug)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (i = 0; i < HID_REPORT_TYPES; i++) {
|
for (i = 0; i < HID_REPORT_TYPES; i++) {
|
||||||
report_enum = device->report_enum + i;
|
report_enum = device->report_enum + i;
|
||||||
list = report_enum->report_list.next;
|
list = report_enum->report_list.next;
|
||||||
while (list != &report_enum->report_list) {
|
while (list != &report_enum->report_list) {
|
||||||
report = (struct hid_report *) list;
|
report = (struct hid_report *) list;
|
||||||
tab(2);
|
tab(2, f);
|
||||||
printk("%s", table[i]);
|
seq_printf(f, "%s", table[i]);
|
||||||
if (report->id)
|
if (report->id)
|
||||||
printk("(%d)", report->id);
|
seq_printf(f, "(%d)", report->id);
|
||||||
printk("[%s]", table[report->type]);
|
seq_printf(f, "[%s]", table[report->type]);
|
||||||
printk("\n");
|
seq_printf(f, "\n");
|
||||||
for (k = 0; k < report->maxfield; k++) {
|
for (k = 0; k < report->maxfield; k++) {
|
||||||
tab(4);
|
tab(4, f);
|
||||||
printk("Field(%d)\n", k);
|
seq_printf(f, "Field(%d)\n", k);
|
||||||
hid_dump_field(report->field[k], 6);
|
hid_dump_field(report->field[k], 6, f);
|
||||||
}
|
}
|
||||||
list = list->next;
|
list = list->next;
|
||||||
}
|
}
|
||||||
|
@ -500,7 +513,7 @@ void hid_dump_input(struct hid_usage *usage, __s32 value) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
printk(KERN_DEBUG "hid-debug: input ");
|
printk(KERN_DEBUG "hid-debug: input ");
|
||||||
hid_resolv_usage(usage->hid);
|
hid_resolv_usage(usage->hid, NULL);
|
||||||
printk(" = %d\n", value);
|
printk(" = %d\n", value);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hid_dump_input);
|
EXPORT_SYMBOL_GPL(hid_dump_input);
|
||||||
|
@ -767,12 +780,84 @@ static const char **names[EV_MAX + 1] = {
|
||||||
[EV_SND] = sounds, [EV_REP] = repeats,
|
[EV_SND] = sounds, [EV_REP] = repeats,
|
||||||
};
|
};
|
||||||
|
|
||||||
void hid_resolv_event(__u8 type, __u16 code) {
|
void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f) {
|
||||||
|
|
||||||
if (!hid_debug)
|
seq_printf(f, "%s.%s", events[type] ? events[type] : "?",
|
||||||
return;
|
|
||||||
|
|
||||||
printk("%s.%s", events[type] ? events[type] : "?",
|
|
||||||
names[type] ? (names[type][code] ? names[type][code] : "?") : "?");
|
names[type] ? (names[type][code] ? names[type][code] : "?") : "?");
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hid_resolv_event);
|
|
||||||
|
void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
struct hid_report *report;
|
||||||
|
struct hid_usage *usage;
|
||||||
|
|
||||||
|
for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
|
||||||
|
list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
|
||||||
|
for (i = 0; i < report->maxfield; i++) {
|
||||||
|
for ( j = 0; j < report->field[i]->maxusage; j++) {
|
||||||
|
usage = report->field[i]->usage + j;
|
||||||
|
hid_resolv_usage(usage->hid, f);
|
||||||
|
seq_printf(f, " ---> ");
|
||||||
|
hid_resolv_event(usage->type, usage->code, f);
|
||||||
|
seq_printf(f, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hid_debug_rdesc_show(struct seq_file *f, void *p)
|
||||||
|
{
|
||||||
|
struct hid_device *hdev = f->private;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* dump HID report descriptor */
|
||||||
|
for (i = 0; i < hdev->rsize; i++)
|
||||||
|
seq_printf(f, "%02x ", hdev->rdesc[i]);
|
||||||
|
seq_printf(f, "\n\n");
|
||||||
|
|
||||||
|
/* dump parsed data and input mappings */
|
||||||
|
hid_dump_device(hdev, f);
|
||||||
|
seq_printf(f, "\n");
|
||||||
|
hid_dump_input_mapping(hdev, f);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hid_debug_rdesc_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
return single_open(file, hid_debug_rdesc_show, inode->i_private);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations hid_debug_rdesc_fops = {
|
||||||
|
.open = hid_debug_rdesc_open,
|
||||||
|
.read = seq_read,
|
||||||
|
.llseek = seq_lseek,
|
||||||
|
.release = single_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
void hid_debug_register(struct hid_device *hdev, const char *name)
|
||||||
|
{
|
||||||
|
hdev->debug_dir = debugfs_create_dir(name, hid_debug_root);
|
||||||
|
hdev->debug_rdesc = debugfs_create_file("rdesc", 0400,
|
||||||
|
hdev->debug_dir, hdev, &hid_debug_rdesc_fops);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_debug_unregister(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
debugfs_remove(hdev->debug_rdesc);
|
||||||
|
debugfs_remove(hdev->debug_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_debug_init(void)
|
||||||
|
{
|
||||||
|
hid_debug_root = debugfs_create_dir("hid", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_debug_exit(void)
|
||||||
|
{
|
||||||
|
debugfs_remove_recursive(hid_debug_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,17 +159,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||||
|
|
||||||
field->hidinput = hidinput;
|
field->hidinput = hidinput;
|
||||||
|
|
||||||
dbg_hid("Mapping: ");
|
|
||||||
hid_resolv_usage(usage->hid);
|
|
||||||
dbg_hid_line(" ---> ");
|
|
||||||
|
|
||||||
if (field->flags & HID_MAIN_ITEM_CONSTANT)
|
if (field->flags & HID_MAIN_ITEM_CONSTANT)
|
||||||
goto ignore;
|
goto ignore;
|
||||||
|
|
||||||
/* only LED usages are supported in output fields */
|
/* only LED usages are supported in output fields */
|
||||||
if (field->report_type == HID_OUTPUT_REPORT &&
|
if (field->report_type == HID_OUTPUT_REPORT &&
|
||||||
(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
|
(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
|
||||||
dbg_hid_line(" [non-LED output field] ");
|
|
||||||
goto ignore;
|
goto ignore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,15 +556,9 @@ mapped:
|
||||||
set_bit(MSC_SCAN, input->mscbit);
|
set_bit(MSC_SCAN, input->mscbit);
|
||||||
}
|
}
|
||||||
|
|
||||||
hid_resolv_event(usage->type, usage->code);
|
|
||||||
|
|
||||||
dbg_hid_line("\n");
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
ignore:
|
ignore:
|
||||||
dbg_hid_line("IGNORED\n");
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
|
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
* Copyright (c) 1999 Andreas Gal
|
* Copyright (c) 1999 Andreas Gal
|
||||||
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||||
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
||||||
* Copyright (c) 2006-2008 Jiri Kosina
|
|
||||||
* Copyright (c) 2007-2008 Oliver Neukum
|
* Copyright (c) 2007-2008 Oliver Neukum
|
||||||
|
* Copyright (c) 2006-2009 Jiri Kosina
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -886,11 +886,6 @@ static int usbhid_parse(struct hid_device *hid)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg_hid("report descriptor (size %u, read %d) = ", rsize, n);
|
|
||||||
for (n = 0; n < rsize; n++)
|
|
||||||
dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
|
|
||||||
dbg_hid_line("\n");
|
|
||||||
|
|
||||||
ret = hid_parse_report(hid, rdesc, rsize);
|
ret = hid_parse_report(hid, rdesc, rsize);
|
||||||
kfree(rdesc);
|
kfree(rdesc);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -1005,7 +1000,6 @@ static int usbhid_start(struct hid_device *hid)
|
||||||
usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
|
usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
|
||||||
|
|
||||||
usbhid_init_reports(hid);
|
usbhid_init_reports(hid);
|
||||||
hid_dump_device(hid);
|
|
||||||
|
|
||||||
set_bit(HID_STARTED, &usbhid->iofl);
|
set_bit(HID_STARTED, &usbhid->iofl);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define __HID_DEBUG_H
|
#define __HID_DEBUG_H
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Jiri Kosina
|
* Copyright (c) 2007-2009 Jiri Kosina
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -22,24 +22,30 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef CONFIG_HID_DEBUG
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
|
||||||
void hid_dump_input(struct hid_usage *, __s32);
|
void hid_dump_input(struct hid_usage *, __s32);
|
||||||
void hid_dump_device(struct hid_device *);
|
void hid_dump_device(struct hid_device *, struct seq_file *);
|
||||||
void hid_dump_field(struct hid_field *, int);
|
void hid_dump_field(struct hid_field *, int, struct seq_file *);
|
||||||
void hid_resolv_usage(unsigned);
|
void hid_resolv_usage(unsigned, struct seq_file *);
|
||||||
void hid_resolv_event(__u8, __u16);
|
void hid_debug_register(struct hid_device *, const char *);
|
||||||
|
void hid_debug_unregister(struct hid_device *);
|
||||||
|
void hid_debug_init(void);
|
||||||
|
void hid_debug_exit(void);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define hid_dump_input(a,b) do { } while (0)
|
#define hid_dump_input(a,b) do { } while (0)
|
||||||
#define hid_dump_device(c) do { } while (0)
|
#define hid_dump_device(c) do { } while (0)
|
||||||
#define hid_dump_field(a,b) do { } while (0)
|
#define hid_dump_field(a,b) do { } while (0)
|
||||||
#define hid_resolv_usage(a) do { } while (0)
|
#define hid_resolv_usage(a) do { } while (0)
|
||||||
#define hid_resolv_event(a,b) do { } while (0)
|
#define hid_resolv_event(a,b) do { } while (0)
|
||||||
|
#define hid_debug_register(a, b) do { } while (0)
|
||||||
#endif /* CONFIG_HID_DEBUG */
|
#define hid_debug_unregister(a) do { } while (0)
|
||||||
|
#define hid_debug_init() do { } while (0)
|
||||||
|
#define hid_debug_exit() do { } while (0)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -451,6 +451,10 @@ struct hid_device { /* device report descriptor */
|
||||||
char phys[64]; /* Device physical location */
|
char phys[64]; /* Device physical location */
|
||||||
char uniq[64]; /* Device unique identifier (serial #) */
|
char uniq[64]; /* Device unique identifier (serial #) */
|
||||||
|
|
||||||
|
/* debugfs */
|
||||||
|
struct dentry *debug_dir;
|
||||||
|
struct dentry *debug_rdesc;
|
||||||
|
|
||||||
void *driver_data;
|
void *driver_data;
|
||||||
|
|
||||||
/* temporary hid_ff handling (until moved to the drivers) */
|
/* temporary hid_ff handling (until moved to the drivers) */
|
||||||
|
|
Loading…
Reference in a new issue