diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-14 17:55:53 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-14 17:55:53 -0700 |
commit | 5489375d481c8456c8259b48e107d03b05309d1d (patch) | |
tree | 2bb3c9fe3b68e135444d1e5a47fdf3a1b7adf284 | |
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
37 files changed, 755 insertions, 232 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 7831a0318d3..111afbe8de0 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -31,21 +31,6 @@ config HID 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 bool "/dev/hidraw raw HID device support" depends on HID @@ -152,6 +137,13 @@ config HID_GYRATION ---help--- Support for Gyration remote control. +config HID_TWINHAN + tristate "Twinhan" if EMBEDDED + depends on USB_HID + default !EMBEDDED + ---help--- + Support for Twinhan IR remote control. + config HID_KENSINGTON tristate "Kensington" if EMBEDDED depends on USB_HID @@ -176,6 +168,7 @@ config LOGITECH_FF - Logitech WingMan Cordless RumblePad 2 - Logitech WingMan Force 3D - Logitech Formula Force EX + - Logitech WingMan Formula Force GP - Logitech MOMO Force wheel and if you want to enable force feedback for them. @@ -314,9 +307,9 @@ config THRUSTMASTER_FF depends on HID_THRUSTMASTER select INPUT_FF_MEMLESS ---help--- - Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or - a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel and - want to enable force feedback support for it. + Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or 3, + a THRUSTMASTER Dual Trigger 3-in-1 or a THRUSTMASTER Ferrari GT + Rumble Force or Force Feedback Wheel. config HID_WACOM tristate "Wacom Bluetooth devices support" if EMBEDDED diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index db35151673b..0de2dff5542 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -3,9 +3,12 @@ # hid-objs := hid-core.o hid-input.o +ifdef CONFIG_DEBUG_FS + hid-objs += hid-debug.o +endif + obj-$(CONFIG_HID) += hid.o -hid-$(CONFIG_HID_DEBUG) += hid-debug.o hid-$(CONFIG_HIDRAW) += hidraw.o hid-logitech-objs := hid-lg.o @@ -40,6 +43,7 @@ obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o +obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o obj-$(CONFIG_HID_WACOM) += hid-wacom.o diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index 42ea359e94c..df474c699fb 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c @@ -145,12 +145,12 @@ static struct hid_driver a4_driver = { .remove = a4_remove, }; -static int a4_init(void) +static int __init a4_init(void) { return hid_register_driver(&a4_driver); } -static void a4_exit(void) +static void __exit a4_exit(void) { hid_unregister_driver(&a4_driver); } diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 303ccce05bb..4b96e7a898c 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -451,7 +451,7 @@ static struct hid_driver apple_driver = { .input_mapped = apple_input_mapped, }; -static int apple_init(void) +static int __init apple_init(void) { int ret; @@ -462,7 +462,7 @@ static int apple_init(void) return ret; } -static void apple_exit(void) +static void __exit apple_exit(void) { hid_unregister_driver(&apple_driver); } diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c index 2f6723133a4..4ce7aa3a519 100644 --- a/drivers/hid/hid-belkin.c +++ b/drivers/hid/hid-belkin.c @@ -88,12 +88,12 @@ static struct hid_driver belkin_driver = { .probe = belkin_probe, }; -static int belkin_init(void) +static int __init belkin_init(void) { return hid_register_driver(&belkin_driver); } -static void belkin_exit(void) +static void __exit belkin_exit(void) { hid_unregister_driver(&belkin_driver); } diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c index ab8209e7e45..7e597d7f770 100644 --- a/drivers/hid/hid-cherry.c +++ b/drivers/hid/hid-cherry.c @@ -70,12 +70,12 @@ static struct hid_driver ch_driver = { .input_mapping = ch_input_mapping, }; -static int ch_init(void) +static int __init ch_init(void) { return hid_register_driver(&ch_driver); } -static void ch_exit(void) +static void __exit ch_exit(void) { hid_unregister_driver(&ch_driver); } diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c index 7f91076d849..8965ad93d51 100644 --- a/drivers/hid/hid-chicony.c +++ b/drivers/hid/hid-chicony.c @@ -63,12 +63,12 @@ static struct hid_driver ch_driver = { .input_mapping = ch_input_mapping, }; -static int ch_init(void) +static int __init ch_init(void) { return hid_register_driver(&ch_driver); } -static void ch_exit(void) +static void __exit ch_exit(void) { hid_unregister_driver(&ch_driver); } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5eb10c2ce66..342b7d36d7b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -44,12 +44,10 @@ #define DRIVER_DESC "HID core driver" #define DRIVER_LICENSE "GPL" -#ifdef CONFIG_HID_DEBUG int hid_debug = 0; 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, "toggle HID debugging messages"); EXPORT_SYMBOL_GPL(hid_debug); -#endif /* * Register a new report for a device. @@ -861,7 +859,7 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_driver *hdrv = hid->driver; int ret; - hid_dump_input(usage, value); + hid_dump_input(hid, usage, value); if (hdrv && hdrv->event && hid_match_usage(hid, usage)) { ret = hdrv->event(hid, field, usage, value); @@ -983,11 +981,10 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) { unsigned size = field->report_size; - hid_dump_input(field->usage + offset, value); + hid_dump_input(field->report->device, field->usage + offset, value); if (offset >= field->report_count) { dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count); - hid_dump_field(field, 8); return -1; } if (field->logical_minimum < 0) { @@ -1078,6 +1075,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i struct hid_report_enum *report_enum; struct hid_driver *hdrv; struct hid_report *report; + char *buf; unsigned int i; int ret; @@ -1091,18 +1089,38 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i return -1; } - dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); + buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, + interrupt ? GFP_ATOMIC : GFP_KERNEL); + + if (!buf) { + report = hid_get_report(report_enum, data); + goto nomem; + } + + snprintf(buf, HID_DEBUG_BUFSIZE - 1, + "\nreport (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); + hid_debug_event(hid, buf); report = hid_get_report(report_enum, data); - if (!report) + if (!report) { + kfree(buf); return -1; + } /* dump the report */ - dbg_hid("report %d (size %u) = ", report->id, size); - for (i = 0; i < size; i++) - dbg_hid_line(" %02x", data[i]); - dbg_hid_line("\n"); + snprintf(buf, HID_DEBUG_BUFSIZE - 1, + "report %d (size %u) = ", report->id, size); + hid_debug_event(hid, buf); + for (i = 0; i < size; i++) { + snprintf(buf, HID_DEBUG_BUFSIZE - 1, + " %02x", data[i]); + hid_debug_event(hid, buf); + } + hid_debug_event(hid, "\n"); + + kfree(buf); +nomem: if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) { ret = hdrv->raw_event(hid, report, data, size); if (ret != 0) @@ -1292,6 +1310,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, @@ -1311,15 +1330,17 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, { } }; @@ -1622,12 +1643,8 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) }, { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD3) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD4) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD5) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, @@ -1694,6 +1711,11 @@ static bool hid_ignore(struct hid_device *hdev) hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST) return true; break; + case USB_VENDOR_ID_SOUNDGRAPH: + if (hdev->product >= USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST && + hdev->product <= USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST) + return true; + break; } if (hdev->type == HID_TYPE_USBMOUSE && @@ -1725,6 +1747,8 @@ int hid_add_device(struct hid_device *hdev) if (!ret) hdev->status |= HID_STAT_ADDED; + hid_debug_register(hdev, dev_name(&hdev->dev)); + return ret; } EXPORT_SYMBOL_GPL(hid_add_device); @@ -1761,6 +1785,9 @@ struct hid_device *hid_allocate_device(void) for (i = 0; i < HID_REPORT_TYPES; i++) INIT_LIST_HEAD(&hdev->report_enum[i].report_list); + init_waitqueue_head(&hdev->debug_wait); + INIT_LIST_HEAD(&hdev->debug_list); + return hdev; err: put_device(&hdev->dev); @@ -1772,6 +1799,7 @@ static void hid_remove_device(struct hid_device *hdev) { if (hdev->status & HID_STAT_ADDED) { device_del(&hdev->dev); + hid_debug_unregister(hdev); hdev->status &= ~HID_STAT_ADDED; } } @@ -1847,6 +1875,10 @@ static int __init hid_init(void) { int ret; + if (hid_debug) + printk(KERN_WARNING "HID: hid_debug is now used solely for parser and driver debugging.\n" + "HID: debugfs is now used for inspecting the device (report descriptor, reports)\n"); + ret = bus_register(&hid_bus_type); if (ret) { printk(KERN_ERR "HID: can't register hid bus\n"); @@ -1857,6 +1889,8 @@ static int __init hid_init(void) if (ret) goto err_bus; + hid_debug_init(); + return 0; err_bus: bus_unregister(&hid_bus_type); @@ -1866,6 +1900,7 @@ err: static void __exit hid_exit(void) { + hid_debug_exit(); hidraw_exit(); bus_unregister(&hid_bus_type); } diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c index 9d6d3b91773..62e9cb10e88 100644 --- a/drivers/hid/hid-cypress.c +++ b/drivers/hid/hid-cypress.c @@ -141,12 +141,12 @@ static struct hid_driver cp_driver = { .probe = cp_probe, }; -static int cp_init(void) +static int __init cp_init(void) { return hid_register_driver(&cp_driver); } -static void cp_exit(void) +static void __exit cp_exit(void) { hid_unregister_driver(&cp_driver); } diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 04359ed64b8..6abd0369aed 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -1,9 +1,9 @@ /* * (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de> * (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,17 @@ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/sched.h> +#include <linux/uaccess.h> +#include <linux/poll.h> + #include <linux/hid.h> #include <linux/hid-debug.h> +static struct dentry *hid_debug_root; + struct hid_usage_entry { unsigned page; unsigned usage; @@ -339,72 +347,120 @@ static const struct hid_usage_entry hid_usage_table[] = { { 0, 0, NULL } }; -static void resolv_usage_page(unsigned page) { +/* Either output directly into simple seq_file, or (if f == NULL) + * allocate a separate buffer that will then be passed to the 'events' + * ringbuffer. + * + * This is because these functions can be called both for "one-shot" + * "rdesc" while resolving, or for blocking "events". + * + * This holds both for resolv_usage_page() and hid_resolv_usage(). + */ +static char *resolv_usage_page(unsigned page, struct seq_file *f) { const struct hid_usage_entry *p; + char *buf = NULL; + + if (!f) { + buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); + if (!buf) + return ERR_PTR(-ENOMEM); + } for (p = hid_usage_table; p->description; p++) if (p->page == page) { - printk("%s", p->description); - return; + if (!f) { + snprintf(buf, HID_DEBUG_BUFSIZE, "%s", + p->description); + return buf; + } + else { + seq_printf(f, "%s", p->description); + return NULL; + } } - printk("%04x", page); + if (!f) + snprintf(buf, HID_DEBUG_BUFSIZE, "%04x", page); + else + seq_printf(f, "%04x", page); + return buf; } -void hid_resolv_usage(unsigned usage) { +char *hid_resolv_usage(unsigned usage, struct seq_file *f) { const struct hid_usage_entry *p; + char *buf = NULL; + int len = 0; + + buf = resolv_usage_page(usage >> 16, f); + if (IS_ERR(buf)) { + printk(KERN_ERR "error allocating HID debug buffer\n"); + return NULL; + } - if (!hid_debug) - return; - resolv_usage_page(usage >> 16); - printk("."); + if (!f) { + len = strlen(buf); + snprintf(buf+len, max(0, HID_DEBUG_BUFSIZE - len), "."); + len++; + } + else { + seq_printf(f, "."); + } for (p = hid_usage_table; p->description; p++) if (p->page == (usage >> 16)) { for(++p; p->description && p->usage != 0; p++) if (p->usage == (usage & 0xffff)) { - printk("%s", p->description); - return; + if (!f) + snprintf(buf + len, + max(0,HID_DEBUG_BUFSIZE - len - 1), + "%s", p->description); + else + seq_printf(f, + "%s", + p->description); + return buf; } break; } - printk("%04x", usage & 0xffff); + if (!f) + snprintf(buf + len, max(0, HID_DEBUG_BUFSIZE - len - 1), + "%04x", usage & 0xffff); + else + seq_printf(f, "%04x", usage & 0xffff); + return buf; } EXPORT_SYMBOL_GPL(hid_resolv_usage); -static void tab(int n) { - printk(KERN_DEBUG "%*s", n, ""); +static void tab(int n, struct seq_file *f) { + 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; - if (!hid_debug) - return; - if (field->physical) { - tab(n); - printk("Physical("); - hid_resolv_usage(field->physical); printk(")\n"); + tab(n, f); + seq_printf(f, "Physical("); + hid_resolv_usage(field->physical, f); seq_printf(f, ")\n"); } if (field->logical) { - tab(n); - printk("Logical("); - hid_resolv_usage(field->logical); printk(")\n"); + tab(n, f); + seq_printf(f, "Logical("); + 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++) { - 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) { - tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum); - tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum); + tab(n, f); seq_printf(f, "Logical Minimum(%d)\n", field->logical_minimum); + tab(n, f); seq_printf(f, "Logical Maximum(%d)\n", field->logical_maximum); } if (field->physical_minimum != field->physical_maximum) { - tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum); - tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum); + tab(n, f); seq_printf(f, "Physical Minimum(%d)\n", field->physical_minimum); + tab(n, f); seq_printf(f, "Physical Maximum(%d)\n", field->physical_maximum); } 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) { 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) { data >>= 4; if(sys > 4) { - tab(n); printk("Unit(Invalid)\n"); + tab(n, f); seq_printf(f, "Unit(Invalid)\n"); } else { 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++) { char nibble = data & 0xf; data >>= 4; if (nibble != 0) { if(earlier_unit++ > 0) - printk("*"); - printk("%s", units[sys][i]); + seq_printf(f, "*"); + seq_printf(f, "%s", units[sys][i]); if(nibble != 1) { /* This is a _signed_ nibble(!) */ int val = nibble & 0x7; if(nibble & 0x08) 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); printk("Report Count(%u)\n", field->report_count); - tab(n); printk("Report Offset(%u)\n", field->report_offset); + tab(n, f); seq_printf(f, "Report Size(%u)\n", field->report_size); + tab(n, f); seq_printf(f, "Report Count(%u)\n", field->report_count); + 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; - printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); - printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); - printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); - printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); - printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); - printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : ""); - printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); - printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); - printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); - printk(")\n"); + seq_printf(f, "%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); + seq_printf(f, "%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); + seq_printf(f, "%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); + seq_printf(f, "%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); + seq_printf(f, "%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); + seq_printf(f, "%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : ""); + seq_printf(f, "%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); + seq_printf(f, "%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); + seq_printf(f, "%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); + seq_printf(f, ")\n"); } 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 *report; struct list_head *list; unsigned i,k; static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; - if (!hid_debug) - return; - for (i = 0; i < HID_REPORT_TYPES; i++) { report_enum = device->report_enum + i; list = report_enum->report_list.next; while (list != &report_enum->report_list) { report = (struct hid_report *) list; - tab(2); - printk("%s", table[i]); + tab(2, f); + seq_printf(f, "%s", table[i]); if (report->id) - printk("(%d)", report->id); - printk("[%s]", table[report->type]); - printk("\n"); + seq_printf(f, "(%d)", report->id); + seq_printf(f, "[%s]", table[report->type]); + seq_printf(f, "\n"); for (k = 0; k < report->maxfield; k++) { - tab(4); - printk("Field(%d)\n", k); - hid_dump_field(report->field[k], 6); + tab(4, f); + seq_printf(f, "Field(%d)\n", k); + hid_dump_field(report->field[k], 6, f); } list = list->next; } @@ -503,13 +557,37 @@ void hid_dump_device(struct hid_device *device) { } EXPORT_SYMBOL_GPL(hid_dump_device); -void hid_dump_input(struct hid_usage *usage, __s32 value) { - if (hid_debug < 2) +/* enqueue string to 'events' ring buffer */ +void hid_debug_event(struct hid_device *hdev, char *buf) +{ + int i; + struct hid_debug_list *list; + + list_for_each_entry(list, &hdev->debug_list, node) { + for (i = 0; i <= strlen(buf); i++) + list->hid_debug_buf[(list->tail + i) % (HID_DEBUG_BUFSIZE - 1)] = + buf[i]; + list->tail = (list->tail + i) % (HID_DEBUG_BUFSIZE - 1); + } +} +EXPORT_SYMBOL_GPL(hid_debug_event); + +void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 value) +{ + char *buf; + int len; + + buf = hid_resolv_usage(usage->hid, NULL); + if (!buf) return; + len = strlen(buf); + snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", value); + + hid_debug_event(hdev, buf); + + kfree(buf); + wake_up_interruptible(&hdev->debug_wait); - printk(KERN_DEBUG "hid-debug: input "); - hid_resolv_usage(usage->hid); - printk(" = %d\n", value); } EXPORT_SYMBOL_GPL(hid_dump_input); @@ -786,12 +864,221 @@ static const char **names[EV_MAX + 1] = { [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) - return; - - printk("%s.%s", events[type] ? events[type] : "?", + seq_printf(f, "%s.%s", events[type] ? events[type] : "?", 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; + |