From 58037eb961f859607b161c50d9d4ecb374de1e8f Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 30 May 2007 15:07:13 +0200 Subject: HID: make debugging output runtime-configurable There have been many reports recently about broken HID devices, the diagnosis of which required users to recompile their kernels in order to be able to provide debugging output needed for coding a quirk for a particular device. This patch makes CONFIG_HID_DEBUG default y if !EMBEDDED and makes it possible to control debugging output produced by HID code by supplying 'debug=1' module parameter. Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 1 + drivers/hid/hid-core.c | 93 +++++++++++++++++++++-------------------- drivers/hid/hid-debug.c | 15 +++++++ drivers/hid/hid-input.c | 26 +++++------- drivers/hid/usbhid/hid-core.c | 38 ++++++++--------- drivers/hid/usbhid/hid-lgff.c | 10 ++--- drivers/hid/usbhid/hid-quirks.c | 8 ++-- drivers/hid/usbhid/hid-tmff.c | 2 +- drivers/hid/usbhid/hid-zpff.c | 8 +--- drivers/hid/usbhid/hiddev.c | 2 +- drivers/hid/usbhid/usbkbd.c | 6 +-- include/linux/hid.h | 20 ++++++--- 12 files changed, 122 insertions(+), 107 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 8fbe9fdac12..5b004b2b1a4 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -24,6 +24,7 @@ config HID config HID_DEBUG bool "HID debugging support" + default y if !EMBEDDED depends on HID ---help--- This option lets the HID layer output diagnostics about its internal diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6ec04e79f68..317cf8a7b63 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -40,6 +40,13 @@ #define DRIVER_DESC "HID core driver" #define DRIVER_LICENSE "GPL" +#ifdef CONFIG_HID_DEBUG +int hid_debug = 0; +module_param_named(debug, hid_debug, bool, 0600); +MODULE_PARM_DESC(debug, "Turn HID debugging mode on and off"); +EXPORT_SYMBOL_GPL(hid_debug); +#endif + /* * Register a new report for a device. */ @@ -78,7 +85,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned struct hid_field *field; if (report->maxfield == HID_MAX_FIELDS) { - dbg("too many fields in report"); + dbg_hid("too many fields in report\n"); return NULL; } @@ -106,7 +113,7 @@ static int open_collection(struct hid_parser *parser, unsigned type) usage = parser->local.usage[0]; if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) { - dbg("collection stack overflow"); + dbg_hid("collection stack overflow\n"); return -1; } @@ -114,7 +121,7 @@ static int open_collection(struct hid_parser *parser, unsigned type) collection = kmalloc(sizeof(struct hid_collection) * parser->device->collection_size * 2, GFP_KERNEL); if (collection == NULL) { - dbg("failed to reallocate collection array"); + dbg_hid("failed to reallocate collection array\n"); return -1; } memcpy(collection, parser->device->collection, @@ -150,7 +157,7 @@ static int open_collection(struct hid_parser *parser, unsigned type) static int close_collection(struct hid_parser *parser) { if (!parser->collection_stack_ptr) { - dbg("collection stack underflow"); + dbg_hid("collection stack underflow\n"); return -1; } parser->collection_stack_ptr--; @@ -178,7 +185,7 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) static int hid_add_usage(struct hid_parser *parser, unsigned usage) { if (parser->local.usage_index >= HID_MAX_USAGES) { - dbg("usage index exceeded"); + dbg_hid("usage index exceeded\n"); return -1; } parser->local.usage[parser->local.usage_index] = usage; @@ -202,12 +209,12 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign int i; if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) { - dbg("hid_register_report failed"); + dbg_hid("hid_register_report failed\n"); return -1; } if (parser->global.logical_maximum < parser->global.logical_minimum) { - dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum); + dbg_hid("logical range invalid %d %d\n", parser->global.logical_minimum, parser->global.logical_maximum); return -1; } @@ -287,7 +294,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_PUSH: if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) { - dbg("global enviroment stack overflow"); + dbg_hid("global enviroment stack overflow\n"); return -1; } @@ -298,7 +305,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_POP: if (!parser->global_stack_ptr) { - dbg("global enviroment stack underflow"); + dbg_hid("global enviroment stack underflow\n"); return -1; } @@ -342,27 +349,27 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: if ((parser->global.report_size = item_udata(item)) > 32) { - dbg("invalid report_size %d", parser->global.report_size); + dbg_hid("invalid report_size %d\n", parser->global.report_size); return -1; } return 0; case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) { - dbg("invalid report_count %d", parser->global.report_count); + dbg_hid("invalid report_count %d\n", parser->global.report_count); return -1; } return 0; case HID_GLOBAL_ITEM_TAG_REPORT_ID: if ((parser->global.report_id = item_udata(item)) == 0) { - dbg("report_id 0 is invalid"); + dbg_hid("report_id 0 is invalid\n"); return -1; } return 0; default: - dbg("unknown global tag 0x%x", item->tag); + dbg_hid("unknown global tag 0x%x\n", item->tag); return -1; } } @@ -377,7 +384,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) unsigned n; if (item->size == 0) { - dbg("item data expected for local item"); + dbg_hid("item data expected for local item\n"); return -1; } @@ -395,14 +402,14 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) * items and the first delimiter set. */ if (parser->local.delimiter_depth != 0) { - dbg("nested delimiters"); + dbg_hid("nested delimiters\n"); return -1; } parser->local.delimiter_depth++; parser->local.delimiter_branch++; } else { if (parser->local.delimiter_depth < 1) { - dbg("bogus close delimiter"); + dbg_hid("bogus close delimiter\n"); return -1; } parser->local.delimiter_depth--; @@ -412,7 +419,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) case HID_LOCAL_ITEM_TAG_USAGE: if (parser->local.delimiter_branch > 1) { - dbg("alternative usage ignored"); + dbg_hid("alternative usage ignored\n"); return 0; } @@ -424,7 +431,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: if (parser->local.delimiter_branch > 1) { - dbg("alternative usage ignored"); + dbg_hid("alternative usage ignored\n"); return 0; } @@ -437,7 +444,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: if (parser->local.delimiter_branch > 1) { - dbg("alternative usage ignored"); + dbg_hid("alternative usage ignored\n"); return 0; } @@ -446,14 +453,14 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) for (n = parser->local.usage_minimum; n <= data; n++) if (hid_add_usage(parser, n)) { - dbg("hid_add_usage failed\n"); + dbg_hid("hid_add_usage failed\n"); return -1; } return 0; default: - dbg("unknown local item tag 0x%x", item->tag); + dbg_hid("unknown local item tag 0x%x\n", item->tag); return 0; } return 0; @@ -487,7 +494,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) ret = hid_add_field(parser, HID_FEATURE_REPORT, data); break; default: - dbg("unknown main item tag 0x%x", item->tag); + dbg_hid("unknown main item tag 0x%x\n", item->tag); ret = 0; } @@ -502,7 +509,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item) { - dbg("reserved item type, tag 0x%x", item->tag); + dbg_hid("reserved item type, tag 0x%x\n", item->tag); return 0; } @@ -667,14 +674,14 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size) while ((start = fetch_item(start, end, &item)) != NULL) { if (item.format != HID_ITEM_FORMAT_SHORT) { - dbg("unexpected long global item"); + dbg_hid("unexpected long global item\n"); hid_free_device(device); vfree(parser); return NULL; } if (dispatch_type[item.type](parser, &item)) { - dbg("item %u %u %u %u parsing failed\n", + dbg_hid("item %u %u %u %u parsing failed\n", item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); hid_free_device(device); vfree(parser); @@ -683,13 +690,13 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size) if (start == end) { if (parser->collection_stack_ptr) { - dbg("unbalanced collection at end of report description"); + dbg_hid("unbalanced collection at end of report description\n"); hid_free_device(device); vfree(parser); return NULL; } if (parser->local.delimiter_depth) { - dbg("unbalanced delimiter at end of report description"); + dbg_hid("unbalanced delimiter at end of report description\n"); hid_free_device(device); vfree(parser); return NULL; @@ -699,7 +706,7 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size) } } - dbg("item fetching failed at offset %d\n", (int)(end - start)); + dbg_hid("item fetching failed at offset %d\n", (int)(end - start)); hid_free_device(device); vfree(parser); return NULL; @@ -915,13 +922,13 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) hid_dump_input(field->usage + offset, value); if (offset >= field->report_count) { - dbg("offset (%d) exceeds report_count (%d)", 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) { if (value != snto32(s32ton(value, size), size)) { - dbg("value %d is out of range", value); + dbg_hid("value %d is out of range\n", value); return -1; } } @@ -934,19 +941,17 @@ 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_report *report; - int n, rsize; + int n, rsize, i; if (!hid) return -ENODEV; if (!size) { - dbg("empty report"); + dbg_hid("empty report\n"); return -1; } -#ifdef CONFIG_HID_DEBUG - printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); -#endif + dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); n = 0; /* Normally report number is 0 */ if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */ @@ -954,25 +959,21 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i size--; } -#ifdef CONFIG_HID_DEBUG - { - int i; - printk(KERN_DEBUG __FILE__ ": report %d (size %u) = ", n, size); - for (i = 0; i < size; i++) - printk(" %02x", data[i]); - printk("\n"); - } -#endif + /* dump the report descriptor */ + dbg_hid("report %d (size %u) = ", n, size); + for (i = 0; i < size; i++) + dbg_hid_line(" %02x", data[i]); + dbg_hid_line("\n"); if (!(report = report_enum->report_id_hash[n])) { - dbg("undefined report_id %d received", n); + dbg_hid("undefined report_id %d received\n", n); return -1; } rsize = ((report->size - 1) >> 3) + 1; if (size < rsize) { - dbg("report %d is too short, (%d < %d)", report->id, size, rsize); + dbg_hid("report %d is too short, (%d < %d)\n", report->id, size, rsize); memset(data + size, 0, rsize - size); } diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 83c4126b37c..a13757b7898 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -347,6 +347,9 @@ static void resolv_usage_page(unsigned page) { void hid_resolv_usage(unsigned usage) { const struct hid_usage_entry *p; + if (!hid_debug) + return; + resolv_usage_page(usage >> 16); printk("."); for (p = hid_usage_table; p->description; p++) @@ -369,6 +372,9 @@ __inline__ static void tab(int n) { void hid_dump_field(struct hid_field *field, int n) { int j; + if (!hid_debug) + return; + if (field->physical) { tab(n); printk("Physical("); @@ -466,6 +472,9 @@ void hid_dump_device(struct hid_device *device) { unsigned i,k; static 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; @@ -489,6 +498,9 @@ 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) + return; + printk("hid-debug: input "); hid_resolv_usage(usage->hid); printk(" = %d\n", value); @@ -758,6 +770,9 @@ static char **names[EV_MAX + 1] = { void hid_resolv_event(__u8 type, __u16 code) { + if (!hid_debug) + return; + printk("%s.%s", events[type] ? events[type] : "?", names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); } diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f817897b17..38595d3935c 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -308,9 +308,7 @@ static int hidinput_setkeycode(struct input_dev *dev, int scancode, clear_bit(old_keycode, dev->keybit); set_bit(usage->code, dev->keybit); -#ifdef CONFIG_HID_DEBUG - printk (KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode); -#endif + dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode); /* Set the keybit for the old keycode if the old keycode is used * by another key */ if (hidinput_find_key (hid, 0, old_keycode)) @@ -333,11 +331,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel field->hidinput = hidinput; -#ifdef CONFIG_HID_DEBUG - printk(KERN_DEBUG "Mapping: "); + dbg_hid("Mapping: "); hid_resolv_usage(usage->hid); - printk(" ---> "); -#endif + dbg_hid_line(" ---> "); if (field->flags & HID_MAIN_ITEM_CONSTANT) goto ignore; @@ -819,15 +815,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } hid_resolv_event(usage->type, usage->code); -#ifdef CONFIG_HID_DEBUG - printk("\n"); -#endif + + dbg_hid_line("\n"); + return; ignore: -#ifdef CONFIG_HID_DEBUG - printk("IGNORED\n"); -#endif + dbg_hid_line("IGNORED\n"); return; } @@ -896,12 +890,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct } if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */ - dbg("Maximum Effects - %d",value); + dbg_hid("Maximum Effects - %d\n",value); return; } if (usage->hid == (HID_UP_PID | 0x7fUL)) { - dbg("PID Pool Report\n"); + dbg_hid("PID Pool Report\n"); return; } @@ -994,7 +988,7 @@ int hidinput_connect(struct hid_device *hid) if (!hidinput || !input_dev) { kfree(hidinput); input_free_device(input_dev); - err("Out of memory during hid input probe"); + err_hid("Out of memory during hid input probe"); return -1; } diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index d91b9dac6df..1eb2c64bc44 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -127,7 +127,7 @@ static void hid_reset(struct work_struct *work) hid_io_error(hid); break; default: - err("can't reset device, %s-%s/input%d, status %d", + err_hid("can't reset device, %s-%s/input%d, status %d", hid_to_usb_dev(hid)->bus->bus_name, hid_to_usb_dev(hid)->devpath, usbhid->ifnum, rc); @@ -220,7 +220,7 @@ static void hid_irq_in(struct urb *urb) if (status) { clear_bit(HID_IN_RUNNING, &usbhid->iofl); if (status != -EPERM) { - err("can't resubmit intr, %s-%s/input%d, status %d", + err_hid("can't resubmit intr, %s-%s/input%d, status %d", hid_to_usb_dev(hid)->bus->bus_name, hid_to_usb_dev(hid)->devpath, usbhid->ifnum, status); @@ -240,10 +240,10 @@ static int hid_submit_out(struct hid_device *hid) usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); usbhid->urbout->dev = hid_to_usb_dev(hid); - dbg("submitting out urb"); + dbg_hid("submitting out urb\n"); if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { - err("usb_submit_urb(out) failed"); + err_hid("usb_submit_urb(out) failed"); return -1; } @@ -287,12 +287,12 @@ static int hid_submit_ctrl(struct hid_device *hid) usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum); usbhid->cr->wLength = cpu_to_le16(len); - dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u", + dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n", usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report", usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength); if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) { - err("usb_submit_urb(ctrl) failed"); + err_hid("usb_submit_urb(ctrl) failed"); return -1; } @@ -474,7 +474,7 @@ int usbhid_wait_io(struct hid_device *hid) if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)), 10*HZ)) { - dbg("timeout waiting for ctrl or out queue to clear"); + dbg_hid("timeout waiting for ctrl or out queue to clear\n"); return -1; } @@ -667,7 +667,7 @@ static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) USB_CTRL_GET_TIMEOUT); if (result < 0) - err("%s failed: %d\n", __func__, result); + err_hid("%s failed: %d\n", __func__, result); kfree(buf); } @@ -746,7 +746,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && (!interface->desc.bNumEndpoints || usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) { - dbg("class descriptor not present\n"); + dbg_hid("class descriptor not present\n"); return NULL; } @@ -755,19 +755,19 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { - dbg("weird size of report descriptor (%u)", rsize); + dbg_hid("weird size of report descriptor (%u)\n", rsize); return NULL; } if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) { - dbg("couldn't allocate rdesc memory"); + dbg_hid("couldn't allocate rdesc memory\n"); return NULL; } hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) { - dbg("reading report descriptor failed"); + dbg_hid("reading report descriptor failed\n"); kfree(rdesc); return NULL; } @@ -781,15 +781,13 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (quirks & HID_QUIRK_SWAPPED_MIN_MAX) hid_fixup_cypress_descriptor(rdesc, rsize); -#ifdef CONFIG_HID_DEBUG - printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); + dbg_hid("report descriptor (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) - printk(" %02x", (unsigned char) rdesc[n]); - printk("\n"); -#endif + dbg_hid_line(" %02x", (unsigned char) rdesc[n]); + dbg_hid_line("\n"); if (!(hid = hid_parse_report(rdesc, n))) { - dbg("parsing report descriptor failed"); + dbg_hid("parsing report descriptor failed\n"); kfree(rdesc); return NULL; } @@ -861,7 +859,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) } if (!usbhid->urbin) { - err("couldn't find an input interrupt endpoint"); + err_hid("couldn't find an input interrupt endpoint"); goto fail; } @@ -978,7 +976,7 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) int i; char *c; - dbg("HID probe called for ifnum %d", + dbg_hid("HID probe called for ifnum %d\n", intf->altsetting->desc.bInterfaceNumber); if (!(hid = usb_hid_configure(intf))) diff --git a/drivers/hid/usbhid/hid-lgff.c b/drivers/hid/usbhid/hid-lgff.c index c5cd4107d6a..4b7ab6a46d9 100644 --- a/drivers/hid/usbhid/hid-lgff.c +++ b/drivers/hid/usbhid/hid-lgff.c @@ -78,7 +78,7 @@ static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *ef report->field[0]->value[1] = 0x08; report->field[0]->value[2] = x; report->field[0]->value[3] = y; - dbg("(x, y)=(%04x, %04x)", x, y); + dbg_hid("(x, y)=(%04x, %04x)\n", x, y); usbhid_submit_report(hid, report, USB_DIR_OUT); break; @@ -93,7 +93,7 @@ static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *ef report->field[0]->value[1] = 0x00; report->field[0]->value[2] = left; report->field[0]->value[3] = right; - dbg("(left, right)=(%04x, %04x)", left, right); + dbg_hid("(left, right)=(%04x, %04x)\n", left, right); usbhid_submit_report(hid, report, USB_DIR_OUT); break; } @@ -113,20 +113,20 @@ int hid_lgff_init(struct hid_device* hid) /* Find the report to use */ if (list_empty(report_list)) { - err("No output report found"); + err_hid("No output report found"); return -1; } /* Check that the report looks ok */ report = list_entry(report_list->next, struct hid_report, list); if (!report) { - err("NULL output report"); + err_hid("NULL output report"); return -1; } field = report->field[0]; if (!field) { - err("NULL field"); + err_hid("NULL field"); return -1; } diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index f6c4145dc20..a418ebfe39b 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -493,7 +493,7 @@ static struct hid_blacklist *usbhid_exists_dquirk(const u16 idVendor, } if (bl_entry != NULL) - dbg("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", + dbg_hid("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", bl_entry->quirks, bl_entry->idVendor, bl_entry->idProduct); @@ -521,13 +521,13 @@ int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct, int list_edited = 0; if (!idVendor) { - dbg("Cannot add a quirk with idVendor = 0"); + dbg_hid("Cannot add a quirk with idVendor = 0\n"); return -EINVAL; } q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL); if (!q_new) { - dbg("Could not allocate quirks_list_struct"); + dbg_hid("Could not allocate quirks_list_struct\n"); return -ENOMEM; } @@ -643,7 +643,7 @@ static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor, bl_entry = &hid_blacklist[n]; if (bl_entry != NULL) - dbg("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", + dbg_hid("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", bl_entry->quirks, bl_entry->idVendor, bl_entry->idProduct); return bl_entry; diff --git a/drivers/hid/usbhid/hid-tmff.c b/drivers/hid/usbhid/hid-tmff.c index ab5ba6ef891..555bb48b429 100644 --- a/drivers/hid/usbhid/hid-tmff.c +++ b/drivers/hid/usbhid/hid-tmff.c @@ -70,7 +70,7 @@ static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *ef tmff->rumble->value[0] = left; tmff->rumble->value[1] = right; - dbg("(left,right)=(%08x, %08x)", left, right); + dbg_hid("(left,right)=(%08x, %08x)\n", left, right); usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); return 0; diff --git a/drivers/hid/usbhid/hid-zpff.c b/drivers/hid/usbhid/hid-zpff.c index a7fbffcdaf3..5a688274f6a 100644 --- a/drivers/hid/usbhid/hid-zpff.c +++ b/drivers/hid/usbhid/hid-zpff.c @@ -21,10 +21,6 @@ */ -/* #define DEBUG */ - -#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg) - #include #include #include @@ -49,14 +45,14 @@ static int hid_zpff_play(struct input_dev *dev, void *data, left = effect->u.rumble.strong_magnitude; right = effect->u.rumble.weak_magnitude; - debug("called with 0x%04x 0x%04x", left, right); + dbg_hid("called with 0x%04x 0x%04x\n", left, right); left = left * 0x7f / 0xffff; right = right * 0x7f / 0xffff; zpff->report->field[2]->value[0] = left; zpff->report->field[3]->value[0] = right; - debug("running with 0x%02x 0x%02x", left, right); + dbg_hid("running with 0x%02x 0x%02x\n", left, right); usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); return 0; diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 488d61bdbf2..e793127f971 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -779,7 +779,7 @@ int hiddev_connect(struct hid_device *hid) retval = usb_register_dev(usbhid->intf, &hiddev_class); if (retval) { - err("Not able to get a minor for this device."); + err_hid("Not able to get a minor for this device."); kfree(hiddev); return -1; } diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 13097878071..b76b02f7b52 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -125,7 +125,7 @@ static void usb_kbd_irq(struct urb *urb) resubmit: i = usb_submit_urb (urb, GFP_ATOMIC); if (i) - err ("can't resubmit intr, %s-%s/input0, status %d", + err_hid ("can't resubmit intr, %s-%s/input0, status %d", kbd->usbdev->bus->bus_name, kbd->usbdev->devpath, i); } @@ -151,7 +151,7 @@ static int usb_kbd_event(struct input_dev *dev, unsigned int type, *(kbd->leds) = kbd->newleds; kbd->led->dev = kbd->usbdev; if (usb_submit_urb(kbd->led, GFP_ATOMIC)) - err("usb_submit_urb(leds) failed"); + err_hid("usb_submit_urb(leds) failed"); return 0; } @@ -169,7 +169,7 @@ static void usb_kbd_led(struct urb *urb) *(kbd->leds) = kbd->newleds; kbd->led->dev = kbd->usbdev; if (usb_submit_urb(kbd->led, GFP_ATOMIC)) - err("usb_submit_urb(leds) failed"); + err_hid("usb_submit_urb(leds) failed"); } static int usb_kbd_open(struct input_dev *dev) diff --git a/include/linux/hid.h b/include/linux/hid.h index 827ee748fd4..eb53d9a9ae5 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -488,6 +488,11 @@ struct hid_descriptor { #define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001)) /* HID core API */ + +#ifdef CONFIG_HID_DEBUG +extern int hid_debug; +#endif + extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report); extern int hidinput_connect(struct hid_device *); @@ -523,14 +528,19 @@ static inline int hid_pidff_init(struct hid_device *hid) { return -ENODEV; } #else static inline int hid_ff_init(struct hid_device *hid) { return -1; } #endif -#ifdef DEBUG -#define dbg(format, arg...) printk(KERN_DEBUG "%s: " format "\n" , \ - __FILE__ , ## arg) + +#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 -#define dbg(format, arg...) do {} while (0) +#define dbg_hid(format, arg...) do {} while (0) +#define dbg_hid_line dbg_hid #endif -#define err(format, arg...) printk(KERN_ERR "%s: " format "\n" , \ +#define err_hid(format, arg...) printk(KERN_ERR "%s: " format "\n" , \ __FILE__ , ## arg) #endif -- cgit v1.2.3 From cb1d93c98c49e268918e35e45e5c407fc4dc4e9f Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 14 May 2007 10:20:29 +0200 Subject: HID: input mapping for Chicony KU-0418 tactical pad Chicony KU-0418 (aka Saitek PZ08AU gaming keyboard) has a separate "tactical pad" with 11 non-functional buttons - they generate usage codes from 0xff00 (MSVENDOR) usage page. Special case handling for this keyboard added, so no later clash with MSVENDOR mappings is going to occur. Pointed out in bugzilla #7352 Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f817897b17..ce6d7644e6a 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -688,7 +688,28 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; case HID_UP_MSVENDOR: - goto ignore; + + /* special case - Chicony Chicony KU-0418 tactical pad */ + if (device->vendor == 0x04f2 && device->product == 0x0418) { + set_bit(EV_REP, input->evbit); + switch(usage->hid & HID_USAGE) { + case 0xff01: map_key_clear(BTN_1); break; + case 0xff02: map_key_clear(BTN_2); break; + case 0xff03: map_key_clear(BTN_3); break; + case 0xff04: map_key_clear(BTN_4); break; + case 0xff05: map_key_clear(BTN_5); break; + case 0xff06: map_key_clear(BTN_6); break; + case 0xff07: map_key_clear(BTN_7); break; + case 0xff08: map_key_clear(BTN_8); break; + case 0xff09: map_key_clear(BTN_9); break; + case 0xff0a: map_key_clear(BTN_A); break; + case 0xff0b: map_key_clear(BTN_B); break; + default: goto ignore; + } + } else { + goto ignore; + } + break; case HID_UP_CUSTOM: /* Reported on Logitech and Powerbook USB keyboards */ -- cgit v1.2.3 From defd208681b721dbf2b69347cca5302d60246405 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Sat, 19 May 2007 16:28:04 +0200 Subject: HID: force hid-input for Microsoft SideWinder GameVoice device Microsoft SideWinder GameVoice driver is a trivial device with a few buttons (0x09 HID usage) and an audio connector, which just forwards the audio input into oridinary sound card present in the computer. Despite this fact, the only interface of this device reports itself as a Telephony/Headset type of HID device. This is apparently incorrect - the device itself doesn't provide any audio/telephony functionality. This is achieved in userland application which only needs to receive the button events from the HID driver. This patch establishes a new quirk which forces hid-input to claim a device it will otherwise leave untouched. Reported-by: Tomas Carnecky Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 2 +- drivers/hid/usbhid/hid-quirks.c | 4 ++++ include/linux/hid.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index ce6d7644e6a..70bf5416708 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -997,7 +997,7 @@ int hidinput_connect(struct hid_device *hid) if (IS_INPUT_APPLICATION(hid->collection[i].usage)) break; - if (i == hid->maxcollection) + if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDINPUT) == 0) return -1; if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index f6c4145dc20..62a7f1e9b58 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -209,6 +209,9 @@ #define USB_DEVICE_ID_MGE_UPS 0xffff #define USB_DEVICE_ID_MGE_UPS1 0x0001 +#define USB_VENDOR_ID_MICROSOFT 0x045e +#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b + #define USB_VENDOR_ID_NEC 0x073e #define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 @@ -290,6 +293,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, + { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, diff --git a/include/linux/hid.h b/include/linux/hid.h index 827ee748fd4..6e45d1056e1 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -276,6 +276,7 @@ struct hid_item { #define HID_QUIRK_DUPLICATE_USAGES 0x00200000 #define HID_QUIRK_RESET_LEDS 0x00400000 #define HID_QUIRK_SWAPPED_MIN_MAX 0x00800000 +#define HID_QUIRK_HIDINPUT 0x01000000 /* * This is the global environment of the parser. This information is -- cgit v1.2.3 From 3cd709866f639d24b0d0f38567c19662c98ea92e Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 30 Apr 2007 13:27:48 +0200 Subject: HID: Use menuconfig objects Make a "menuconfig" out of the Kconfig objects "menu, ..., endmenu", so that the user can disable all the options in that menu at once instead of having to disable each option separately. Signed-off-by: Jan Engelhardt Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 8fbe9fdac12..d96eb086582 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1,8 +1,12 @@ # # HID driver configuration # -menu "HID Devices" +menuconfig HID_SUPPORT + bool "HID Devices" depends on INPUT + default y + +if HID_SUPPORT config HID tristate "Generic HID support" @@ -38,5 +42,4 @@ config HID_DEBUG source "drivers/hid/usbhid/Kconfig" -endmenu - +endif # HID_SUPPORT -- cgit v1.2.3 From 2fa45a4cffd0c4ab4e238e8ad3b4f9b0c10ac1f3 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 30 May 2007 11:11:12 -0400 Subject: USB HID: avoid flush_scheduled_work() This patch (as914) replaces a call to flush_scheduled_work() with cancel_work_sync(), in order to help avoid potential deadlocks. Signed-off-by: Alan Stern Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index d91b9dac6df..093abb5c987 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -956,7 +956,7 @@ static void hid_disconnect(struct usb_interface *intf) usb_kill_urb(usbhid->urbctrl); del_timer_sync(&usbhid->io_retry); - flush_scheduled_work(); + cancel_work_sync(&usbhid->reset_work); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(hid); -- cgit v1.2.3 From 1b3ebe931195725cceee825c430a8bd5319c2566 Mon Sep 17 00:00:00 2001 From: Julien Eyries Date: Tue, 12 Jun 2007 10:12:40 +0200 Subject: HID: support for Gameron dual psx adaptor This patch provides support for the Gameron dual psx adaptor. The modification is to add the quirk HID_QUIRK_MULTI_INPUT for this specific USB device. Signed-off-by: Julien Eyries Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-quirks.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 62a7f1e9b58..a320c24ea6e 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -105,6 +105,9 @@ #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 +#define USB_VENDOR_ID_GAMERON 0x0810 +#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001 + #define USB_VENDOR_ID_GLAB 0x06c2 #define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 #define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 @@ -281,6 +284,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, -- cgit v1.2.3 From 5fce620c0c7caa9e8d9588e8dcc66c52c456851a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 12 Jun 2007 22:04:32 +0200 Subject: HID: fix mismatch between hid-input HUT find/search mapping and the HUT When comparing usb vs ps2 / testing the keycodes generated for the easy access keys on my trust (microsoft compatible) keyboard. I noticed the search key generated the keycode for find when connected through USB. This lead me to check the consumer page mappings in hid-input.c . And it turns out the the mapping for ID 0x221 deviates from the HUT standard document: http://www.usb.org/developers/devclass_docs/Hut1_12.pdf Currently it is incorrectly mapped to find, whereas it should be mapped to search. I also added missing bindings for ID 0x21f, the real find and for 0x222, goto. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 70bf5416708..6cb884638fd 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -598,7 +598,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x21b: map_key_clear(KEY_COPY); break; case 0x21c: map_key_clear(KEY_CUT); break; case 0x21d: map_key_clear(KEY_PASTE); break; - case 0x221: map_key_clear(KEY_FIND); break; + case 0x21f: map_key_clear(KEY_FIND); break; + case 0x221: map_key_clear(KEY_SEARCH); break; + case 0x222: map_key_clear(KEY_GOTO); break; case 0x223: map_key_clear(KEY_HOMEPAGE); break; case 0x224: map_key_clear(KEY_BACK); break; case 0x225: map_key_clear(KEY_FORWARD); break; -- cgit v1.2.3 From 92d9e6e607eb7f8f1d2a43935f45cf300cf6fdf8 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 18 Jun 2007 13:30:11 +0200 Subject: HID: support for Petalynx Maxter remote control Petalynx Maxter remote control [1] 0x18b1/0x0037 emits 0xfa and 0xfc from consumer page (reserved in HUT 1.12) for back and more keys. It also emits a few usages from LOGIVENDOR page, which need adding. Also, this device has broken report descriptor - the reported maximum is too low - it doesn't contain the range for 'back' and 'more' keys, so we need to bump it up before the report descriptor is being parsed. Besides all this, it also requires NOGET quirk. This patch does so. [1] http://www.elmak.pl/index.php?option=com_phpshop&page=shop.browse&category_id=14&ext=opis&lang=en Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 17 +++++++++++++++-- drivers/hid/usbhid/hid-core.c | 18 ++++++++++++++++++ drivers/hid/usbhid/hid-quirks.c | 5 +++++ include/linux/hid.h | 1 + 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 6cb884638fd..1b8b3334140 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -566,6 +566,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x0e5: map_key_clear(KEY_BASSBOOST); break; case 0x0e9: map_key_clear(KEY_VOLUMEUP); break; case 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break; + + /* reserved in HUT 1.12. Reported on Petalynx remote */ + case 0x0f6: map_key_clear(KEY_NEXT); break; + case 0x0fa: map_key_clear(KEY_BACK); break; + case 0x183: map_key_clear(KEY_CONFIG); break; case 0x184: map_key_clear(KEY_WORDPROCESSOR); break; case 0x185: map_key_clear(KEY_EDITOR); break; @@ -727,10 +732,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } break; - case HID_UP_LOGIVENDOR: /* Reported on Logitech Ultra X Media Remote */ - + case HID_UP_LOGIVENDOR: set_bit(EV_REP, input->evbit); switch(usage->hid & HID_USAGE) { + /* Reported on Logitech Ultra X Media Remote */ case 0x004: map_key_clear(KEY_AGAIN); break; case 0x00d: map_key_clear(KEY_HOME); break; case 0x024: map_key_clear(KEY_SHUFFLE); break; @@ -748,6 +753,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x04d: map_key_clear(KEY_SUBTITLE); break; case 0x051: map_key_clear(KEY_RED); break; case 0x052: map_key_clear(KEY_CLOSE); break; + + /* Reported on Petalynx Maxter remote */ + case 0x05a: map_key_clear(KEY_TEXT); break; + case 0x05b: map_key_clear(KEY_RED); break; + case 0x05c: map_key_clear(KEY_GREEN); break; + case 0x05d: map_key_clear(KEY_YELLOW); break; + case 0x05e: map_key_clear(KEY_BLUE); break; + default: goto ignore; } break; diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 093abb5c987..ef7b881aab3 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -688,6 +688,21 @@ static void hid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize) } } +/* Petalynx Maxter Remote has maximum for consumer page set too low */ +static void hid_fixup_petalynx_descriptor(unsigned char *rdesc, int rsize) +{ + if (rsize >= 60 && rdesc[39] == 0x2a + && rdesc[40] == 0xf5 + && rdesc[41] == 0x00 + && rdesc[59] == 0x26 + && rdesc[60] == 0xf9 + && rdesc[61] == 0x00) { + info("Fixing up Petalynx Maxter Remote report descriptor"); + rdesc[60] = 0xfa; + rdesc[40] = 0xfa; + } +} + /* * Some USB barcode readers from cypress have usage min and usage max in * the wrong order @@ -781,6 +796,9 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (quirks & HID_QUIRK_SWAPPED_MIN_MAX) hid_fixup_cypress_descriptor(rdesc, rsize); + if (quirks & HID_QUIRK_PETALYNX_DESCRIPTOR) + hid_fixup_petalynx_descriptor(rdesc, rsize); + #ifdef CONFIG_HID_DEBUG printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index a320c24ea6e..ad8a4ac8e43 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -226,6 +226,9 @@ #define USB_VENDOR_ID_PANTHERLORD 0x0810 #define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 +#define USB_VENDOR_ID_PETALYNX 0x18b1 +#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 + #define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 #define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 @@ -426,6 +429,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_PETALYNX_DESCRIPTOR | HID_QUIRK_NOGET }, + { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, diff --git a/include/linux/hid.h b/include/linux/hid.h index 6e45d1056e1..e41067951dd 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -277,6 +277,7 @@ struct hid_item { #define HID_QUIRK_RESET_LEDS 0x00400000 #define HID_QUIRK_SWAPPED_MIN_MAX 0x00800000 #define HID_QUIRK_HIDINPUT 0x01000000 +#define HID_QUIRK_PETALYNX_DESCRIPTOR 0x02000000 /* * This is the global environment of the parser. This information is -- cgit v1.2.3 From b8e98f1c47b743e56f8ebe21006199cefd577ce5 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 18 Jun 2007 17:41:14 +0200 Subject: HID: Add NOGET quirk for all NCR devices Devices manufactured by NCR have userspace hiddev-based drivers, which do all the necessary device querying by themselves. The devices must not be queried directly by the generic HID driver, as reported by NCR engineers. Cc: Petr Ostadal Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-quirks.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index ad8a4ac8e43..a78f5187b6e 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -215,6 +215,10 @@ #define USB_VENDOR_ID_MICROSOFT 0x045e #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b +#define USB_VENDOR_ID_NCR 0x0404 +#define USB_DEVICE_ID_NCR_FIRST 0x0300 +#define USB_DEVICE_ID_NCR_LAST 0x03ff + #define USB_VENDOR_ID_NEC 0x073e #define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 @@ -688,6 +692,12 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct) idProduct <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) return HID_QUIRK_IGNORE; + /* NCR devices must not be queried for reports */ + if (idVendor == USB_VENDOR_ID_NCR && + idProduct >= USB_DEVICE_ID_NCR_FIRST && + idProduct <= USB_DEVICE_ID_NCR_LAST) + return HID_QUIRK_NOGET; + down_read(&dquirks_rwsem); bl_entry = usbhid_exists_dquirk(idVendor, idProduct); if (!bl_entry) -- cgit v1.2.3 From ea9a4a8b0e5a34eca6613e39d21be879d92ecff5 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 19 Jun 2007 14:09:14 +0200 Subject: HID: separate quirks for report descriptor fixup Lately there have been quite a lot of bug reports against broken devices which require us to fix their report descriptor in the runtime, before it is passed to the HID parser. Those devices have eaten quite an amount of our quirks space, which isn't particularly necessary - the quirks are not needed after the report descriptor is parsed, and they just consume bits. Therefore this patch separates the quirks for report descriptor fixup, and moves their handling into separate code. The quirks are then forgotten as soon as the report descriptor has been parsed. Module parameter 'rdesc_quirks' is introduced to be able to modify these quirks in runtime in a similar way to 'quirks' parameter for ordinary HID quirks. Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 89 +++-------------------- drivers/hid/usbhid/hid-quirks.c | 155 +++++++++++++++++++++++++++++++++++++--- include/linux/hid.h | 36 ++++++---- 3 files changed, 174 insertions(+), 106 deletions(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index ef7b881aab3..3efc3734cec 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -60,6 +60,12 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying " " quirks=vendorID:productID:quirks" " where vendorID, productID, and quirks are all in" " 0x-prefixed hex"); +static char *rdesc_quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL }; +module_param_array_named(rdesc_quirks, rdesc_quirks_param, charp, NULL, 0444); +MODULE_PARM_DESC(rdesc_quirks, "Add/modify report descriptor quirks by specifying " + " rdesc_quirks=vendorID:productID:rdesc_quirks" + " where vendorID, productID, and rdesc_quirks are all in" + " 0x-prefixed hex"); /* * Input submission and I/O error handler. */ @@ -632,20 +638,6 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); } -/* - * Cherry Cymotion keyboard have an invalid HID report descriptor, - * that needs fixing before we can parse it. - */ - -static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize) -{ - if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { - info("Fixing up Cherry Cymotion report descriptor"); - rdesc[11] = rdesc[16] = 0xff; - rdesc[12] = rdesc[17] = 0x03; - } -} - /* * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller * to "operational". Without this, the ps3 controller will not report any @@ -672,61 +664,6 @@ static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) kfree(buf); } -/* - * Certain Logitech keyboards send in report #3 keys which are far - * above the logical maximum described in descriptor. This extends - * the original value of 0x28c of logical maximum to 0x104d - */ -static void hid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 90 && rdesc[83] == 0x26 - && rdesc[84] == 0x8c - && rdesc[85] == 0x02) { - info("Fixing up Logitech keyboard report descriptor"); - rdesc[84] = rdesc[89] = 0x4d; - rdesc[85] = rdesc[90] = 0x10; - } -} - -/* Petalynx Maxter Remote has maximum for consumer page set too low */ -static void hid_fixup_petalynx_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 60 && rdesc[39] == 0x2a - && rdesc[40] == 0xf5 - && rdesc[41] == 0x00 - && rdesc[59] == 0x26 - && rdesc[60] == 0xf9 - && rdesc[61] == 0x00) { - info("Fixing up Petalynx Maxter Remote report descriptor"); - rdesc[60] = 0xfa; - rdesc[40] = 0xfa; - } -} - -/* - * Some USB barcode readers from cypress have usage min and usage max in - * the wrong order - */ -static void hid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize) -{ - short fixed = 0; - int i; - - for (i = 0; i < rsize - 4; i++) { - if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) { - unsigned char tmp; - - rdesc[i] = 0x19; rdesc[i+2] = 0x29; - tmp = rdesc[i+3]; - rdesc[i+3] = rdesc[i+1]; - rdesc[i+1] = tmp; - } - } - - if (fixed) - info("Fixing up Cypress report descriptor"); -} - static struct hid_device *usb_hid_configure(struct usb_interface *intf) { struct usb_host_interface *interface = intf->cur_altsetting; @@ -787,17 +724,9 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) return NULL; } - if ((quirks & HID_QUIRK_CYMOTION)) - hid_fixup_cymotion_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_LOGITECH_DESCRIPTOR) - hid_fixup_logitech_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_SWAPPED_MIN_MAX) - hid_fixup_cypress_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_PETALYNX_DESCRIPTOR) - hid_fixup_petalynx_descriptor(rdesc, rsize); + usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct), rdesc, + rsize, rdesc_quirks_param); #ifdef CONFIG_HID_DEBUG printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index a78f5187b6e..a75c236fcaf 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -299,8 +299,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, @@ -424,17 +422,11 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_LOGITECH_DESCRIPTOR }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_PETALYNX_DESCRIPTOR | HID_QUIRK_NOGET }, - { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, @@ -443,6 +435,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, @@ -466,8 +459,26 @@ static const struct hid_blacklist { { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_SWAPPED_MIN_MAX }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_SWAPPED_MIN_MAX }, + { 0, 0 } +}; + +/* Quirks for devices which require report descriptor fixup go here */ +static const struct hid_rdesc_blacklist { + __u16 idVendor; + __u16 idProduct; + __u32 quirks; +} hid_rdesc_blacklist[] = { + + { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_RDESC_CYMOTION }, + + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_RDESC_LOGITECH }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_RDESC_LOGITECH }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_RDESC_LOGITECH }, + + { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX }, + + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_RDESC_SWAPPED_MIN_MAX }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_RDESC_SWAPPED_MIN_MAX }, { 0, 0 } }; @@ -576,7 +587,6 @@ int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct, return 0; } - /** * usbhid_remove_all_dquirks: remove all runtime HID quirks from memory * @@ -709,3 +719,126 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct) return quirks; } +/* + * Cherry Cymotion keyboard have an invalid HID report descriptor, + * that needs fixing before we can parse it. + */ +static void usbhid_fixup_cymotion_descriptor(char *rdesc, int rsize) +{ + if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { + printk(KERN_INFO "Fixing up Cherry Cymotion report descriptor\n"); + rdesc[11] = rdesc[16] = 0xff; + rdesc[12] = rdesc[17] = 0x03; + } +} + + +/* + * Certain Logitech keyboards send in report #3 keys which are far + * above the logical maximum described in descriptor. This extends + * the original value of 0x28c of logical maximum to 0x104d + */ +static void usbhid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize) +{ + if (rsize >= 90 && rdesc[83] == 0x26 + && rdesc[84] == 0x8c + && rdesc[85] == 0x02) { + printk(KERN_INFO "Fixing up Logitech keyboard report descriptor\n"); + rdesc[84] = rdesc[89] = 0x4d; + rdesc[85] = rdesc[90] = 0x10; + } +} + +/* Petalynx Maxter Remote has maximum for consumer page set too low */ +static void usbhid_fixup_petalynx_descriptor(unsigned char *rdesc, int rsize) +{ + if (rsize >= 60 && rdesc[39] == 0x2a + && rdesc[40] == 0xf5 + && rdesc[41] == 0x00 + && rdesc[59] == 0x26 + && rdesc[60] == 0xf9 + && rdesc[61] == 0x00) { + printk(KERN_INFO "Fixing up Petalynx Maxter Remote report descriptor\n"); + rdesc[60] = 0xfa; + rdesc[40] = 0xfa; + } +} + +/* + * Some USB barcode readers from cypress have usage min and usage max in + * the wrong order + */ +static void usbhid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize) +{ + short fixed = 0; + int i; + + for (i = 0; i < rsize - 4; i++) { + if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) { + unsigned char tmp; + + rdesc[i] = 0x19; rdesc[i+2] = 0x29; + tmp = rdesc[i+3]; + rdesc[i+3] = rdesc[i+1]; + rdesc[i+1] = tmp; + } + } + + if (fixed) + printk(KERN_INFO "Fixing up Cypress report descriptor\n"); +} + + +static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize) +{ + if ((quirks & HID_QUIRK_RDESC_CYMOTION)) + usbhid_fixup_cymotion_descriptor(rdesc, rsize); + + if (quirks & HID_QUIRK_RDESC_LOGITECH) + usbhid_fixup_logitech_descriptor(rdesc, rsize); + + if (quirks & HID_QUIRK_RDESC_SWAPPED_MIN_MAX) + usbhid_fixup_cypress_descriptor(rdesc, rsize); + + if (quirks & HID_QUIRK_RDESC_PETALYNX) + usbhid_fixup_petalynx_descriptor(rdesc, rsize); +} + +/** + * usbhid_fixup_report_descriptor: check if report descriptor needs fixup + * + * Description: + * Walks the hid_rdesc_blacklist[] array and checks whether the device + * is known to have broken report descriptor that needs to be fixed up + * prior to entering the HID parser + * + * Returns: nothing + */ +void usbhid_fixup_report_descriptor(const u16 idVendor, const u16 idProduct, + char *rdesc, unsigned rsize, char **quirks_param) +{ + int n, m; + u16 paramVendor, paramProduct; + u32 quirks; + + /* static rdesc quirk entries */ + for (n = 0; hid_rdesc_blacklist[n].idVendor; n++) + if (hid_rdesc_blacklist[n].idVendor == idVendor && + hid_rdesc_blacklist[n].idProduct == idProduct) + __usbhid_fixup_report_descriptor(hid_rdesc_blacklist[n].quirks, + rdesc, rsize); + + /* runtime rdesc quirk entries handling */ + for (n = 0; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) { + m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x", + ¶mVendor, ¶mProduct, &quirks); + + if (m != 3) + printk(KERN_WARNING + "Could not parse HID quirk module param %s\n", + quirks_param[n]); + else if (paramVendor == idVendor && paramProduct == idProduct) + __usbhid_fixup_report_descriptor(quirks, rdesc, rsize); + } + +} diff --git a/include/linux/hid.h b/include/linux/hid.h index e41067951dd..4daf5eea78a 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -263,21 +263,26 @@ struct hid_item { #define HID_QUIRK_2WHEEL_MOUSE_HACK_5 0x00000100 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x00000200 #define HID_QUIRK_MIGHTYMOUSE 0x00000400 -#define HID_QUIRK_CYMOTION 0x00000800 -#define HID_QUIRK_POWERBOOK_HAS_FN 0x00001000 -#define HID_QUIRK_POWERBOOK_FN_ON 0x00002000 -#define HID_QUIRK_INVERT_HWHEEL 0x00004000 -#define HID_QUIRK_POWERBOOK_ISO_KEYBOARD 0x00008000 -#define HID_QUIRK_BAD_RELATIVE_KEYS 0x00010000 -#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00020000 -#define HID_QUIRK_IGNORE_MOUSE 0x00040000 -#define HID_QUIRK_SONY_PS3_CONTROLLER 0x00080000 -#define HID_QUIRK_LOGITECH_DESCRIPTOR 0x00100000 -#define HID_QUIRK_DUPLICATE_USAGES 0x00200000 -#define HID_QUIRK_RESET_LEDS 0x00400000 -#define HID_QUIRK_SWAPPED_MIN_MAX 0x00800000 -#define HID_QUIRK_HIDINPUT 0x01000000 -#define HID_QUIRK_PETALYNX_DESCRIPTOR 0x02000000 +#define HID_QUIRK_POWERBOOK_HAS_FN 0x00000800 +#define HID_QUIRK_POWERBOOK_FN_ON 0x00001000 +#define HID_QUIRK_INVERT_HWHEEL 0x00002000 +#define HID_QUIRK_POWERBOOK_ISO_KEYBOARD 0x00004000 +#define HID_QUIRK_BAD_RELATIVE_KEYS 0x00008000 +#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 +#define HID_QUIRK_IGNORE_MOUSE 0x00020000 +#define HID_QUIRK_SONY_PS3_CONTROLLER 0x00040000 +#define HID_QUIRK_DUPLICATE_USAGES 0x00080000 +#define HID_QUIRK_RESET_LEDS 0x00100000 +#define HID_QUIRK_HIDINPUT 0x00200000 + +/* + * Separate quirks for runtime report descriptor fixup + */ + +#define HID_QUIRK_RDESC_CYMOTION 0x00000001 +#define HID_QUIRK_RDESC_LOGITECH 0x00000002 +#define HID_QUIRK_RDESC_SWAPPED_MIN_MAX 0x00000004 +#define HID_QUIRK_RDESC_PETALYNX 0x00000008 /* * This is the global environment of the parser. This information is @@ -508,6 +513,7 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct); int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct, const u32 quirks); int usbhid_quirks_init(char **quirks_param); void usbhid_quirks_exit(void); +void usbhid_fixup_report_descriptor(const u16, const u16, char *, unsigned, char **); #ifdef CONFIG_HID_FF int hid_ff_init(struct hid_device *hid); -- cgit v1.2.3 From 816cbfda8b5113629707f604660204701e93b7ce Mon Sep 17 00:00:00 2001 From: Diogo Kastrup Date: Tue, 19 Jun 2007 14:16:15 +0200 Subject: HID: fix autocentering of PID devices When setting the autocentering of PID devices, PID_DIRECTION_ENABLE is not being explicitely set to 1. This results in autocentering working only on the vertical axis when this field is preset to 0. Fix that by setting it explicitely to 1 when preparing the set_effect report for autocentering spring effect. Signed-off-by: Diogo Kastrup Signed-off-by: Anssi Hannula Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-pidff.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index f5a90e950e6..011326178c0 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -738,6 +738,7 @@ static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude) pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = 0; pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0; pidff_set(&pidff->set_effect[PID_GAIN], magnitude); + pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; pidff->set_effect[PID_START_DELAY].value[0] = 0; usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], -- cgit v1.2.3 From 5f9c464aaa1ba3a773c47004e98eb1f3aa2ab2a4 Mon Sep 17 00:00:00 2001 From: Ryo Dairiki Date: Mon, 25 Jun 2007 10:31:12 +0200 Subject: HID: support for logitech cordless desktop LX500 special mapping This keyboard has wireless mouse which has left, middle, right buttons and 2-dimensional scrolling wheel. Unfornetuly, this wheel reports side scrolling events and 11 or 12 button events at the same time. I've wrote a patch to fix this mapping. I'm not sure if this mapping is proper for buttons, because , for example, there is no entry for "burn cd" in input.h. The patch also supress 11 and 12 button events from mouse when you scroll the wheel left and right. With this patch, only side scrolling events are reported. (This mouse has only 4 buttons and 2D wheel. There is no such buttons like 11 and 12.) Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 28 ++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-quirks.c | 3 +++ include/linux/hid.h | 2 ++ 3 files changed, 33 insertions(+) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 1b8b3334140..60de16a83c3 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -60,6 +60,19 @@ static const unsigned char hid_keyboard[256] = { 150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk }; +/* extended mapping for certain Logitech hardware (Logitech cordless desktop LX500) */ +#define LOGITECH_EXPANDED_KEYMAP_SIZE 80 +static int logitech_expanded_keymap[LOGITECH_EXPANDED_KEYMAP_SIZE] = { + 0,216, 0,213,175,156, 0, 0, 0, 0, + 144, 0, 0, 0, 0, 0, 0, 0, 0,212, + 174,167,152,161,112, 0, 0, 0,154, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,183,184,185,186,187, + 188,189,190,191,192,193,194, 0, 0, 0 +}; + static const struct { __s32 x; __s32 y; @@ -378,6 +391,21 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } } + /* Special handling for Logitech Cordless Desktop */ + if (field->application != HID_GD_MOUSE) { + if (device->quirks & HID_QUIRK_LOGITECH_EXPANDED_KEYMAP) { + int hid = usage->hid & HID_USAGE; + if (hid < LOGITECH_EXPANDED_KEYMAP_SIZE && logitech_expanded_keymap[hid] != 0) + code = logitech_expanded_keymap[hid]; + } + } else { + if (device->quirks & HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL) { + int hid = usage->hid & HID_USAGE; + if (hid == 7 || hid == 8) + goto ignore; + } + } + map_key(code); break; diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index a75c236fcaf..5614a70f564 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -201,6 +201,7 @@ #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_S510_RECEIVER 0xc50c #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 +#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 #define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 #define USB_DEVICE_ID_DINOVO_EDGE 0xc714 @@ -422,6 +423,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, diff --git a/include/linux/hid.h b/include/linux/hid.h index 4daf5eea78a..44b64f0d055 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -274,6 +274,8 @@ struct hid_item { #define HID_QUIRK_DUPLICATE_USAGES 0x00080000 #define HID_QUIRK_RESET_LEDS 0x00100000 #define HID_QUIRK_HIDINPUT 0x00200000 +#define HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL 0x00400000 +#define HID_QUIRK_LOGITECH_EXPANDED_KEYMAP 0x00800000 /* * Separate quirks for runtime report descriptor fixup -- cgit v1.2.3 From 3c5f4b25f3552c9bcb52eb956753a3c8140480b7 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 28 Jun 2007 22:33:47 +0200 Subject: HID: Logitech keyboard 0xc311 needs reset leds quirk The keyboard 0x046d/0xc311 needs reset leds quirk Tested-by: Antonino Ingargiola Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 5614a70f564..ce6c970b597 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -199,6 +199,7 @@ #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 +#define USB_DEVICE_ID_LOGITECH_KBD 0xc311 #define USB_DEVICE_ID_S510_RECEIVER 0xc50c #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 #define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 @@ -461,6 +462,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD, HID_QUIRK_RESET_LEDS }, { 0, 0 } }; -- cgit v1.2.3 From 2c1d8aea2ca76df1b1de2aed23e3ceda2a044ed1 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 4 Jul 2007 16:45:59 +0200 Subject: HID: handle cases of volume knobs generating relative values There are some devices (for example Dell Multimedia Keyboard SK-8135) that have a volume control knob which generates relative events instead of absolute. hid-input maps them to ABS_VOLUME. HUT pages don't restrict volume to absolute values. Adding REL_VOLUME doesn't seem feasible, nothing knows how to handle it. This patch translates relative ABS_VOLUME events into appropriate number of series of VOLUME_UP or VOLUME_DOWN events respectively, so that userspace sees the correct values in the end. kernel.org bugzilla 5233 Reported-by: Jochen Eisinger Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 60de16a83c3..bf7e307503c 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -882,6 +882,16 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel field->dpad = usage->code; } + /* for those devices which produce Consumer volume usage as relative, + * we emulate pressing volumeup/volumedown appropriate number of times + * in hidinput_hid_event() + */ + if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) && + (usage->code == ABS_VOLUME)) { + set_bit(KEY_VOLUMEUP, input->keybit); + set_bit(KEY_VOLUMEDOWN, input->keybit); + } + hid_resolv_event(usage->type, usage->code); #ifdef CONFIG_HID_DEBUG printk("\n"); @@ -972,6 +982,21 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ return; + if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) && + (usage->code == ABS_VOLUME)) { + int count = abs(value); + int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN; + int i; + + for (i = 0; i < count; i++) { + input_event(input, EV_KEY, direction, 1); + input_sync(input); + input_event(input, EV_KEY, direction, 0); + input_sync(input); + } + return; + } + input_event(input, usage->type, usage->code, value); if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY)) -- cgit v1.2.3