From a635f9dd83f3382577f4544a96df12356e951a40 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 12 Jun 2009 15:20:55 +0200 Subject: [PATCH 01/19] 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//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 --- drivers/hid/Kconfig | 15 --- drivers/hid/Makefile | 5 +- drivers/hid/hid-core.c | 13 +- drivers/hid/hid-debug.c | 227 +++++++++++++++++++++++----------- drivers/hid/hid-input.c | 13 +- drivers/hid/usbhid/hid-core.c | 8 +- include/linux/hid-debug.h | 34 ++--- include/linux/hid.h | 4 + 8 files changed, 196 insertions(+), 123 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 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 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 @@ # 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 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 @@ #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)"); EXPORT_SYMBOL_GPL(hid_debug); -#endif /* * 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) { 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) { @@ -1721,6 +1718,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); @@ -1768,6 +1767,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; } } @@ -1843,6 +1843,10 @@ static int __init hid_init(void) { 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); if (ret) { printk(KERN_ERR "HID: can't register hid bus\n"); @@ -1853,6 +1857,8 @@ static int __init hid_init(void) if (ret) goto err_bus; + hid_debug_init(); + return 0; err_bus: bus_unregister(&hid_bus_type); @@ -1862,6 +1868,7 @@ err: static void __exit hid_exit(void) { + hid_debug_exit(); hidraw_exit(); bus_unregister(&hid_bus_type); } 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 @@ /* * (c) 1999 Andreas Gal * (c) 2000-2001 Vojtech Pavlik - * (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 */ +#include +#include #include #include +static struct dentry *hid_debug_root; + struct hid_usage_entry { unsigned page; unsigned usage; @@ -331,72 +335,83 @@ static const struct hid_usage_entry hid_usage_table[] = { { 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; for (p = hid_usage_table; p->description; p++) if (p->page == page) { - printk("%s", p->description); + if (!f) + printk("%s", p->description); + else + seq_printf(f, "%s", p->description); 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; - if (!hid_debug) - return; - - resolv_usage_page(usage >> 16); - printk("."); + resolv_usage_page(usage >> 16, f); + if (!f) + printk("."); + 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); + if (!f) + printk("%s", p->description); + else + seq_printf(f, + "%s", + p->description); return; } break; } - printk("%04x", usage & 0xffff); + if (!f) + printk("%04x", usage & 0xffff); + else + seq_printf(f, "%04x", usage & 0xffff); } 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" }; @@ -417,77 +432,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>= 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; } @@ -500,7 +513,7 @@ void hid_dump_input(struct hid_usage *usage, __s32 value) { return; printk(KERN_DEBUG "hid-debug: input "); - hid_resolv_usage(usage->hid); + hid_resolv_usage(usage->hid, NULL); printk(" = %d\n", value); } EXPORT_SYMBOL_GPL(hid_dump_input); @@ -767,12 +780,84 @@ 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; + + 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); +} + 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 field->hidinput = hidinput; - dbg_hid("Mapping: "); - hid_resolv_usage(usage->hid); - dbg_hid_line(" ---> "); - if (field->flags & HID_MAIN_ITEM_CONSTANT) goto ignore; /* only LED usages are supported in output fields */ if (field->report_type == HID_OUTPUT_REPORT && (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { - dbg_hid_line(" [non-LED output field] "); goto ignore; } @@ -561,15 +556,9 @@ mapped: set_bit(MSC_SCAN, input->mscbit); } - hid_resolv_event(usage->type, usage->code); - - dbg_hid_line("\n"); - - return; - ignore: - dbg_hid_line("IGNORED\n"); return; + } 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 @@ * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik * Copyright (c) 2005 Michael Haboustak for Concept2, Inc - * Copyright (c) 2006-2008 Jiri Kosina * 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; } - 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); kfree(rdesc); 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_init_reports(hid); - hid_dump_device(hid); set_bit(HID_STARTED, &usbhid->iofl); 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 @@ #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_device(struct hid_device *); -void hid_dump_field(struct hid_field *, int); -void hid_resolv_usage(unsigned); -void hid_resolv_event(__u8, __u16); +void hid_dump_device(struct hid_device *, struct seq_file *); +void hid_dump_field(struct hid_field *, int, struct seq_file *); +void hid_resolv_usage(unsigned, struct seq_file *); +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 -#define hid_dump_input(a,b) do { } while (0) -#define hid_dump_device(c) do { } while (0) -#define hid_dump_field(a,b) do { } while (0) -#define hid_resolv_usage(a) do { } while (0) -#define hid_resolv_event(a,b) do { } while (0) - -#endif /* CONFIG_HID_DEBUG */ - +#define hid_dump_input(a,b) do { } while (0) +#define hid_dump_device(c) do { } while (0) +#define hid_dump_field(a,b) do { } while (0) +#define hid_resolv_usage(a) do { } while (0) +#define hid_resolv_event(a,b) do { } while (0) +#define hid_debug_register(a, b) do { } while (0) +#define hid_debug_unregister(a) do { } while (0) +#define hid_debug_init() do { } while (0) +#define hid_debug_exit() do { } while (0) + +#endif #endif 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 */ char phys[64]; /* Device physical location */ char uniq[64]; /* Device unique identifier (serial #) */ + /* debugfs */ + struct dentry *debug_dir; + struct dentry *debug_rdesc; + void *driver_data; /* temporary hid_ff handling (until moved to the drivers) */ From cd667ce24796700e1a0e6e7528efc61c96ff832e Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 12 Jun 2009 15:20:57 +0200 Subject: [PATCH 02/19] HID: use debugfs for events/reports dumping This is a followup patch to the one implemeting rdesc representation in debugfs rather than being dependent on compile-time CONFIG_HID_DEBUG setting. The API of the appropriate formatting functions is slightly modified -- if they are passed seq_file pointer, the one-shot output for 'rdesc' file mode is used, and therefore the message is formatted into the corresponding seq_file immediately. Otherwise the called function allocated a new buffer, formats the text into the buffer and returns the pointer to it, so that it can be queued into the ring-buffer of the processess blocked waiting on input on 'events' file in debugfs. 'debug' parameter to the 'hid' module is now used solely for the prupose of inetrnal driver state debugging (parser, transport, etc). Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 42 +++++-- drivers/hid/hid-debug.c | 241 ++++++++++++++++++++++++++++++++++---- include/linux/hid-debug.h | 18 ++- include/linux/hid.h | 26 ++-- 4 files changed, 277 insertions(+), 50 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index d4317db85b54..449bd747d116 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -46,7 +46,7 @@ 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); /* @@ -859,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); @@ -981,7 +981,7 @@ 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); @@ -1075,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 = hid->report_enum + type; struct hid_driver *hdrv = hid->driver; struct hid_report *report; + char *buf; unsigned int i; int ret; @@ -1086,18 +1087,36 @@ 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) 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) @@ -1756,6 +1775,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); @@ -1844,8 +1866,8 @@ static int __init hid_init(void) int ret; if (hid_debug) - printk(KERN_WARNING "HID: hid_debug parameter has been deprecated. " - "Debugging data are now provided via debugfs\n"); + 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) { diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 067e173aa3e4..a331a1821e85 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -28,6 +28,10 @@ #include #include +#include +#include +#include + #include #include @@ -335,49 +339,86 @@ static const struct hid_usage_entry hid_usage_table[] = { { 0, 0, NULL } }; -static void resolv_usage_page(unsigned page, struct seq_file *f) { +/* 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) { - if (!f) - printk("%s", p->description); - else + if (!f) { + snprintf(buf, HID_DEBUG_BUFSIZE, "%s", + p->description); + return buf; + } + else { seq_printf(f, "%s", p->description); - return; + return NULL; + } } if (!f) - printk("%04x", page); + snprintf(buf, HID_DEBUG_BUFSIZE, "%04x", page); else seq_printf(f, "%04x", page); + return buf; } -void hid_resolv_usage(unsigned usage, struct seq_file *f) { +char *hid_resolv_usage(unsigned usage, struct seq_file *f) { const struct hid_usage_entry *p; + char *buf = NULL; + int len = 0; - resolv_usage_page(usage >> 16, f); - if (!f) - printk("."); - else + buf = resolv_usage_page(usage >> 16, f); + if (IS_ERR(buf)) { + printk(KERN_ERR "error allocating HID debug buffer\n"); + return NULL; + } + + + 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)) { if (!f) - printk("%s", p->description); + snprintf(buf + len, + max(0,HID_DEBUG_BUFSIZE - len - 1), + "%s", p->description); else seq_printf(f, "%s", p->description); - return; + return buf; } break; } if (!f) - printk("%04x", usage & 0xffff); + 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); @@ -508,13 +549,37 @@ void hid_dump_device(struct hid_device *device, struct seq_file *f) } EXPORT_SYMBOL_GPL(hid_dump_device); -void hid_dump_input(struct hid_usage *usage, __s32 value) { - if (hid_debug < 2) - return; +/* 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, NULL); - printk(" = %d\n", value); } EXPORT_SYMBOL_GPL(hid_dump_input); @@ -808,6 +873,7 @@ void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f) } + static int hid_debug_rdesc_show(struct seq_file *f, void *p) { struct hid_device *hdev = f->private; @@ -831,6 +897,126 @@ static int hid_debug_rdesc_open(struct inode *inode, struct file *file) return single_open(file, hid_debug_rdesc_show, inode->i_private); } +static int hid_debug_events_open(struct inode *inode, struct file *file) +{ + int err = 0; + struct hid_debug_list *list; + + if (!(list = kzalloc(sizeof(struct hid_debug_list), GFP_KERNEL))) { + err = -ENOMEM; + goto out; + } + + if (!(list->hid_debug_buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_KERNEL))) { + err = -ENOMEM; + goto out; + } + list->hdev = (struct hid_device *) inode->i_private; + file->private_data = list; + mutex_init(&list->read_mutex); + + list_add_tail(&list->node, &list->hdev->debug_list); + +out: + return err; +} + +static ssize_t hid_debug_events_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct hid_debug_list *list = file->private_data; + int ret = 0, len; + DECLARE_WAITQUEUE(wait, current); + + while (ret == 0) { + mutex_lock(&list->read_mutex); + if (list->head == list->tail) { + add_wait_queue(&list->hdev->debug_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + while (list->head == list->tail) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + if (!list->hdev || !list->hdev->debug) { + ret = -EIO; + break; + } + + /* allow O_NONBLOCK from other threads */ + mutex_unlock(&list->read_mutex); + schedule(); + mutex_lock(&list->read_mutex); + set_current_state(TASK_INTERRUPTIBLE); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&list->hdev->debug_wait, &wait); + } + + if (ret) + goto out; + + /* pass the ringbuffer contents to userspace */ +copy_rest: + if (list->tail == list->head) + goto out; + if (list->tail > list->head) { + len = list->tail - list->head; + + if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) { + ret = -EFAULT; + goto out; + } + ret += len; + list->head += len; + } else { + len = HID_DEBUG_BUFSIZE - list->head; + + if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) { + ret = -EFAULT; + goto out; + } + list->head = 0; + ret += len; + goto copy_rest; + } + + } +out: + mutex_unlock(&list->read_mutex); + return ret; +} + +static unsigned int hid_debug_events_poll(struct file *file, poll_table *wait) +{ + struct hid_debug_list *list = file->private_data; + + poll_wait(file, &list->hdev->debug_wait, wait); + if (list->head != list->tail) + return POLLIN | POLLRDNORM; + if (!list->hdev->debug) + return POLLERR | POLLHUP; + return 0; +} + +static int hid_debug_events_release(struct inode *inode, struct file *file) +{ + struct hid_debug_list *list = file->private_data; + + list_del(&list->node); + kfree(list->hid_debug_buf); + kfree(list); + + return 0; +} + static const struct file_operations hid_debug_rdesc_fops = { .open = hid_debug_rdesc_open, .read = seq_read, @@ -838,16 +1024,31 @@ static const struct file_operations hid_debug_rdesc_fops = { .release = single_release, }; +static const struct file_operations hid_debug_events_fops = { + .owner = THIS_MODULE, + .open = hid_debug_events_open, + .read = hid_debug_events_read, + .poll = hid_debug_events_poll, + .release = hid_debug_events_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); + hdev->debug_events = debugfs_create_file("events", 0400, + hdev->debug_dir, hdev, &hid_debug_events_fops); + hdev->debug = 1; } void hid_debug_unregister(struct hid_device *hdev) { + hdev->debug = 0; + wake_up_interruptible(&hdev->debug_wait); debugfs_remove(hdev->debug_rdesc); + debugfs_remove(hdev->debug_events); debugfs_remove(hdev->debug_dir); } diff --git a/include/linux/hid-debug.h b/include/linux/hid-debug.h index 516e12c33235..ec08ac1ad687 100644 --- a/include/linux/hid-debug.h +++ b/include/linux/hid-debug.h @@ -24,14 +24,27 @@ #ifdef CONFIG_DEBUG_FS -void hid_dump_input(struct hid_usage *, __s32); +void hid_dump_input(struct hid_device *, struct hid_usage *, __s32); void hid_dump_device(struct hid_device *, struct seq_file *); void hid_dump_field(struct hid_field *, int, struct seq_file *); -void hid_resolv_usage(unsigned, struct seq_file *); +char *hid_resolv_usage(unsigned, struct seq_file *); 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); +void hid_debug_event(struct hid_device *, char *); + +#define HID_DEBUG_BUFSIZE 512 + +struct hid_debug_list { + char *hid_debug_buf; + int head; + int tail; + struct fasync_struct *fasync; + struct hid_device *hdev; + struct list_head node; + struct mutex read_mutex; +}; #else @@ -44,6 +57,7 @@ void hid_debug_exit(void); #define hid_debug_unregister(a) do { } while (0) #define hid_debug_init() do { } while (0) #define hid_debug_exit() do { } while (0) +#define hid_debug_event(a,b) do { } while (0) #endif diff --git a/include/linux/hid.h b/include/linux/hid.h index da09ab140ef1..60fa52913f89 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -451,10 +451,6 @@ struct hid_device { /* device report descriptor */ char phys[64]; /* Device physical location */ char uniq[64]; /* Device unique identifier (serial #) */ - /* debugfs */ - struct dentry *debug_dir; - struct dentry *debug_rdesc; - void *driver_data; /* temporary hid_ff handling (until moved to the drivers) */ @@ -468,6 +464,14 @@ struct hid_device { /* device report descriptor */ /* handler for raw output data, used by hidraw */ int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t); + + /* debugging support via debugfs */ + unsigned short debug; + struct dentry *debug_dir; + struct dentry *debug_rdesc; + struct dentry *debug_events; + struct list_head debug_list; + wait_queue_head_t debug_wait; }; static inline void *hid_get_drvdata(struct hid_device *hdev) @@ -625,9 +629,7 @@ struct hid_ll_driver { /* HID core API */ -#ifdef CONFIG_HID_DEBUG extern int hid_debug; -#endif extern int hid_add_device(struct hid_device *); extern void hid_destroy_device(struct hid_device *); @@ -783,21 +785,9 @@ int hid_pidff_init(struct hid_device *hid); #define hid_pidff_init NULL #endif -#ifdef CONFIG_HID_DEBUG #define dbg_hid(format, arg...) if (hid_debug) \ printk(KERN_DEBUG "%s: " format ,\ __FILE__ , ## arg) -#define dbg_hid_line(format, arg...) if (hid_debug) \ - printk(format, ## arg) -#else -static inline int __attribute__((format(printf, 1, 2))) -dbg_hid(const char *fmt, ...) -{ - return 0; -} -#define dbg_hid_line dbg_hid -#endif /* HID_DEBUG */ - #define err_hid(format, arg...) printk(KERN_ERR "%s: " format "\n" , \ __FILE__ , ## arg) #endif /* HID_FF */ From 38b7f49a0654cb52cac61c6455807248eee3059d Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 26 Jun 2009 10:48:34 +0200 Subject: [PATCH 03/19] HID: fix debugfs build with !CONFIG_DEBUG_FS Fix the debug function prototypes to be correct even in the !CONFIG_DEBUG_FS case. Reported-by: Stephen Rothwell Signed-off-by: Jiri Kosina --- include/linux/hid-debug.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/hid-debug.h b/include/linux/hid-debug.h index ec08ac1ad687..53744fa1c8b7 100644 --- a/include/linux/hid-debug.h +++ b/include/linux/hid-debug.h @@ -22,6 +22,8 @@ * */ +#define HID_DEBUG_BUFSIZE 512 + #ifdef CONFIG_DEBUG_FS void hid_dump_input(struct hid_device *, struct hid_usage *, __s32); @@ -34,7 +36,6 @@ void hid_debug_init(void); void hid_debug_exit(void); void hid_debug_event(struct hid_device *, char *); -#define HID_DEBUG_BUFSIZE 512 struct hid_debug_list { char *hid_debug_buf; @@ -48,11 +49,10 @@ struct hid_debug_list { #else -#define hid_dump_input(a,b) do { } while (0) -#define hid_dump_device(c) do { } while (0) -#define hid_dump_field(a,b) do { } while (0) -#define hid_resolv_usage(a) do { } while (0) -#define hid_resolv_event(a,b) do { } while (0) +#define hid_dump_input(a,b,c) do { } while (0) +#define hid_dump_device(a,b) do { } while (0) +#define hid_dump_field(a,b,c) do { } while (0) +#define hid_resolv_usage(a,b) do { } while (0) #define hid_debug_register(a, b) do { } while (0) #define hid_debug_unregister(a) do { } while (0) #define hid_debug_init() do { } while (0) From 55dba52458a11126ff4445b5b94ebde03afcf47a Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 26 Jun 2009 10:50:12 +0200 Subject: [PATCH 04/19] HID: fix memory leak on error path in debug code If hid_get_report() fails, we forgot to free the already allocated buffer for debugging messages on error path. Fix that up. Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 449bd747d116..86c2ff2429d8 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1100,8 +1100,10 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i hid_debug_event(hid, buf); report = hid_get_report(report_enum, data); - if (!report) + if (!report) { + kfree(buf); return -1; + } /* dump the report */ snprintf(buf, HID_DEBUG_BUFSIZE - 1, From 0810b51170570ed21cae56b8131db6a5e3efa584 Mon Sep 17 00:00:00 2001 From: Robert Schedel Date: Tue, 23 Jun 2009 11:26:48 +0200 Subject: [PATCH 05/19] HID: Support new variants of Samsung USB IR receiver (0419:0001) This patch extends the existing Samsung IrDA (0419:0001) quirk file with newly reported variants: * New device variants with 203 byte and 135 byte report descriptors were reported to be recognized incorrectly. This patch adds an autodetection for those two, using report descriptor size to enable new quirks. * Any other unknown 0419:0001 variants will now be treated without any quirk flags (i.e. IGNORE_HIDINPUT/HIDDEV_FORCE will not be set by default anymore). More details: 1. Descriptor size 184 bytes ("Satelco bundled remote") Already supported since kernel 2.6.25 (my old patch). 2. Descriptor size 203 bytes ("Optronix remote") This receiver mostly works with the regular HID input driver. Only when some keys are released, another spurious key press event is interpreted due to incorrect array ranges. According to HID 1.11, section 6.2.2.5, arrays should return a 0 value when no control is asserted, and ranges should go from 1 to the number of elements. The patch clips the value with a logical range from 1..15 (instead of originally 0..18). Ticket with more information available at https://bugs.launchpad.net/bugs/326986 3. Descriptor size 135 bytes ("Gotview remote") This receiver has a similar issue than the previous one, i.e. it mostly works with regular HID input, except some key press events get stuck on key release. The patch clips the array value from 1..14 (instead of originally 0..17). Ticket with more information available at http://bugs.archlinux.org/task/15216 4. Other unknown variants (found one report with 218 bytes, but no further information about issues) For such unknown variants we should refrain from changing any device flags. Currently, HIDINPUT is suppressed and HIDDEV is enforced (because in 2.6.25 the quirk table did not yet allow differentiating variants and we did not expect variants either). Now we should be as strict as possible and enable it only for the first variant above. Signed-off-by: Robert Schedel Signed-off-by: Jiri Kosina --- drivers/hid/hid-samsung.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c index 07083aa6c19a..ba91d9485ad0 100644 --- a/drivers/hid/hid-samsung.c +++ b/drivers/hid/hid-samsung.c @@ -25,25 +25,48 @@ /* * Samsung IrDA remote controller (reports as Cypress USB Mouse). * + * There are several variants for 0419:0001: + * + * 1. 184 byte report descriptor * Vendor specific report #4 has a size of 48 bit, * and therefore is not accepted when inspecting the descriptors. * As a workaround we reinterpret the report as: * Variable type, count 6, size 8 bit, log. maximum 255 * The burden to reconstruct the data is moved into user space. + * + * 2. 203 byte report descriptor + * Report #4 has an array field with logical range 0..18 instead of 1..15. + * + * 3. 135 byte report descriptor + * Report #4 has an array field with logical range 0..17 instead of 1..14. */ static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int rsize) { - if (rsize >= 182 && rdesc[175] == 0x25 && rdesc[176] == 0x40 && + if (rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 && rdesc[177] == 0x75 && rdesc[178] == 0x30 && rdesc[179] == 0x95 && rdesc[180] == 0x01 && rdesc[182] == 0x40) { - dev_info(&hdev->dev, "fixing up Samsung IrDA report " - "descriptor\n"); + dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report " + "descriptor\n", 184); rdesc[176] = 0xff; rdesc[178] = 0x08; rdesc[180] = 0x06; rdesc[182] = 0x42; + } else + if (rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 && + rdesc[194] == 0x25 && rdesc[195] == 0x12) { + dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report " + "descriptor\n", 203); + rdesc[193] = 0x1; + rdesc[195] = 0xf; + } else + if (rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 && + rdesc[126] == 0x25 && rdesc[127] == 0x11) { + dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report " + "descriptor\n", 135); + rdesc[125] = 0x1; + rdesc[127] = 0xe; } } @@ -51,6 +74,7 @@ static int samsung_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; + unsigned int cmask = HID_CONNECT_DEFAULT; ret = hid_parse(hdev); if (ret) { @@ -58,8 +82,13 @@ static int samsung_probe(struct hid_device *hdev, goto err_free; } - ret = hid_hw_start(hdev, (HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDINPUT) | - HID_CONNECT_HIDDEV_FORCE); + if (hdev->rsize == 184) { + /* disable hidinput, force hiddev */ + cmask = (cmask & ~HID_CONNECT_HIDINPUT) | + HID_CONNECT_HIDDEV_FORCE; + } + + ret = hid_hw_start(hdev, cmask); if (ret) { dev_err(&hdev->dev, "hw start failed\n"); goto err_free; From fd30ea8c871552ddd6a5e1c0886de8fef4df53bc Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 23 Jun 2009 12:11:31 +0200 Subject: [PATCH 06/19] HID: add force feedback support for Logitech WingMan Formula Force GP Add force feedback support for Logitech WingMan Formula Force GP (0x046d/0xc293). Reported-by: wylda@volny.cz Tested-by: wylda@volny.cz Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-lg.c | 2 ++ drivers/hid/hid-lgff.c | 1 + 5 files changed, 6 insertions(+) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 7831a0318d3c..d3910c64bf01 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -176,6 +176,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. diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5eb10c2ce665..d51091df5c1f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1292,6 +1292,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) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 630101037921..398f731fd699 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -296,6 +296,7 @@ #define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 #define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 +#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 #define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 #define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 7afbaa0efd18..b30af30a0b17 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -299,6 +299,8 @@ static const struct hid_device_id lg_devices[] = { .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ), + .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), .driver_data = LG_FF2 }, { } diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c index 56099709581c..95835ea5690f 100644 --- a/drivers/hid/hid-lgff.c +++ b/drivers/hid/hid-lgff.c @@ -67,6 +67,7 @@ static const struct dev_type devices[] = { { 0x046d, 0xc219, ff_rumble }, { 0x046d, 0xc283, ff_joystick }, { 0x046d, 0xc286, ff_joystick_ac }, + { 0x046d, 0xc293, ff_joystick }, { 0x046d, 0xc294, ff_wheel }, { 0x046d, 0xc295, ff_joystick }, { 0x046d, 0xca03, ff_wheel }, From 8b424887b603e953af7238fef96629424dc915cb Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Tue, 23 Jun 2009 15:32:19 +0200 Subject: [PATCH 07/19] HID: Avoid double spin_lock_init on usbhid->lock Avoid double spin_lock_init on usbhid->lock. Signed-off-by: Sergey Senozhatsky Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 3c1fcb7640ab..9238bcffaf01 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -985,7 +985,6 @@ static int usbhid_start(struct hid_device *hid) INIT_WORK(&usbhid->restart_work, __usbhid_restart_queues); setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); - spin_lock_init(&usbhid->lock); spin_lock_init(&usbhid->lock); usbhid->intf = intf; From 837b47533a6476ab3fb96b1f52edd3ce9f3162b8 Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Tue, 23 Jun 2009 14:09:26 -0400 Subject: [PATCH 08/19] HID: ntrig tool separation and pen usages When both touch and pen are active send a tool announcement before sending any status changes so that event users may differentiate which tool is changing. Restored three usage codes used by the pen. Signed-off-by: Rafi Rubin Acked-by: Stephane Chatty Signed-off-by: Jiri Kosina --- drivers/hid/hid-ntrig.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 75ed9d2c1a36..d7b3e61fbf8f 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -27,6 +27,9 @@ struct ntrig_data { __s32 x, y, id, w, h; char reading_a_point, found_contact_id; + char pen_active; + char finger_active; + char inverted; }; /* @@ -63,10 +66,7 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_UP_DIGITIZER: switch (usage->hid) { /* we do not want to map these for now */ - case HID_DG_INVERT: /* value is always 0 */ - case HID_DG_ERASER: /* value is always 0 */ case HID_DG_CONTACTID: /* value is useless */ - case HID_DG_BARRELSWITCH: /* doubtful */ case HID_DG_INPUTMODE: case HID_DG_DEVICEINDEX: case HID_DG_CONTACTCOUNT: @@ -125,6 +125,18 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, if (hid->claimed & HID_CLAIMED_INPUT) { switch (usage->hid) { + + case HID_DG_INRANGE: + if (field->application & 0x3) + nd->pen_active = (value != 0); + else + nd->finger_active = (value != 0); + return 0; + + case HID_DG_INVERT: + nd->inverted = value; + return 0; + case HID_GD_X: nd->x = value; nd->reading_a_point = 1; @@ -147,7 +159,11 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, * report received in a finger event. We want * to emit a normal (X, Y) position */ - if (! nd->found_contact_id) { + if (!nd->found_contact_id) { + if (nd->pen_active && nd->finger_active) { + input_report_key(input, BTN_TOOL_DOUBLETAP, 0); + input_report_key(input, BTN_TOOL_DOUBLETAP, 1); + } input_event(input, EV_ABS, ABS_X, nd->x); input_event(input, EV_ABS, ABS_Y, nd->y); } @@ -159,6 +175,14 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, * to emit a normal (X, Y) position */ if (! nd->found_contact_id) { + if (nd->pen_active && nd->finger_active) { + input_report_key(input, + nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN + , 0); + input_report_key(input, + nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN + , 1); + } input_event(input, EV_ABS, ABS_X, nd->x); input_event(input, EV_ABS, ABS_Y, nd->y); input_event(input, EV_ABS, ABS_PRESSURE, value); @@ -233,6 +257,7 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) if (ret) kfree (nd); + return ret; } From 7a84b1336a145d683fb8cdfd6c2c67545a58b126 Mon Sep 17 00:00:00 2001 From: Ruben Aos Garralda Date: Mon, 29 Jun 2009 09:41:29 +0200 Subject: [PATCH 09/19] HID: add rumble support for Thrustmaster Dual Trigger 3-in-1 This patch enables rumble in Thrustmaster Dual 3-in-1 trigger gamepads (in both PC and PS3 modes). It uses the same code as Thrustmaster FireStorm Dual Power 2, so it only adds new USB IDs to hid-core.c and hid-tmff.c Signed-off-by: Ruben Aos Garralda Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 6 +++--- drivers/hid/hid-core.c | 2 ++ drivers/hid/hid-tmff.c | 6 +++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index d3910c64bf01..a66ffe747d9c 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -315,9 +315,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/hid-core.c b/drivers/hid/hid-core.c index d51091df5c1f..5601a1432e00 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1312,6 +1312,8 @@ 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) }, diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c index fcd6ccd02fee..6589a7c790e8 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -243,7 +243,11 @@ err: static const struct hid_device_id tm_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300), .driver_data = (unsigned long)ff_rumble }, - { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304), + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304), /* FireStorm Dual Power 2 (and 3) */ + .driver_data = (unsigned long)ff_rumble }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323), /* Dual Trigger 3-in-1 (PC Mode) */ + .driver_data = (unsigned long)ff_rumble }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324), /* Dual Trigger 3-in-1 (PS3 Mode) */ .driver_data = (unsigned long)ff_rumble }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651), /* FGT Rumble Force Wheel */ .driver_data = (unsigned long)ff_rumble }, From a24f423bdf253ccee369adc6c5451b40a0716fbb Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Thu, 2 Jul 2009 19:08:38 +0200 Subject: [PATCH 10/19] HID: adding __init/__exit macros to module init/exit functions Trivial patch which adds the __init and __exit macros to the module_init / module_exit functions of several HID drivers from drivers/hid/ Signed-off-by: Peter Huewe Signed-off-by: Jiri Kosina --- drivers/hid/hid-a4tech.c | 4 ++-- drivers/hid/hid-apple.c | 4 ++-- drivers/hid/hid-belkin.c | 4 ++-- drivers/hid/hid-cherry.c | 4 ++-- drivers/hid/hid-chicony.c | 4 ++-- drivers/hid/hid-cypress.c | 4 ++-- drivers/hid/hid-ezkey.c | 4 ++-- drivers/hid/hid-gyration.c | 4 ++-- drivers/hid/hid-kensington.c | 4 ++-- drivers/hid/hid-kye.c | 4 ++-- drivers/hid/hid-lg.c | 4 ++-- drivers/hid/hid-microsoft.c | 4 ++-- drivers/hid/hid-monterey.c | 4 ++-- drivers/hid/hid-ntrig.c | 4 ++-- drivers/hid/hid-petalynx.c | 4 ++-- drivers/hid/hid-pl.c | 4 ++-- drivers/hid/hid-samsung.c | 4 ++-- drivers/hid/hid-sjoy.c | 4 ++-- drivers/hid/hid-sony.c | 4 ++-- drivers/hid/hid-sunplus.c | 4 ++-- drivers/hid/hid-tmff.c | 4 ++-- drivers/hid/hid-topseed.c | 4 ++-- drivers/hid/hid-wacom.c | 4 ++-- drivers/hid/hid-zpff.c | 4 ++-- 24 files changed, 48 insertions(+), 48 deletions(-) diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index 42ea359e94cf..df474c699fb8 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 303ccce05bb3..4b96e7a898cf 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 2f6723133a4b..4ce7aa3a519f 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 ab8209e7e45c..7e597d7f770f 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 7f91076d8493..8965ad93d510 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-cypress.c b/drivers/hid/hid-cypress.c index 9d6d3b91773b..62e9cb10e88c 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-ezkey.c b/drivers/hid/hid-ezkey.c index 0a1fe054799b..ca1163e9d42d 100644 --- a/drivers/hid/hid-ezkey.c +++ b/drivers/hid/hid-ezkey.c @@ -78,12 +78,12 @@ static struct hid_driver ez_driver = { .event = ez_event, }; -static int ez_init(void) +static int __init ez_init(void) { return hid_register_driver(&ez_driver); } -static void ez_exit(void) +static void __exit ez_exit(void) { hid_unregister_driver(&ez_driver); } diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c index d42d222097a8..cab13e8c7d29 100644 --- a/drivers/hid/hid-gyration.c +++ b/drivers/hid/hid-gyration.c @@ -81,12 +81,12 @@ static struct hid_driver gyration_driver = { .event = gyration_event, }; -static int gyration_init(void) +static int __init gyration_init(void) { return hid_register_driver(&gyration_driver); } -static void gyration_exit(void) +static void __exit gyration_exit(void) { hid_unregister_driver(&gyration_driver); } diff --git a/drivers/hid/hid-kensington.c b/drivers/hid/hid-kensington.c index 7353bd79cbe9..a5b4016e9bd7 100644 --- a/drivers/hid/hid-kensington.c +++ b/drivers/hid/hid-kensington.c @@ -48,12 +48,12 @@ static struct hid_driver ks_driver = { .input_mapping = ks_input_mapping, }; -static int ks_init(void) +static int __init ks_init(void) { return hid_register_driver(&ks_driver); } -static void ks_exit(void) +static void __exit ks_exit(void) { hid_unregister_driver(&ks_driver); } diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index 72ee3fec56d9..f8871712b7b5 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -54,12 +54,12 @@ static struct hid_driver kye_driver = { .report_fixup = kye_report_fixup, }; -static int kye_init(void) +static int __init kye_init(void) { return hid_register_driver(&kye_driver); } -static void kye_exit(void) +static void __exit kye_exit(void) { hid_unregister_driver(&kye_driver); } diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index b30af30a0b17..0f870a3243ed 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -317,12 +317,12 @@ static struct hid_driver lg_driver = { .probe = lg_probe, }; -static int lg_init(void) +static int __init lg_init(void) { return hid_register_driver(&lg_driver); } -static void lg_exit(void) +static void __exit lg_exit(void) { hid_unregister_driver(&lg_driver); } diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 5e9e37a0506d..359cc447c6c6 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -197,12 +197,12 @@ static struct hid_driver ms_driver = { .probe = ms_probe, }; -static int ms_init(void) +static int __init ms_init(void) { return hid_register_driver(&ms_driver); } -static void ms_exit(void) +static void __exit ms_exit(void) { hid_unregister_driver(&ms_driver); } diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c index 240f87618be6..2cd05aa244b9 100644 --- a/drivers/hid/hid-monterey.c +++ b/drivers/hid/hid-monterey.c @@ -65,12 +65,12 @@ static struct hid_driver mr_driver = { .input_mapping = mr_input_mapping, }; -static int mr_init(void) +static int __init mr_init(void) { return hid_register_driver(&mr_driver); } -static void mr_exit(void) +static void __exit mr_exit(void) { hid_unregister_driver(&mr_driver); } diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index d7b3e61fbf8f..49ce69d7bba7 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -290,12 +290,12 @@ static struct hid_driver ntrig_driver = { .event = ntrig_event, }; -static int ntrig_init(void) +static int __init ntrig_init(void) { return hid_register_driver(&ntrig_driver); } -static void ntrig_exit(void) +static void __exit ntrig_exit(void) { hid_unregister_driver(&ntrig_driver); } diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c index 2e83e8ff891a..500fbd0652dc 100644 --- a/drivers/hid/hid-petalynx.c +++ b/drivers/hid/hid-petalynx.c @@ -105,12 +105,12 @@ static struct hid_driver pl_driver = { .probe = pl_probe, }; -static int pl_init(void) +static int __init pl_init(void) { return hid_register_driver(&pl_driver); } -static void pl_exit(void) +static void __exit pl_exit(void) { hid_unregister_driver(&pl_driver); } diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index 4db9a3483760..c6d7dbc935b1 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -217,12 +217,12 @@ static struct hid_driver pl_driver = { .probe = pl_probe, }; -static int pl_init(void) +static int __init pl_init(void) { return hid_register_driver(&pl_driver); } -static void pl_exit(void) +static void __exit pl_exit(void) { hid_unregister_driver(&pl_driver); } diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c index ba91d9485ad0..5b222eed0692 100644 --- a/drivers/hid/hid-samsung.c +++ b/drivers/hid/hid-samsung.c @@ -112,12 +112,12 @@ static struct hid_driver samsung_driver = { .probe = samsung_probe, }; -static int samsung_init(void) +static int __init samsung_init(void) { return hid_register_driver(&samsung_driver); } -static void samsung_exit(void) +static void __exit samsung_exit(void) { hid_unregister_driver(&samsung_driver); } diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c index eab169e5c371..203c438b016f 100644 --- a/drivers/hid/hid-sjoy.c +++ b/drivers/hid/hid-sjoy.c @@ -163,12 +163,12 @@ static struct hid_driver sjoy_driver = { .probe = sjoy_probe, }; -static int sjoy_init(void) +static int __init sjoy_init(void) { return hid_register_driver(&sjoy_driver); } -static void sjoy_exit(void) +static void __exit sjoy_exit(void) { hid_unregister_driver(&sjoy_driver); } diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index c2599388a350..4e8450228a24 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -135,12 +135,12 @@ static struct hid_driver sony_driver = { .report_fixup = sony_report_fixup, }; -static int sony_init(void) +static int __init sony_init(void) { return hid_register_driver(&sony_driver); } -static void sony_exit(void) +static void __exit sony_exit(void) { hid_unregister_driver(&sony_driver); } diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c index e0a8fd36a85b..438107d9f1b2 100644 --- a/drivers/hid/hid-sunplus.c +++ b/drivers/hid/hid-sunplus.c @@ -65,12 +65,12 @@ static struct hid_driver sp_driver = { .input_mapping = sp_input_mapping, }; -static int sp_init(void) +static int __init sp_init(void) { return hid_register_driver(&sp_driver); } -static void sp_exit(void) +static void __exit sp_exit(void) { hid_unregister_driver(&sp_driver); } diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c index 6589a7c790e8..167ea746fb9c 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -263,12 +263,12 @@ static struct hid_driver tm_driver = { .probe = tm_probe, }; -static int tm_init(void) +static int __init tm_init(void) { return hid_register_driver(&tm_driver); } -static void tm_exit(void) +static void __exit tm_exit(void) { hid_unregister_driver(&tm_driver); } diff --git a/drivers/hid/hid-topseed.c b/drivers/hid/hid-topseed.c index 152ccfabeba5..6925eda1081a 100644 --- a/drivers/hid/hid-topseed.c +++ b/drivers/hid/hid-topseed.c @@ -60,12 +60,12 @@ static struct hid_driver ts_driver = { .input_mapping = ts_input_mapping, }; -static int ts_init(void) +static int __init ts_init(void) { return hid_register_driver(&ts_driver); } -static void ts_exit(void) +static void __exit ts_exit(void) { hid_unregister_driver(&ts_driver); } diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 1f9237f511e3..747542172242 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -237,7 +237,7 @@ static struct hid_driver wacom_driver = { .raw_event = wacom_raw_event, }; -static int wacom_init(void) +static int __init wacom_init(void) { int ret; @@ -248,7 +248,7 @@ static int wacom_init(void) return ret; } -static void wacom_exit(void) +static void __exit wacom_exit(void) { hid_unregister_driver(&wacom_driver); } diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c index 57f710757bf4..a79f0d78c6be 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -152,12 +152,12 @@ static struct hid_driver zp_driver = { .probe = zp_probe, }; -static int zp_init(void) +static int __init zp_init(void) { return hid_register_driver(&zp_driver); } -static void zp_exit(void) +static void __exit zp_exit(void) { hid_unregister_driver(&zp_driver); } From 711a680e35059bc5c7c28d3c4bd0bebd3b7bb6ee Mon Sep 17 00:00:00 2001 From: Bruno Premont Date: Mon, 13 Jul 2009 14:19:58 +0200 Subject: [PATCH 11/19] HID: driver for Twinhan USB 6253:0100 remote control Add explicit key mappings for TwinHan USB HID remote control. All dummy Ctrl, Alt, Meta, ... key press/release events generated by the remote are silenced by "unmapping" them. This makes Power and Volume keys single-key and strips the regular (even while idle) key release events for Ctrl, Alt, Meta, ... Signed-off-by: Bruno Premont Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 + drivers/hid/hid-twinhan.c | 147 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 159 insertions(+) create mode 100644 drivers/hid/hid-twinhan.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index a66ffe747d9c..aba8facecce8 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -152,6 +152,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 diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index db35151673b1..9b9271d6527a 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -40,6 +40,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-core.c b/drivers/hid/hid-core.c index 5601a1432e00..6164ed3a4143 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1317,6 +1317,7 @@ static const struct hid_device_id hid_blacklist[] = { { 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) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 398f731fd699..3058e472aeb5 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -404,6 +404,9 @@ #define USB_VENDOR_ID_TURBOX 0x062a #define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 +#define USB_VENDOR_ID_TWINHAN 0x6253 +#define USB_DEVICE_ID_TWINHAN_IR_REMOTE 0x0100 + #define USB_VENDOR_ID_UCLOGIC 0x5543 #define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042 diff --git a/drivers/hid/hid-twinhan.c b/drivers/hid/hid-twinhan.c new file mode 100644 index 000000000000..b05f602c051e --- /dev/null +++ b/drivers/hid/hid-twinhan.c @@ -0,0 +1,147 @@ +/* + * HID driver for TwinHan IR remote control + * + * Based on hid-gyration.c + * + * Copyright (c) 2009 Bruno Prémont + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +/* Remote control key layout + listing: + * + * Full Screen Power + * KEY_SCREEN KEY_POWER2 + * + * 1 2 3 + * KEY_NUMERIC_1 KEY_NUMERIC_2 KEY_NUMERIC_3 + * + * 4 5 6 + * KEY_NUMERIC_4 KEY_NUMERIC_5 KEY_NUMERIC_6 + * + * 7 8 9 + * KEY_NUMERIC_7 KEY_NUMERIC_8 KEY_NUMERIC_9 + * + * REC 0 Favorite + * KEY_RECORD KEY_NUMERIC_0 KEY_FAVORITES + * + * Rewind Forward + * KEY_REWIND CH+ KEY_FORWARD + * KEY_CHANNELUP + * + * VOL- > VOL+ + * KEY_VOLUMEDOWN KEY_PLAY KEY_VOLUMEUP + * + * CH- + * KEY_CHANNELDOWN + * Recall Stop + * KEY_RESTART KEY_STOP + * + * Timeshift/Pause Mute Cancel + * KEY_PAUSE KEY_MUTE KEY_CANCEL + * + * Capture Preview EPG + * KEY_PRINT KEY_PROGRAM KEY_EPG + * + * Record List Tab Teletext + * KEY_LIST KEY_TAB KEY_TEXT + */ + +#define th_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int twinhan_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD) + return 0; + + switch (usage->hid & HID_USAGE) { + /* Map all keys from Twinhan Remote */ + case 0x004: th_map_key_clear(KEY_TEXT); break; + case 0x006: th_map_key_clear(KEY_RESTART); break; + case 0x008: th_map_key_clear(KEY_EPG); break; + case 0x00c: th_map_key_clear(KEY_REWIND); break; + case 0x00e: th_map_key_clear(KEY_PROGRAM); break; + case 0x00f: th_map_key_clear(KEY_LIST); break; + case 0x010: th_map_key_clear(KEY_MUTE); break; + case 0x011: th_map_key_clear(KEY_FORWARD); break; + case 0x013: th_map_key_clear(KEY_PRINT); break; + case 0x017: th_map_key_clear(KEY_PAUSE); break; + case 0x019: th_map_key_clear(KEY_FAVORITES); break; + case 0x01d: th_map_key_clear(KEY_SCREEN); break; + case 0x01e: th_map_key_clear(KEY_NUMERIC_1); break; + case 0x01f: th_map_key_clear(KEY_NUMERIC_2); break; + case 0x020: th_map_key_clear(KEY_NUMERIC_3); break; + case 0x021: th_map_key_clear(KEY_NUMERIC_4); break; + case 0x022: th_map_key_clear(KEY_NUMERIC_5); break; + case 0x023: th_map_key_clear(KEY_NUMERIC_6); break; + case 0x024: th_map_key_clear(KEY_NUMERIC_7); break; + case 0x025: th_map_key_clear(KEY_NUMERIC_8); break; + case 0x026: th_map_key_clear(KEY_NUMERIC_9); break; + case 0x027: th_map_key_clear(KEY_NUMERIC_0); break; + case 0x028: th_map_key_clear(KEY_PLAY); break; + case 0x029: th_map_key_clear(KEY_CANCEL); break; + case 0x02b: th_map_key_clear(KEY_TAB); break; + /* Power = 0x0e0 + 0x0e1 + 0x0e2 + 0x03f */ + case 0x03f: th_map_key_clear(KEY_POWER2); break; + case 0x04a: th_map_key_clear(KEY_RECORD); break; + case 0x04b: th_map_key_clear(KEY_CHANNELUP); break; + case 0x04d: th_map_key_clear(KEY_STOP); break; + case 0x04e: th_map_key_clear(KEY_CHANNELDOWN); break; + /* Volume down = 0x0e1 + 0x051 */ + case 0x051: th_map_key_clear(KEY_VOLUMEDOWN); break; + /* Volume up = 0x0e1 + 0x052 */ + case 0x052: th_map_key_clear(KEY_VOLUMEUP); break; + /* Kill the extra keys used for multi-key "power" and "volume" keys + * as well as continuously to release CTRL,ALT,META,... keys */ + case 0x0e0: + case 0x0e1: + case 0x0e2: + case 0x0e3: + case 0x0e4: + case 0x0e5: + case 0x0e6: + case 0x0e7: + default: + return -1; + } + return 1; +} + +static const struct hid_device_id twinhan_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) }, + { } +}; +MODULE_DEVICE_TABLE(hid, twinhan_devices); + +static struct hid_driver twinhan_driver = { + .name = "twinhan", + .id_table = twinhan_devices, + .input_mapping = twinhan_input_mapping, +}; + +static int twinhan_init(void) +{ + return hid_register_driver(&twinhan_driver); +} + +static void twinhan_exit(void) +{ + hid_unregister_driver(&twinhan_driver); +} + +module_init(twinhan_init); +module_exit(twinhan_exit); +MODULE_LICENSE("GPL"); From 3040c8203d68a9a0564af81729085054fd6b5b03 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 12 Jul 2009 09:42:47 +0200 Subject: [PATCH 12/19] HID: Drop NULL test on list_entry result list_entry, which is an alias for container_of, cannot return NULL, as there is no way to add a NULL value to a doubly linked list. A simplified version of the semantic match that findds this problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r@ expression x,E; statement S1,S2; position p,p1; @@ *x = list_entry@p(...) ... when != x = E *if@p1 (x == NULL) S1 else S2 // Signed-off-by: Julia Lawall Signed-off-by: Jiri Kosina --- drivers/hid/hid-lgff.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c index 95835ea5690f..987abebe0829 100644 --- a/drivers/hid/hid-lgff.c +++ b/drivers/hid/hid-lgff.c @@ -151,11 +151,6 @@ int lgff_init(struct hid_device* hid) /* Check that the report looks ok */ report = list_entry(report_list->next, struct hid_report, list); - if (!report) { - err_hid("NULL output report"); - return -1; - } - field = report->field[0]; if (!field) { err_hid("NULL field"); From 44cb2db10d05fc263c9fa44347d350d4f7f5890c Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Sat, 8 Aug 2009 02:17:32 +0200 Subject: [PATCH 13/19] HID: fix overrun in quirks initialization Check whether index is within bounds before testing the element. declared in drivers/hid/usbhid/hid-core.c:62: static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = ... Signed-off-by: Roel Kluin Cc: Jiri Kosina Signed-off-by: Andrew Morton Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index d8f7423f363e..0d9045aa2c4b 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -201,7 +201,7 @@ int usbhid_quirks_init(char **quirks_param) u32 quirks; int n = 0, m; - for (; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) { + for (; n < MAX_USBHID_BOOT_QUIRKS && quirks_param[n]; n++) { m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x", &idVendor, &idProduct, &quirks); From a809dda0369ae6157f7bcd3e704b857d43704cbd Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 8 Aug 2009 02:26:10 +0200 Subject: [PATCH 14/19] HID: fix memory leak on error patch in debug code Error handling code following a kzalloc should free the allocated data. The semantic match that finds the problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r exists@ local idexpression x; statement S; expression E; identifier f,f1,l; position p1,p2; expression *ptr != NULL; @@ x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...); ... if (x == NULL) S <... when != x when != if (...) { <+...x...+> } ( x->f1 = E | (x->f1 == NULL || ...) | f(...,x->f1,...) ) ...> ( return \(0\|<+...x...+>\|ptr\); | return@p2 ...; ) @script:python@ p1 << r.p1; p2 << r.p2; @@ print "* file: %s kmalloc %s return %s" % (p1[0].file,p1[0].line,p2[0].line) // Signed-off-by: Julia Lawall Cc: Jiri Kosina Signed-off-by: Andrew Morton Signed-off-by: Jiri Kosina --- drivers/hid/hid-debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index a331a1821e85..06e87dc77796 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -909,6 +909,7 @@ static int hid_debug_events_open(struct inode *inode, struct file *file) if (!(list->hid_debug_buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_KERNEL))) { err = -ENOMEM; + kfree(list); goto out; } list->hdev = (struct hid_device *) inode->i_private; From 31f7fd795d17b264c3c05e4a976f963012c17c68 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 31 Jul 2009 10:56:36 -0400 Subject: [PATCH 15/19] HID: ignore all recent SoundGraph iMON devices After some inspection of the Windows iMON driver, several additional device IDs were added to the lirc_imon driver. At least a few of these have been seen in the wild, and require manual quirking to keep the usbhid driver from binding to them. Rather than list out every single device, ignore the entire device ID range, 0x0034 - 0x0046. Some of these may not advertise themselves as HID devices, but no harm done to such devices anyway. Does the right thing in brief testing w/my 0x0045 device. Signed-off-by: Jarod Wilson Acked-by: Anssi Hannula Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 10 +++++----- drivers/hid/hid-ids.h | 7 ++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6164ed3a4143..afb6a3baade6 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1627,11 +1627,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, { 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) }, @@ -1698,6 +1693,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 && diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3058e472aeb5..35752fcc0d04 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -377,11 +377,8 @@ #define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 #define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 -#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD 0x0038 -#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD2 0x0036 -#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD3 0x0034 -#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD4 0x0044 -#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD5 0x0045 +#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034 +#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046 #define USB_VENDOR_ID_SUN 0x0430 #define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab From 4cfae3e80431fd113f0f4a8e1d4fff84aafe045c Mon Sep 17 00:00:00 2001 From: Henning Glawe Date: Sun, 2 Aug 2009 22:18:12 +0200 Subject: [PATCH 16/19] HID: ignore Philips IEEE802.15.4 RF Dongle This usb device claims to be of HID class, but is in fact a 802.15.4 lowpan transceiver, therefore the generic HID driver cannot operate this device. A separate driver for this device will be written for this using the new 802.15.4 stack. Signed-off-by: Henning Glawe Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index afb6a3baade6..48567d8fe358 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1626,6 +1626,7 @@ 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_TENX, USB_DEVICE_ID_TENX_IBUDDY1) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 35752fcc0d04..adbef5d069c4 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -360,6 +360,9 @@ #define USB_VENDOR_ID_PETALYNX 0x18b1 #define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 +#define USB_VENDOR_ID_PHILIPS 0x0471 +#define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617 + #define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 #define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 From 52cfc61bf95262d55bc00063d7597e5e008fa22e Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 17 Aug 2009 15:37:18 -0700 Subject: [PATCH 17/19] HID: local function should be static __usbhid_submit_report() is a local function wrapped by the exported symbol usbhid_submit_report(). As such, it should be static. Signed-off-by: H Hartley Sweeten Cc: Jiri Kosina Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 9238bcffaf01..25f38a5af269 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -489,7 +489,8 @@ static void hid_ctrl(struct urb *urb) wake_up(&usbhid->wait); } -void __usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) +static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *report, + unsigned char dir) { int head; struct usbhid_device *usbhid = hid->driver_data; From affbb8c6e690be2196258e65f3cc92d55b18d9fa Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 20 Aug 2009 12:04:14 +0200 Subject: [PATCH 18/19] HID: support larger reports than 64 bytes in hiddev hiddev userspace driver uses a rignbuffer to store the parsed usages that should be returned through read(). This buffer is 64 bytes long, which is sufficient for queueing single USB 1.0 low-speed report, which is of maximum size 48 bytes. There are however USB HID devices which are full-speed USB devices, and therefore they are free to produce reports 64 bytes long. This is correctly handled by HID core, but read() on hiddev node gets stuck forever, because the ring buffer loops infinitely (as it is exactly 64 bytes long as well), never advancing the buffer pointer. Plus, the core driver is ready to handle highspeed devices, so we should be able to handle reports from such devices in the hiddev driver as well, which means we need larger ringbuffer. Reported-by: Michael Zeisel Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hiddev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 215b2addddbb..4d1dc0cf1401 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -44,7 +44,7 @@ #define HIDDEV_MINOR_BASE 96 #define HIDDEV_MINORS 16 #endif -#define HIDDEV_BUFFER_SIZE 64 +#define HIDDEV_BUFFER_SIZE 2048 struct hiddev { int exist; From 42960a13001aa6df52ca9952ce996f94a744ea65 Mon Sep 17 00:00:00 2001 From: Jan Scholz Date: Wed, 26 Aug 2009 13:18:51 +0200 Subject: [PATCH 19/19] HID: completely remove apple mightymouse from blacklist Commit fa047e4f6fa63a6e9d0ae4d7749538830d14a343 "HID: fix inverted wheel for bluetooth version of apple mighty mouse" is incomplete. If we remove Apple MightyMouse (bluetooth version) from the list of apple_devices in drivers/hid/hid-apple.c we have to remove it from hid_blacklist in drivers/hid/hid-core.c as well. Signed-off-by: Jan Scholz Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5eb10c2ce665..047844df992a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1319,7 +1319,6 @@ static const struct hid_device_id hid_blacklist[] = { { 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) }, { } };