From ddf7540e9c3a3d65739daa339c8838fa39cf2758 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 12 Jul 2013 11:01:02 +0200 Subject: HID: logitech-dj: use inlined helpers hid_hw_open/close Use the inlined helpers hid_hw_open/close instead of direct calls to ->ll_driver->open() and ->ll_driver->close(). Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 5207591a598c..db3192b24e6e 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -756,10 +756,10 @@ static int logi_dj_probe(struct hid_device *hdev, } /* This is enabling the polling urb on the IN endpoint */ - retval = hdev->ll_driver->open(hdev); + retval = hid_hw_open(hdev); if (retval < 0) { - dev_err(&hdev->dev, "%s:hdev->ll_driver->open returned " - "error:%d\n", __func__, retval); + dev_err(&hdev->dev, "%s:hid_hw_open returned error:%d\n", + __func__, retval); goto llopen_failed; } @@ -776,7 +776,7 @@ static int logi_dj_probe(struct hid_device *hdev, return retval; logi_dj_recv_query_paired_devices_failed: - hdev->ll_driver->close(hdev); + hid_hw_close(hdev); llopen_failed: switch_to_dj_mode_fail: @@ -818,7 +818,7 @@ static void logi_dj_remove(struct hid_device *hdev) cancel_work_sync(&djrcv_dev->work); - hdev->ll_driver->close(hdev); + hid_hw_close(hdev); hid_hw_stop(hdev); /* I suppose that at this point the only context that can access -- cgit v1.2.3-59-g8ed1b From 38ead6ef1d94e782bec49002ff65f2bdaddfeb15 Mon Sep 17 00:00:00 2001 From: Paul Chavent Date: Sun, 7 Jul 2013 17:43:56 +0200 Subject: HID: core: fix hid delimiter local tag parsing. When device with the DELIMITER tag in its report descriptor is encountered during parsing, it's mistakenly immediately refused by HID core for no justifiable reason. [jkosina@suse.cz: polish changelog] Signed-off-by: Paul Chavent Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index e39dac68063c..8de5cb8319b9 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -450,7 +450,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) } parser->local.delimiter_depth--; } - return 1; + return 0; case HID_LOCAL_ITEM_TAG_USAGE: -- cgit v1.2.3-59-g8ed1b From 0adb9c2c5ed42f199cb2a630c37d18dee385fae2 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 15 Jul 2013 10:12:18 +0200 Subject: HID: kye: Add report fixup for Genius Gx Imperator Keyboard Genius Gx Imperator Keyboard presents the same problem in its report descriptors than Genius Gila Gaming Mouse. Use the same fixup for both. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=928561 Reported-and-tested-by: Honza Brazdil Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-kye.c | 45 ++++++++++++++++++++++++++++----------------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8de5cb8319b9..b0f2f459f59b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1594,6 +1594,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GX_IMPERATOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c5aea29f164f..02885319c10a 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -479,6 +479,7 @@ #define USB_VENDOR_ID_KYE 0x0458 #define USB_DEVICE_ID_KYE_ERGO_525V 0x0087 #define USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE 0x0138 +#define USB_DEVICE_ID_GENIUS_GX_IMPERATOR 0x4018 #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 #define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010 #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index 1e2ee2aa84a0..73845120295e 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -268,6 +268,26 @@ static __u8 easypen_m610x_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +static __u8 *kye_consumer_control_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize, int offset, const char *device_name) { + /* + * the fixup that need to be done: + * - change Usage Maximum in the Comsumer Control + * (report ID 3) to a reasonable value + */ + if (*rsize >= offset + 31 && + /* Usage Page (Consumer Devices) */ + rdesc[offset] == 0x05 && rdesc[offset + 1] == 0x0c && + /* Usage (Consumer Control) */ + rdesc[offset + 2] == 0x09 && rdesc[offset + 3] == 0x01 && + /* Usage Maximum > 12287 */ + rdesc[offset + 10] == 0x2a && rdesc[offset + 12] > 0x2f) { + hid_info(hdev, "fixing up %s report descriptor\n", device_name); + rdesc[offset + 12] = 0x2f; + } + return rdesc; +} + static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { @@ -315,23 +335,12 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, } break; case USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE: - /* - * the fixup that need to be done: - * - change Usage Maximum in the Comsumer Control - * (report ID 3) to a reasonable value - */ - if (*rsize >= 135 && - /* Usage Page (Consumer Devices) */ - rdesc[104] == 0x05 && rdesc[105] == 0x0c && - /* Usage (Consumer Control) */ - rdesc[106] == 0x09 && rdesc[107] == 0x01 && - /* Usage Maximum > 12287 */ - rdesc[114] == 0x2a && rdesc[116] > 0x2f) { - hid_info(hdev, - "fixing up Genius Gila Gaming Mouse " - "report descriptor\n"); - rdesc[116] = 0x2f; - } + rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 104, + "Genius Gila Gaming Mouse"); + break; + case USB_DEVICE_ID_GENIUS_GX_IMPERATOR: + rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 83, + "Genius Gx Imperator Keyboard"); break; } return rdesc; @@ -428,6 +437,8 @@ static const struct hid_device_id kye_devices[] = { USB_DEVICE_ID_KYE_EASYPEN_M610X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, + USB_DEVICE_ID_GENIUS_GX_IMPERATOR) }, { } }; MODULE_DEVICE_TABLE(hid, kye_devices); -- cgit v1.2.3-59-g8ed1b From 27ce405039bfe6d3f4143415c638f56a3df77dca Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 10 Jul 2013 19:56:27 +0200 Subject: HID: fix data access in implement() implement() is setting bytes in LE data stream. In case the data is not aligned to 64bits, it reads past the allocated buffer. It doesn't really change any value there (it's properly bitmasked), but in case that this read past the boundary hits a page boundary, pagefault happens when accessing 64bits of 'x' in implement(), and kernel oopses. This happens much more often when numbered reports are in use, as the initial 8bit skip in the buffer makes the whole process work on values which are not aligned to 64bits. This problem dates back to attempts in 2005 and 2006 to make implement() and extract() as generic as possible, and even back then the problem was realized by Adam Kroperlin, but falsely assumed to be impossible to cause any harm: http://www.mail-archive.com/linux-usb-devel@lists.sourceforge.net/msg47690.html I have made several attempts at fixing it "on the spot" directly in implement(), but the results were horrible; the special casing for processing last 64bit chunk and switching to different math makes it unreadable mess. I therefore took a path to allocate a few bytes more which will never make it into final report, but are there as a cushion for all the 64bit math operations happening in implement() and extract(). All callers of hid_output_report() are converted at the same time to allocate the buffer by newly introduced hid_alloc_report_buf() helper. Bruno noticed that the whole raw_size test can be dropped as well, as hid_alloc_report_buf() makes sure that the buffer is always of a proper size. Reviewed-by: Benjamin Tissoires Acked-by: Gustavo Padovan Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 19 ++++++++++++++++++- drivers/hid/hid-logitech-dj.c | 12 ++++++++++-- drivers/hid/hid-picolcd_debugfs.c | 23 ++++++++++++----------- drivers/hid/usbhid/hid-core.c | 5 ++--- include/linux/hid.h | 1 + net/bluetooth/hidp/core.c | 14 +++++++++----- 6 files changed, 52 insertions(+), 22 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b0f2f459f59b..a1b248cea5b0 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1128,7 +1128,8 @@ static void hid_output_field(const struct hid_device *hid, } /* - * Create a report. + * Create a report. 'data' has to be allocated using + * hid_alloc_report_buf() so that it has proper size. */ void hid_output_report(struct hid_report *report, __u8 *data) @@ -1144,6 +1145,22 @@ void hid_output_report(struct hid_report *report, __u8 *data) } EXPORT_SYMBOL_GPL(hid_output_report); +/* + * Allocator for buffer that is going to be passed to hid_output_report() + */ +u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) +{ + /* + * 7 extra bytes are necessary to achieve proper functionality + * of implement() working on 8 byte chunks + */ + + int len = ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7; + + return kmalloc(len, flags); +} +EXPORT_SYMBOL_GPL(hid_alloc_report_buf); + /* * Set a field value. The report this field belongs to has to be * created and transferred to the device, to set this value in the diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 5207591a598c..4d792739dbd1 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -574,7 +574,7 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, struct hid_field *field; struct hid_report *report; - unsigned char data[8]; + unsigned char *data; int offset; dbg_hid("%s: %s, type:%d | code:%d | value:%d\n", @@ -590,6 +590,13 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, return -1; } hid_set_field(field, offset, value); + + data = hid_alloc_report_buf(field->report, GFP_KERNEL); + if (!data) { + dev_warn(&dev->dev, "failed to allocate report buf memory\n"); + return -1; + } + hid_output_report(field->report, &data[0]); output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT]; @@ -600,8 +607,9 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT); - return 0; + kfree(data); + return 0; } static int logi_dj_ll_start(struct hid_device *hid) diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c index 59ab8e157e6b..024cdf3c2297 100644 --- a/drivers/hid/hid-picolcd_debugfs.c +++ b/drivers/hid/hid-picolcd_debugfs.c @@ -394,7 +394,7 @@ static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data, void picolcd_debug_out_report(struct picolcd_data *data, struct hid_device *hdev, struct hid_report *report) { - u8 raw_data[70]; + u8 *raw_data; int raw_size = (report->size >> 3) + 1; char *buff; #define BUFF_SZ 256 @@ -407,20 +407,20 @@ void picolcd_debug_out_report(struct picolcd_data *data, if (!buff) return; - snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", - report->id, raw_size); - hid_debug_event(hdev, buff); - if (raw_size + 5 > sizeof(raw_data)) { + raw_data = hid_alloc_report_buf(report, GFP_ATOMIC); + if (!raw_data) { kfree(buff); - hid_debug_event(hdev, " TOO BIG\n"); return; - } else { - raw_data[0] = report->id; - hid_output_report(report, raw_data); - dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); - hid_debug_event(hdev, buff); } + snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", + report->id, raw_size); + hid_debug_event(hdev, buff); + raw_data[0] = report->id; + hid_output_report(report, raw_data); + dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); + hid_debug_event(hdev, buff); + switch (report->id) { case REPORT_LED_STATE: /* 1 data byte with GPO state */ @@ -644,6 +644,7 @@ void picolcd_debug_out_report(struct picolcd_data *data, break; } wake_up_interruptible(&hdev->debug_wait); + kfree(raw_data); kfree(buff); } diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 99418285222c..ada164e1b3a1 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -535,7 +535,6 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re { int head; struct usbhid_device *usbhid = hid->driver_data; - int len = ((report->size - 1) >> 3) + 1 + (report->id > 0); if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) return; @@ -546,7 +545,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re return; } - usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC); + usbhid->out[usbhid->outhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC); if (!usbhid->out[usbhid->outhead].raw_report) { hid_warn(hid, "output queueing failed\n"); return; @@ -595,7 +594,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re } if (dir == USB_DIR_OUT) { - usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC); + usbhid->ctrl[usbhid->ctrlhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC); if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) { hid_warn(hid, "control queueing failed\n"); return; diff --git a/include/linux/hid.h b/include/linux/hid.h index 0c48991b0402..acccdf4eb485 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -744,6 +744,7 @@ struct hid_field *hidinput_get_led_field(struct hid_device *hid); unsigned int hidinput_count_leds(struct hid_device *hid); __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code); void hid_output_report(struct hid_report *report, __u8 *data); +u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags); struct hid_device *hid_allocate_device(void); struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 46c6a148f0b3..212980ff99b9 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -231,17 +231,21 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) static int hidp_send_report(struct hidp_session *session, struct hid_report *report) { - unsigned char buf[32], hdr; - int rsize; + unsigned char hdr; + u8 *buf; + int rsize, ret; - rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0); - if (rsize > sizeof(buf)) + buf = hid_alloc_report_buf(report, GFP_ATOMIC); + if (!buf) return -EIO; hid_output_report(report, buf); hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; - return hidp_send_intr_message(session, hdr, buf, rsize); + ret = hidp_send_intr_message(session, hdr, buf, rsize); + + kfree(buf); + return ret; } static int hidp_get_raw_report(struct hid_device *hid, -- cgit v1.2.3-59-g8ed1b From bc197eedef1ae082ec662c64c3f4aa302821fb7a Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 22 Jul 2013 17:11:44 +0200 Subject: HID: fix unused rsize usage 27ce4050 ("HID: fix data access in implement()") by mistake removed a setting of buffer size in hidp. Fix that by putting it back. Reported-by: kbuild test robot Signed-off-by: Jiri Kosina --- net/bluetooth/hidp/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 212980ff99b9..2e658ade4454 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -242,6 +242,7 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep hid_output_report(report, buf); hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; + rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0); ret = hidp_send_intr_message(session, hdr, buf, rsize); kfree(buf); -- cgit v1.2.3-59-g8ed1b From dfc450b55d6b9215da27c5dc2c5f3ca1865575a6 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 19 Jul 2013 15:53:16 +0900 Subject: HID: replace strict_strtoul() with kstrtoul() The usage of strict_strtoul() is not preferred, because strict_strtoul() is obsolete. Thus, kstrtoul() should be used. Signed-off-by: Jingoo Han Signed-off-by: Jiri Kosina --- drivers/hid/hid-magicmouse.c | 2 +- drivers/hid/hid-ntrig.c | 12 ++++++------ drivers/hid/hid-roccat-arvo.c | 6 +++--- drivers/hid/hid-roccat-isku.c | 2 +- drivers/hid/hid-roccat-kone.c | 4 ++-- drivers/hid/hid-roccat-koneplus.c | 2 +- drivers/hid/hid-roccat-kovaplus.c | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 5bc37343eb22..a32f5a24b27c 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -36,7 +36,7 @@ MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel"); static unsigned int scroll_speed = 32; static int param_set_scroll_speed(const char *val, struct kernel_param *kp) { unsigned long speed; - if (!val || strict_strtoul(val, 0, &speed) || speed > 63) + if (!val || kstrtoul(val, 0, &speed) || speed > 63) return -EINVAL; scroll_speed = speed; return 0; diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index ef95102515e4..98d1fdf7d8cd 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -237,7 +237,7 @@ static ssize_t set_min_width(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val > nd->sensor_physical_width) @@ -272,7 +272,7 @@ static ssize_t set_min_height(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val > nd->sensor_physical_height) @@ -306,7 +306,7 @@ static ssize_t set_activate_slack(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val > 0x7f) @@ -341,7 +341,7 @@ static ssize_t set_activation_width(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val > nd->sensor_physical_width) @@ -377,7 +377,7 @@ static ssize_t set_activation_height(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val > nd->sensor_physical_height) @@ -411,7 +411,7 @@ static ssize_t set_deactivate_slack(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; /* diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index 327f9b8ed1f4..071ee9e2fd9f 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -59,7 +59,7 @@ static ssize_t arvo_sysfs_set_mode_key(struct device *dev, unsigned long state; int retval; - retval = strict_strtoul(buf, 10, &state); + retval = kstrtoul(buf, 10, &state); if (retval) return retval; @@ -107,7 +107,7 @@ static ssize_t arvo_sysfs_set_key_mask(struct device *dev, unsigned long key_mask; int retval; - retval = strict_strtoul(buf, 10, &key_mask); + retval = kstrtoul(buf, 10, &key_mask); if (retval) return retval; @@ -159,7 +159,7 @@ static ssize_t arvo_sysfs_set_actual_profile(struct device *dev, unsigned long profile; int retval; - retval = strict_strtoul(buf, 10, &profile); + retval = kstrtoul(buf, 10, &profile); if (retval) return retval; diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 8023751d5257..5dd0ea4eb4f7 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -82,7 +82,7 @@ static ssize_t isku_sysfs_set_actual_profile(struct device *dev, isku = hid_get_drvdata(dev_get_drvdata(dev)); usb_dev = interface_to_usbdev(to_usb_interface(dev)); - retval = strict_strtoul(buf, 10, &profile); + retval = kstrtoul(buf, 10, &profile); if (retval) return retval; diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 7fae070788fa..00ab287f7384 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -456,7 +456,7 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev, kone = hid_get_drvdata(dev_get_drvdata(dev)); usb_dev = interface_to_usbdev(to_usb_interface(dev)); - retval = strict_strtoul(buf, 10, &state); + retval = kstrtoul(buf, 10, &state); if (retval) return retval; @@ -545,7 +545,7 @@ static ssize_t kone_sysfs_set_startup_profile(struct device *dev, kone = hid_get_drvdata(dev_get_drvdata(dev)); usb_dev = interface_to_usbdev(to_usb_interface(dev)); - retval = strict_strtoul(buf, 10, &new_startup_profile); + retval = kstrtoul(buf, 10, &new_startup_profile); if (retval) return retval; diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 6a48fa3c7da9..26b9663ddf47 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -246,7 +246,7 @@ static ssize_t koneplus_sysfs_set_actual_profile(struct device *dev, koneplus = hid_get_drvdata(dev_get_drvdata(dev)); usb_dev = interface_to_usbdev(to_usb_interface(dev)); - retval = strict_strtoul(buf, 10, &profile); + retval = kstrtoul(buf, 10, &profile); if (retval) return retval; diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index b8b37789b864..c2a17e45c99c 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -282,7 +282,7 @@ static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev, kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); usb_dev = interface_to_usbdev(to_usb_interface(dev)); - retval = strict_strtoul(buf, 10, &profile); + retval = kstrtoul(buf, 10, &profile); if (retval) return retval; -- cgit v1.2.3-59-g8ed1b From cb2c9e3f92480a292670e2cc261723ce4de8059e Mon Sep 17 00:00:00 2001 From: Olivier Scherler Date: Sat, 27 Jul 2013 19:20:02 +0200 Subject: HID: Add new driver for non-compliant Xin-Mo devices. The driver currently only supports the Dual Arcade controller. It fixes the negative axis event values (the devices sends -2) to match the logical axis minimum of the HID report descriptor (the report announces -1). It is needed because hid-input discards out of bounds values. Signed-off-by: Olivier Scherler Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 8 ++++++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 +++ drivers/hid/hid-xinmo.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 drivers/hid/hid-xinmo.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 14ef6ab69790..3d7c9f67b6d7 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -743,6 +743,14 @@ config HID_WIIMOTE To compile this driver as a module, choose M here: the module will be called hid-wiimote. +config HID_XINMO + tristate "Xin-Mo non-fully compliant devices" + depends on HID + ---help--- + Support for Xin-Mo devices that are not fully compliant with the HID + standard. Currently only supports the Xin-Mo Dual Arcade. Say Y here + if you have a Xin-Mo Dual Arcade controller. + config HID_ZEROPLUS tristate "Zeroplus based game controller support" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 6f687287e212..a959f4aecaf5 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -110,6 +110,7 @@ obj-$(CONFIG_HID_TIVO) += hid-tivo.o obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o +obj-$(CONFIG_HID_XINMO) += hid-xinmo.o obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o obj-$(CONFIG_HID_WACOM) += hid-wacom.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index a1b248cea5b0..627fea6593a3 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1751,6 +1751,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 02885319c10a..83622f89fa4b 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -885,6 +885,9 @@ #define USB_VENDOR_ID_XAT 0x2505 #define USB_DEVICE_ID_XAT_CSR 0x0220 +#define USB_VENDOR_ID_XIN_MO 0x16c0 +#define USB_DEVICE_ID_XIN_MO_DUAL_ARCADE 0x05e1 + #define USB_VENDOR_ID_XIROKU 0x1477 #define USB_DEVICE_ID_XIROKU_SPX 0x1006 #define USB_DEVICE_ID_XIROKU_MPX 0x1007 diff --git a/drivers/hid/hid-xinmo.c b/drivers/hid/hid-xinmo.c new file mode 100644 index 000000000000..6153e50d9721 --- /dev/null +++ b/drivers/hid/hid-xinmo.c @@ -0,0 +1,72 @@ +/* + * HID driver for Xin-Mo devices, currently only the Dual Arcade controller. + * Fixes the negative axis event values (the devices sends -2) to match the + * logical axis minimum of the HID report descriptor (the report announces + * -1). It is needed because hid-input discards out of bounds values. + * (This module is based on "hid-saitek" and "hid-lg".) + * + * Copyright (c) 2013 Olivier Scherler + */ + +/* + * 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, or (at your option) + * any later version. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +/* + * Fix negative events that are out of bounds. + */ +static int xinmo_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + switch (usage->code) { + case ABS_X: + case ABS_Y: + case ABS_Z: + case ABS_RX: + if (value < -1) { + input_event(field->hidinput->input, usage->type, + usage->code, -1); + return 1; + } + break; + } + + return 0; +} + +static const struct hid_device_id xinmo_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, xinmo_devices); + +static struct hid_driver xinmo_driver = { + .name = "xinmo", + .id_table = xinmo_devices, + .event = xinmo_event +}; + +static int __init xinmo_init(void) +{ + return hid_register_driver(&xinmo_driver); +} + +static void __exit xinmo_exit(void) +{ + hid_unregister_driver(&xinmo_driver); +} + +module_init(xinmo_init); +module_exit(xinmo_exit); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From abf832bfc349b54fd500f1e3b612f7f3cd9dfcc6 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 24 Jul 2013 19:38:04 +0200 Subject: HID: trivial devm conversion for special hid drivers It is safe to use devres allocation within the hid subsystem: - the devres release is called _after_ the call to .remove(), meaning that no freed pointers will exists while removing the device - if a .probe() fails, devres releases all the allocated ressources before going to the next driver: there will not be ghost ressources attached to a hid device if several drivers are probed. Given that, we can clean up a little some of the HID drivers. These ones are trivial: - there is only one kzalloc in the driver - the .remove() callback contains only one kfree on top of hid_hw_stop() - the error path in the probe is easy enough to be manually checked Signed-off-by: Benjamin Tissoires Reviewed-by: Andy Shevchenko Signed-off-by: Jiri Kosina --- drivers/hid/hid-a4tech.c | 21 ++++----------------- drivers/hid/hid-apple.c | 16 +++------------- drivers/hid/hid-magicmouse.c | 17 +++-------------- drivers/hid/hid-sony.c | 9 +++------ drivers/hid/hid-zydacron.c | 19 +++---------------- 5 files changed, 16 insertions(+), 66 deletions(-) diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index 7c5507e94820..9428ea7cdf8a 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c @@ -90,11 +90,10 @@ static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id) struct a4tech_sc *a4; int ret; - a4 = kzalloc(sizeof(*a4), GFP_KERNEL); + a4 = devm_kzalloc(&hdev->dev, sizeof(*a4), GFP_KERNEL); if (a4 == NULL) { hid_err(hdev, "can't alloc device descriptor\n"); - ret = -ENOMEM; - goto err_free; + return -ENOMEM; } a4->quirks = id->driver_data; @@ -104,27 +103,16 @@ static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err_free; + return ret; } ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); - goto err_free; + return ret; } return 0; -err_free: - kfree(a4); - return ret; -} - -static void a4_remove(struct hid_device *hdev) -{ - struct a4tech_sc *a4 = hid_get_drvdata(hdev); - - hid_hw_stop(hdev); - kfree(a4); } static const struct hid_device_id a4_devices[] = { @@ -144,7 +132,6 @@ static struct hid_driver a4_driver = { .input_mapped = a4_input_mapped, .event = a4_event, .probe = a4_probe, - .remove = a4_remove, }; module_hid_driver(a4_driver); diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index feae88b53fcd..bad40b9315b2 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -349,7 +349,7 @@ static int apple_probe(struct hid_device *hdev, unsigned int connect_mask = HID_CONNECT_DEFAULT; int ret; - asc = kzalloc(sizeof(*asc), GFP_KERNEL); + asc = devm_kzalloc(&hdev->dev, sizeof(*asc), GFP_KERNEL); if (asc == NULL) { hid_err(hdev, "can't alloc apple descriptor\n"); return -ENOMEM; @@ -362,7 +362,7 @@ static int apple_probe(struct hid_device *hdev, ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err_free; + return ret; } if (quirks & APPLE_HIDDEV) @@ -373,19 +373,10 @@ static int apple_probe(struct hid_device *hdev, ret = hid_hw_start(hdev, connect_mask); if (ret) { hid_err(hdev, "hw start failed\n"); - goto err_free; + return ret; } return 0; -err_free: - kfree(asc); - return ret; -} - -static void apple_remove(struct hid_device *hdev) -{ - hid_hw_stop(hdev); - kfree(hid_get_drvdata(hdev)); } static const struct hid_device_id apple_devices[] = { @@ -545,7 +536,6 @@ static struct hid_driver apple_driver = { .id_table = apple_devices, .report_fixup = apple_report_fixup, .probe = apple_probe, - .remove = apple_remove, .event = apple_event, .input_mapping = apple_input_mapping, .input_mapped = apple_input_mapped, diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 5bc37343eb22..d393eb7ddaf0 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -484,7 +484,7 @@ static int magicmouse_probe(struct hid_device *hdev, struct hid_report *report; int ret; - msc = kzalloc(sizeof(*msc), GFP_KERNEL); + msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL); if (msc == NULL) { hid_err(hdev, "can't alloc magicmouse descriptor\n"); return -ENOMEM; @@ -498,13 +498,13 @@ static int magicmouse_probe(struct hid_device *hdev, ret = hid_parse(hdev); if (ret) { hid_err(hdev, "magicmouse hid parse failed\n"); - goto err_free; + return ret; } ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "magicmouse hw start failed\n"); - goto err_free; + return ret; } if (!msc->input) { @@ -548,19 +548,9 @@ static int magicmouse_probe(struct hid_device *hdev, return 0; err_stop_hw: hid_hw_stop(hdev); -err_free: - kfree(msc); return ret; } -static void magicmouse_remove(struct hid_device *hdev) -{ - struct magicmouse_sc *msc = hid_get_drvdata(hdev); - - hid_hw_stop(hdev); - kfree(msc); -} - static const struct hid_device_id magic_mice[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 }, @@ -574,7 +564,6 @@ static struct hid_driver magicmouse_driver = { .name = "magicmouse", .id_table = magic_mice, .probe = magicmouse_probe, - .remove = magicmouse_remove, .raw_event = magicmouse_raw_event, .input_mapping = magicmouse_input_mapping, .input_configured = magicmouse_input_configured, diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index ecbc74923d06..f9898aad72fa 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -623,7 +623,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) struct sony_sc *sc; unsigned int connect_mask = HID_CONNECT_DEFAULT; - sc = kzalloc(sizeof(*sc), GFP_KERNEL); + sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); if (sc == NULL) { hid_err(hdev, "can't alloc sony descriptor\n"); return -ENOMEM; @@ -635,7 +635,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err_free; + return ret; } if (sc->quirks & VAIO_RDESC_CONSTANT) @@ -648,7 +648,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_hw_start(hdev, connect_mask); if (ret) { hid_err(hdev, "hw start failed\n"); - goto err_free; + return ret; } if (sc->quirks & SIXAXIS_CONTROLLER_USB) { @@ -668,8 +668,6 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) return 0; err_stop: hid_hw_stop(hdev); -err_free: - kfree(sc); return ret; } @@ -681,7 +679,6 @@ static void sony_remove(struct hid_device *hdev) buzz_remove(hdev); hid_hw_stop(hdev); - kfree(sc); } static const struct hid_device_id sony_devices[] = { diff --git a/drivers/hid/hid-zydacron.c b/drivers/hid/hid-zydacron.c index e4cddeccd6b5..1a660bd97ab2 100644 --- a/drivers/hid/hid-zydacron.c +++ b/drivers/hid/hid-zydacron.c @@ -169,7 +169,7 @@ static int zc_probe(struct hid_device *hdev, const struct hid_device_id *id) int ret; struct zc_device *zc; - zc = kzalloc(sizeof(*zc), GFP_KERNEL); + zc = devm_kzalloc(&hdev->dev, sizeof(*zc), GFP_KERNEL); if (zc == NULL) { hid_err(hdev, "can't alloc descriptor\n"); return -ENOMEM; @@ -180,28 +180,16 @@ static int zc_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err_free; + return ret; } ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); - goto err_free; + return ret; } return 0; -err_free: - kfree(zc); - - return ret; -} - -static void zc_remove(struct hid_device *hdev) -{ - struct zc_device *zc = hid_get_drvdata(hdev); - - hid_hw_stop(hdev); - kfree(zc); } static const struct hid_device_id zc_devices[] = { @@ -217,7 +205,6 @@ static struct hid_driver zc_driver = { .input_mapping = zc_input_mapping, .raw_event = zc_raw_event, .probe = zc_probe, - .remove = zc_remove, }; module_hid_driver(zc_driver); -- cgit v1.2.3-59-g8ed1b From c08d46aa805ba46d501f610c2448d07bea979780 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 24 Jul 2013 19:38:05 +0200 Subject: HID: multitouch: devm conversion HID special drivers can use safely the devres API. Use it to remove 25 lines of code and to clean up a little the error paths. Besides the basic kzalloc -> devm_kzalloc conversions, I changed the place of the allocation of the new name. Doing this right in mt_input_configured() removes the kstrdup call which was not very helpful and the new way is simpler to understand (and to debug). Reviewed-by: Andy Shevchenko Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 71 ++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index cb0e361d7a4b..0fe00e2552f2 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -261,17 +261,6 @@ static struct mt_class mt_classes[] = { { } }; -static void mt_free_input_name(struct hid_input *hi) -{ - struct hid_device *hdev = hi->report->device; - const char *name = hi->input->name; - - if (name != hdev->name) { - hi->input->name = hdev->name; - kfree(name); - } -} - static ssize_t mt_show_quirks(struct device *dev, struct device_attribute *attr, char *buf) @@ -415,13 +404,6 @@ static void mt_pen_report(struct hid_device *hid, struct hid_report *report) static void mt_pen_input_configured(struct hid_device *hdev, struct hid_input *hi) { - char *name = kzalloc(strlen(hi->input->name) + 5, GFP_KERNEL); - if (name) { - sprintf(name, "%s Pen", hi->input->name); - mt_free_input_name(hi); - hi->input->name = name; - } - /* force BTN_STYLUS to allow tablet matching in udev */ __set_bit(BTN_STYLUS, hi->input->keybit); } @@ -928,16 +910,26 @@ static void mt_post_parse(struct mt_device *td) static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); - char *name = kstrdup(hdev->name, GFP_KERNEL); - - if (name) - hi->input->name = name; + char *name; + const char *suffix = NULL; if (hi->report->id == td->mt_report_id) mt_touch_input_configured(hdev, hi); - if (hi->report->id == td->pen_report_id) + if (hi->report->field[0]->physical == HID_DG_STYLUS) { + suffix = "Pen"; mt_pen_input_configured(hdev, hi); + } + + if (suffix) { + name = devm_kzalloc(&hi->input->dev, + strlen(hdev->name) + strlen(suffix) + 2, + GFP_KERNEL); + if (name) { + sprintf(name, "%s %s", hdev->name, suffix); + hi->input->name = name; + } + } } static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) @@ -945,7 +937,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) int ret, i; struct mt_device *td; struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */ - struct hid_input *hi; for (i = 0; mt_classes[i].name ; i++) { if (id->driver_data == mt_classes[i].name) { @@ -967,7 +958,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) hdev->quirks |= HID_QUIRK_MULTI_INPUT; hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT; - td = kzalloc(sizeof(struct mt_device), GFP_KERNEL); + td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL); if (!td) { dev_err(&hdev->dev, "cannot allocate multitouch data\n"); return -ENOMEM; @@ -980,11 +971,11 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) td->pen_report_id = -1; hid_set_drvdata(hdev, td); - td->fields = kzalloc(sizeof(struct mt_fields), GFP_KERNEL); + td->fields = devm_kzalloc(&hdev->dev, sizeof(struct mt_fields), + GFP_KERNEL); if (!td->fields) { dev_err(&hdev->dev, "cannot allocate multitouch fields data\n"); - ret = -ENOMEM; - goto fail; + return -ENOMEM; } if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID) @@ -992,29 +983,22 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_parse(hdev); if (ret != 0) - goto fail; + return ret; ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) - goto hid_fail; + return ret; ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); mt_set_maxcontacts(hdev); mt_set_input_mode(hdev); - kfree(td->fields); + /* release .fields memory as it is not used anymore */ + devm_kfree(&hdev->dev, td->fields); td->fields = NULL; return 0; - -hid_fail: - list_for_each_entry(hi, &hdev->inputs, list) - mt_free_input_name(hi); -fail: - kfree(td->fields); - kfree(td); - return ret; } #ifdef CONFIG_PM @@ -1039,17 +1023,8 @@ static int mt_resume(struct hid_device *hdev) static void mt_remove(struct hid_device *hdev) { - struct mt_device *td = hid_get_drvdata(hdev); - struct hid_input *hi; - sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); - list_for_each_entry(hi, &hdev->inputs, list) - mt_free_input_name(hi); - hid_hw_stop(hdev); - - kfree(td); - hid_set_drvdata(hdev, NULL); } static const struct hid_device_id mt_devices[] = { -- cgit v1.2.3-59-g8ed1b From ddf64a3c03d4d68431146a0f1622844cc6cb6c22 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 15 Jul 2013 19:10:10 +0200 Subject: HID: usbhid: make usbhid_set_leds() static usbhid_set_leds() is only used inside of usbhid/hid-core.c so no need to export it. Signed-off-by: David Herrmann Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 3 +-- include/linux/hid.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 99418285222c..5482bf447688 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -857,7 +857,7 @@ static int hid_find_field_early(struct hid_device *hid, unsigned int page, return -1; } -void usbhid_set_leds(struct hid_device *hid) +static void usbhid_set_leds(struct hid_device *hid) { struct hid_field *field; int offset; @@ -867,7 +867,6 @@ void usbhid_set_leds(struct hid_device *hid) usbhid_submit_report(hid, field->report, USB_DIR_OUT); } } -EXPORT_SYMBOL_GPL(usbhid_set_leds); /* * Traverse the supplied list of reports and find the longest diff --git a/include/linux/hid.h b/include/linux/hid.h index 0c48991b0402..b8058c5c5594 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -989,7 +989,6 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct); int usbhid_quirks_init(char **quirks_param); void usbhid_quirks_exit(void); -void usbhid_set_leds(struct hid_device *hid); #ifdef CONFIG_HID_PID int hid_pidff_init(struct hid_device *hid); -- cgit v1.2.3-59-g8ed1b From 60682284e40be070a4a13df2cb332286b4750f8a Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 15 Jul 2013 19:10:11 +0200 Subject: HID: usbhid: update LED fields unlocked Report fields can be updated from HID drivers unlocked via hid_set_field(). It is protected by input_lock in HID core so only a single input event is handled at a time. USBHID can thus update the field unlocked and doesn't conflict with any HID vendor/device drivers. Note, many HID drivers make heavy use of hid_set_field() in that way. But usbhid also schedules a work to gather multiple LED changes in a single report. Hence, we used to lock the LED field update so the work can read a consistent state. However, hid_set_field() only writes a single integer field, which is guaranteed to be allocated all the time. So the worst possible race-condition is a garbage read on the LED field. Therefore, there is no need to protect the update. In fact, the only thing that is prevented by locking hid_set_field(), is an LED update while the scheduled work currently writes an older LED update out. However, this means, a new work is scheduled directly when the old one is done writing the new state to the device. So we actually _win_ by not protecting the write and allowing the write to be combined with the current write. A new worker is still scheduled, but will not write any new state. So the LED will not blink unnecessarily on the device. Assume we have the LED set to 0. Two request come in which enable the LED and immediately disable it. The current situation with two CPUs would be: usb_hidinput_input_event() | hid_led() ---------------------------------+---------------------------------- spin_lock(&usbhid->lock); hid_set_field(1); spin_unlock(&usbhid->lock); schedule_work(...); spin_lock(&usbhid->lock); __usbhid_submit_report(..1..); spin_unlock(&usbhid->lock); spin_lock(&usbhid->lock); hid_set_field(0); spin_unlock(&usbhid->lock); schedule_work(...); spin_lock(&usbhid->lock); __usbhid_submit_report(..0..); spin_unlock(&usbhid->lock); With the locking removed, we _might_ end up with (look at the changed __usbhid_submit_report() parameters in the first try!): usb_hidinput_input_event() | hid_led() ---------------------------------+---------------------------------- hid_set_field(1); schedule_work(...); spin_lock(&usbhid->lock); hid_set_field(0); schedule_work(...); __usbhid_submit_report(..0..); spin_unlock(&usbhid->lock); ... next work ... spin_lock(&usbhid->lock); __usbhid_submit_report(..0..); spin_unlock(&usbhid->lock); As one can see, we no longer send the "LED ON" signal as it is disabled immediately afterwards and the following "LED OFF" request overwrites the pending "LED ON". It is important to note that hid_set_field() is not atomic, so we might also end up with any other value. But that doesn't matter either as we _always_ schedule the next work with a correct value and schedule_work() acts as memory barrier, anyways. So in the worst case, we run __usbhid_submit_report(....) in the first case and the following __usbhid_submit_report() will write the correct value. But LED states are booleans so any garbage will be converted to either 0 or 1 and the remote device will never see invalid requests. Why all this? It avoids any custom locking around hid_set_field() in usbhid and finally allows us to provide a generic hidinput_input_event() handler for all HID transport drivers. Signed-off-by: David Herrmann Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 5482bf447688..62b51316176c 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -664,6 +664,19 @@ static void hid_led(struct work_struct *work) return; } + /* + * field->report is accessed unlocked regarding HID core. So there might + * be another incoming SET-LED request from user-space, which changes + * the LED state while we assemble our outgoing buffer. However, this + * doesn't matter as hid_output_report() correctly converts it into a + * boolean value no matter what information is currently set on the LED + * field (even garbage). So the remote device will always get a valid + * request. + * And in case we send a wrong value, a next hid_led() worker is spawned + * for every SET-LED request so the following hid_led() worker will send + * the correct value, guaranteed! + */ + spin_lock_irqsave(&usbhid->lock, flags); if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) { usbhid->ledcount = hidinput_count_leds(hid); @@ -678,7 +691,6 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un struct hid_device *hid = input_get_drvdata(dev); struct usbhid_device *usbhid = hid->driver_data; struct hid_field *field; - unsigned long flags; int offset; if (type == EV_FF) @@ -692,9 +704,7 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un return -1; } - spin_lock_irqsave(&usbhid->lock, flags); hid_set_field(field, offset, value); - spin_unlock_irqrestore(&usbhid->lock, flags); /* * Defer performing requested LED action. -- cgit v1.2.3-59-g8ed1b From bfde79cb3541170f8413bc8be34406f86c49392a Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 15 Jul 2013 19:10:13 +0200 Subject: HID: usbhid: use generic hidinput_input_event() HID core provides the same functionality as we do, so drop the custom hidinput_input_event() handler. Signed-off-by: David Herrmann Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 74 ++----------------------------------------- drivers/hid/usbhid/usbhid.h | 3 -- 2 files changed, 3 insertions(+), 74 deletions(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 62b51316176c..8c3235705679 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -649,72 +649,6 @@ static void usbhid_submit_report(struct hid_device *hid, struct hid_report *repo spin_unlock_irqrestore(&usbhid->lock, flags); } -/* Workqueue routine to send requests to change LEDs */ -static void hid_led(struct work_struct *work) -{ - struct usbhid_device *usbhid = - container_of(work, struct usbhid_device, led_work); - struct hid_device *hid = usbhid->hid; - struct hid_field *field; - unsigned long flags; - - field = hidinput_get_led_field(hid); - if (!field) { - hid_warn(hid, "LED event field not found\n"); - return; - } - - /* - * field->report is accessed unlocked regarding HID core. So there might - * be another incoming SET-LED request from user-space, which changes - * the LED state while we assemble our outgoing buffer. However, this - * doesn't matter as hid_output_report() correctly converts it into a - * boolean value no matter what information is currently set on the LED - * field (even garbage). So the remote device will always get a valid - * request. - * And in case we send a wrong value, a next hid_led() worker is spawned - * for every SET-LED request so the following hid_led() worker will send - * the correct value, guaranteed! - */ - - spin_lock_irqsave(&usbhid->lock, flags); - if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) { - usbhid->ledcount = hidinput_count_leds(hid); - hid_dbg(usbhid->hid, "New ledcount = %u\n", usbhid->ledcount); - __usbhid_submit_report(hid, field->report, USB_DIR_OUT); - } - spin_unlock_irqrestore(&usbhid->lock, flags); -} - -static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct usbhid_device *usbhid = hid->driver_data; - struct hid_field *field; - int offset; - - if (type == EV_FF) - return input_ff_event(dev, type, code, value); - - if (type != EV_LED) - return -1; - - if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) { - hid_warn(dev, "event field not found\n"); - return -1; - } - - hid_set_field(field, offset, value); - - /* - * Defer performing requested LED action. - * This is more likely gather all LED changes into a single URB. - */ - schedule_work(&usbhid->led_work); - - return 0; -} - static int usbhid_wait_io(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; @@ -1283,7 +1217,6 @@ static struct hid_ll_driver usb_hid_driver = { .open = usbhid_open, .close = usbhid_close, .power = usbhid_power, - .hidinput_input_event = usb_hidinput_input_event, .request = usbhid_request, .wait = usbhid_wait_io, .idle = usbhid_idle, @@ -1377,8 +1310,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); spin_lock_init(&usbhid->lock); - INIT_WORK(&usbhid->led_work, hid_led); - ret = hid_add_device(hid); if (ret) { if (ret != -ENODEV) @@ -1411,7 +1342,6 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid) { del_timer_sync(&usbhid->io_retry); cancel_work_sync(&usbhid->reset_work); - cancel_work_sync(&usbhid->led_work); } static void hid_cease_io(struct usbhid_device *usbhid) @@ -1531,15 +1461,17 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) struct usbhid_device *usbhid = hid->driver_data; int status = 0; bool driver_suspended = false; + unsigned int ledcount; if (PMSG_IS_AUTO(message)) { + ledcount = hidinput_count_leds(hid); spin_lock_irq(&usbhid->lock); /* Sync with error handler */ if (!test_bit(HID_RESET_PENDING, &usbhid->iofl) && !test_bit(HID_CLEAR_HALT, &usbhid->iofl) && !test_bit(HID_OUT_RUNNING, &usbhid->iofl) && !test_bit(HID_CTRL_RUNNING, &usbhid->iofl) && !test_bit(HID_KEYS_PRESSED, &usbhid->iofl) - && (!usbhid->ledcount || ignoreled)) + && (!ledcount || ignoreled)) { set_bit(HID_SUSPENDED, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index dbb6af699135..f633c24ce28b 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -92,9 +92,6 @@ struct usbhid_device { unsigned int retry_delay; /* Delay length in ms */ struct work_struct reset_work; /* Task context for resets */ wait_queue_head_t wait; /* For sleeping */ - int ledcount; /* counting the number of active leds */ - - struct work_struct led_work; /* Task context for setting LEDs */ }; #define hid_to_usb_dev(hid_dev) \ -- cgit v1.2.3-59-g8ed1b From ac126f46cbab355d045772c1ecce899b683b2745 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 15 Jul 2013 19:10:14 +0200 Subject: HID: i2c: use generic hidinput_input_event() HID core provides the same functionality, so drop the custom handler. Besides, the current handler doesn't schedule any outgoing report so it did not work, anyway. Signed-off-by: David Herrmann Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 879b0ed701a3..db2253b5193a 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -756,29 +756,6 @@ static int i2c_hid_power(struct hid_device *hid, int lvl) return ret; } -static int i2c_hid_hidinput_input_event(struct input_dev *dev, - unsigned int type, unsigned int code, int value) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct hid_field *field; - int offset; - - if (type == EV_FF) - return input_ff_event(dev, type, code, value); - - if (type != EV_LED) - return -1; - - offset = hidinput_find_field(hid, type, code, &field); - - if (offset == -1) { - hid_warn(dev, "event field not found\n"); - return -1; - } - - return hid_set_field(field, offset, value); -} - static struct hid_ll_driver i2c_hid_ll_driver = { .parse = i2c_hid_parse, .start = i2c_hid_start, @@ -787,7 +764,6 @@ static struct hid_ll_driver i2c_hid_ll_driver = { .close = i2c_hid_close, .power = i2c_hid_power, .request = i2c_hid_request, - .hidinput_input_event = i2c_hid_hidinput_input_event, }; static int i2c_hid_init_irq(struct i2c_client *client) -- cgit v1.2.3-59-g8ed1b From bdb829e1dd710029a075b5f86d4053e7715beb06 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 15 Jul 2013 19:10:15 +0200 Subject: HID: uhid: use generic hidinput_input_event() HID core provides the same functionality and can convert the input event to a raw output report. We can thus drop UHID_OUTPUT_EV and rely on the mandatory UHID_OUTPUT. User-space wasn't able to do anything with UHID_OUTPUT_EV, anyway. They don't have access to the report fields. Signed-off-by: David Herrmann Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- Documentation/hid/uhid.txt | 4 +++- drivers/hid/uhid.c | 25 ------------------------- include/uapi/linux/uhid.h | 4 +++- 3 files changed, 6 insertions(+), 27 deletions(-) diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt index 3c741214dfbb..dc35a2b75eee 100644 --- a/Documentation/hid/uhid.txt +++ b/Documentation/hid/uhid.txt @@ -149,11 +149,13 @@ needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads. is of type "struct uhid_data_req". This may be received even though you haven't received UHID_OPEN, yet. - UHID_OUTPUT_EV: + UHID_OUTPUT_EV (obsolete): Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This is called for force-feedback, LED or similar events which are received through an input device by the HID subsystem. You should convert this into raw reports and send them to your device similar to events of type UHID_OUTPUT. + This is no longer sent by newer kernels. Instead, HID core converts it into a + raw output report and sends it via UHID_OUTPUT. UHID_FEATURE: This event is sent if the kernel driver wants to perform a feature request as diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index fc307e0422af..f53f2d52e677 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -116,30 +116,6 @@ static void uhid_hid_close(struct hid_device *hid) uhid_queue_event(uhid, UHID_CLOSE); } -static int uhid_hid_input(struct input_dev *input, unsigned int type, - unsigned int code, int value) -{ - struct hid_device *hid = input_get_drvdata(input); - struct uhid_device *uhid = hid->driver_data; - unsigned long flags; - struct uhid_event *ev; - - ev = kzalloc(sizeof(*ev), GFP_ATOMIC); - if (!ev) - return -ENOMEM; - - ev->type = UHID_OUTPUT_EV; - ev->u.output_ev.type = type; - ev->u.output_ev.code = code; - ev->u.output_ev.value = value; - - spin_lock_irqsave(&uhid->qlock, flags); - uhid_queue(uhid, ev); - spin_unlock_irqrestore(&uhid->qlock, flags); - - return 0; -} - static int uhid_hid_parse(struct hid_device *hid) { struct uhid_device *uhid = hid->driver_data; @@ -273,7 +249,6 @@ static struct hid_ll_driver uhid_hid_driver = { .stop = uhid_hid_stop, .open = uhid_hid_open, .close = uhid_hid_close, - .hidinput_input_event = uhid_hid_input, .parse = uhid_hid_parse, }; diff --git a/include/uapi/linux/uhid.h b/include/uapi/linux/uhid.h index e9ed951e2b09..414b74be4da1 100644 --- a/include/uapi/linux/uhid.h +++ b/include/uapi/linux/uhid.h @@ -30,7 +30,7 @@ enum uhid_event_type { UHID_OPEN, UHID_CLOSE, UHID_OUTPUT, - UHID_OUTPUT_EV, + UHID_OUTPUT_EV, /* obsolete! */ UHID_INPUT, UHID_FEATURE, UHID_FEATURE_ANSWER, @@ -69,6 +69,8 @@ struct uhid_output_req { __u8 rtype; } __attribute__((__packed__)); +/* Obsolete! Newer kernels will no longer send these events but instead convert + * it into raw output reports via UHID_OUTPUT. */ struct uhid_output_ev_req { __u16 type; __u16 code; -- cgit v1.2.3-59-g8ed1b From 50c9d75b6f01a337aab728511bc1d2a0a3d7b800 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 15 Jul 2013 19:10:12 +0200 Subject: HID: input: generic hidinput_input_event handler The hidinput_input_event() callback converts input events written from userspace into HID reports and sends them to the device. We currently implement this in every HID transport driver, even though most of them do the same. This provides a generic hidinput_input_event() implementation which is mostly copied from usbhid. It uses a delayed worker to allow multiple LED events to be collected into a single output event. We use the custom ->request() transport driver callback to allow drivers to adjust the outgoing report and handle the request asynchronously. If no custom ->request() callback is available, we fall back to the generic raw output report handler (which is synchronous). Drivers can still provide custom hidinput_input_event() handlers (see logitech-dj) if the generic implementation doesn't fit their needs. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/hid.h | 1 + 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7480799e535c..308eee8fc7c3 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1137,6 +1137,74 @@ unsigned int hidinput_count_leds(struct hid_device *hid) } EXPORT_SYMBOL_GPL(hidinput_count_leds); +static void hidinput_led_worker(struct work_struct *work) +{ + struct hid_device *hid = container_of(work, struct hid_device, + led_work); + struct hid_field *field; + struct hid_report *report; + int len; + __u8 *buf; + + field = hidinput_get_led_field(hid); + if (!field) + return; + + /* + * field->report is accessed unlocked regarding HID core. So there might + * be another incoming SET-LED request from user-space, which changes + * the LED state while we assemble our outgoing buffer. However, this + * doesn't matter as hid_output_report() correctly converts it into a + * boolean value no matter what information is currently set on the LED + * field (even garbage). So the remote device will always get a valid + * request. + * And in case we send a wrong value, a next led worker is spawned + * for every SET-LED request so the following worker will send the + * correct value, guaranteed! + */ + + report = field->report; + + /* use custom SET_REPORT request if possible (asynchronous) */ + if (hid->ll_driver->request) + return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT); + + /* fall back to generic raw-output-report */ + len = ((report->size - 1) >> 3) + 1 + (report->id > 0); + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return; + + hid_output_report(report, buf); + /* synchronous output report */ + hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT); + kfree(buf); +} + +static int hidinput_input_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct hid_field *field; + int offset; + + if (type == EV_FF) + return input_ff_event(dev, type, code, value); + + if (type != EV_LED) + return -1; + + if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) { + hid_warn(dev, "event field not found\n"); + return -1; + } + + hid_set_field(field, offset, value); + + schedule_work(&hid->led_work); + return 0; +} + static int hidinput_open(struct input_dev *dev) { struct hid_device *hid = input_get_drvdata(dev); @@ -1183,7 +1251,10 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid) } input_set_drvdata(input_dev, hid); - input_dev->event = hid->ll_driver->hidinput_input_event; + if (hid->ll_driver->hidinput_input_event) + input_dev->event = hid->ll_driver->hidinput_input_event; + else if (hid->ll_driver->request || hid->hid_output_raw_report) + input_dev->event = hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; input_dev->setkeycode = hidinput_setkeycode; @@ -1278,6 +1349,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) int i, j, k; INIT_LIST_HEAD(&hid->inputs); + INIT_WORK(&hid->led_work, hidinput_led_worker); if (!force) { for (i = 0; i < hid->maxcollection; i++) { @@ -1379,6 +1451,12 @@ void hidinput_disconnect(struct hid_device *hid) input_unregister_device(hidinput->input); kfree(hidinput); } + + /* led_work is spawned by input_dev callbacks, but doesn't access the + * parent input_dev at all. Once all input devices are removed, we + * know that led_work will never get restarted, so we can cancel it + * synchronously and are safe. */ + cancel_work_sync(&hid->led_work); } EXPORT_SYMBOL_GPL(hidinput_disconnect); diff --git a/include/linux/hid.h b/include/linux/hid.h index b8058c5c5594..ea4b828cb9cd 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -456,6 +456,7 @@ struct hid_device { /* device report descriptor */ enum hid_type type; /* device type (mouse, kbd, ...) */ unsigned country; /* HID country */ struct hid_report_enum report_enum[HID_REPORT_TYPES]; + struct work_struct led_work; /* delayed LED worker */ struct semaphore driver_lock; /* protects the current driver, except during input */ struct semaphore driver_input_lock; /* protects the current driver */ -- cgit v1.2.3-59-g8ed1b From 3d7d248cf484fe37595698e0ca31a9bcecc85a42 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 13 Jun 2013 09:50:35 +0200 Subject: HID: i2c-hid: add DT bindings Add device tree based support for HID over I2C devices. Tested on an Odroid-X board with a Synaptics touchpad. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- .../devicetree/bindings/hid/hid-over-i2c.txt | 28 ++++++++++++++ drivers/hid/i2c-hid/i2c-hid.c | 44 +++++++++++++++++++++- include/linux/i2c/i2c-hid.h | 3 +- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/hid/hid-over-i2c.txt diff --git a/Documentation/devicetree/bindings/hid/hid-over-i2c.txt b/Documentation/devicetree/bindings/hid/hid-over-i2c.txt new file mode 100644 index 000000000000..488edcb264c4 --- /dev/null +++ b/Documentation/devicetree/bindings/hid/hid-over-i2c.txt @@ -0,0 +1,28 @@ +* HID over I2C Device-Tree bindings + +HID over I2C provides support for various Human Interface Devices over the +I2C bus. These devices can be for example touchpads, keyboards, touch screens +or sensors. + +The specification has been written by Microsoft and is currently available here: +http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx + +If this binding is used, the kernel module i2c-hid will handle the communication +with the device and the generic hid core layer will handle the protocol. + +Required properties: +- compatible: must be "hid-over-i2c" +- reg: i2c slave address +- hid-descr-addr: HID descriptor address +- interrupt-parent: the phandle for the interrupt controller +- interrupts: interrupt line + +Example: + + i2c-hid-dev@2c { + compatible = "hid-over-i2c"; + reg = <0x2c>; + hid-descr-addr = <0x0020>; + interrupt-parent = <&gpx3>; + interrupts = <3 2>; + }; diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 879b0ed701a3..fc9d92cb3f39 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -933,6 +934,42 @@ static inline int i2c_hid_acpi_pdata(struct i2c_client *client, } #endif +#ifdef CONFIG_OF +static int i2c_hid_of_probe(struct i2c_client *client, + struct i2c_hid_platform_data *pdata) +{ + struct device *dev = &client->dev; + u32 val; + int ret; + + ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val); + if (ret) { + dev_err(&client->dev, "HID register address not provided\n"); + return -ENODEV; + } + if (val >> 16) { + dev_err(&client->dev, "Bad HID register address: 0x%08x\n", + val); + return -EINVAL; + } + pdata->hid_descriptor_address = val; + + return 0; +} + +static const struct of_device_id i2c_hid_of_match[] = { + { .compatible = "hid-over-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_hid_of_match); +#else +static inline int i2c_hid_of_probe(struct i2c_client *client, + struct i2c_hid_platform_data *pdata) +{ + return -ENODEV; +} +#endif + static int i2c_hid_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) { @@ -954,7 +991,11 @@ static int i2c_hid_probe(struct i2c_client *client, if (!ihid) return -ENOMEM; - if (!platform_data) { + if (client->dev.of_node) { + ret = i2c_hid_of_probe(client, &ihid->pdata); + if (ret) + goto err; + } else if (!platform_data) { ret = i2c_hid_acpi_pdata(client, &ihid->pdata); if (ret) { dev_err(&client->dev, @@ -1095,6 +1136,7 @@ static struct i2c_driver i2c_hid_driver = { .owner = THIS_MODULE, .pm = &i2c_hid_pm, .acpi_match_table = ACPI_PTR(i2c_hid_acpi_match), + .of_match_table = of_match_ptr(i2c_hid_of_match), }, .probe = i2c_hid_probe, diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/i2c/i2c-hid.h index 60e411d764d4..7aa901d92058 100644 --- a/include/linux/i2c/i2c-hid.h +++ b/include/linux/i2c/i2c-hid.h @@ -19,7 +19,8 @@ * @hid_descriptor_address: i2c register where the HID descriptor is stored. * * Note that it is the responsibility of the platform driver (or the acpi 5.0 - * driver) to setup the irq related to the gpio in the struct i2c_board_info. + * driver, or the flattened device tree) to setup the irq related to the gpio in + * the struct i2c_board_info. * The platform driver should also setup the gpio according to the device: * * A typical example is the following: -- cgit v1.2.3-59-g8ed1b From ce7373685ec33b94c293d9cfdb46daecb22db261 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 31 Jul 2013 17:17:58 -0400 Subject: HID: logitech-dj: Fix non-atomic kmalloc in logi_dj_ll_input_event() The ll_driver's .hidinput_input_event() method is called from atomic context [1]. Use GFP_ATOMIC for allocation of the synthesized hid report. BUG: sleeping function called from invalid context at /home/peter/src/kernels/next/mm/slub.c:941 in_atomic(): 1, irqs_disabled(): 1, pid: 2095, name: Xorg INFO: lockdep is turned off. irq event stamp: 1502178 hardirqs last enabled at (1502177): [] _raw_spin_unlock_irqrestore+0x65/0x80 hardirqs last disabled at (1502178): [] common_interrupt+0x6a/0x6f softirqs last enabled at (1501802): [] __do_softirq+0x183/0x420 softirqs last disabled at (1501799): [] irq_exit+0xb5/0xc0 CPU: 3 PID: 2095 Comm: Xorg Not tainted 3.11-next-20130725-xeon+lockdep #20130725 Hardware name: Dell Inc. Precision WorkStation T5400 /0RW203, BIOS A11 04/30/2012 ffffffff81a662e0 ffff8802adcf9ca8 ffffffff8177c330 0000000000000000 ffff8802a76d2440 ffff8802adcf9cd8 ffffffff810867d0 ffff8802a7ac8000 0000000000000010 00000000ffffffff 00000000000000d0 ffff8802adcf9d38 Call Trace: [] dump_stack+0x4f/0x84 [] __might_sleep+0x140/0x1f0 [] __kmalloc+0x6b/0x2e0 [] ? hid_alloc_report_buf+0x28/0x30 [hid] [] hid_alloc_report_buf+0x28/0x30 [hid] [] logi_dj_ll_input_event+0xb0/0x1b0 [hid_logitech_dj] [] input_handle_event+0x8e/0x540 [] ? input_inject_event+0x5d/0x220 [] input_inject_event+0x1c0/0x220 [] ? input_inject_event+0x44/0x220 [] ? might_fault+0xa0/0xb0 [] ? might_fault+0x57/0xb0 [] evdev_write+0xde/0x160 [] vfs_write+0xc8/0x1f0 [] SyS_write+0x55/0xa0 [] system_call_fastpath+0x16/0x1b Signed-off-by: Peter Hurley Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 4d792739dbd1..d318222c6315 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -591,7 +591,7 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, } hid_set_field(field, offset, value); - data = hid_alloc_report_buf(field->report, GFP_KERNEL); + data = hid_alloc_report_buf(field->report, GFP_ATOMIC); if (!data) { dev_warn(&dev->dev, "failed to allocate report buf memory\n"); return -1; -- cgit v1.2.3-59-g8ed1b From 4858bfe07388afaccce0a66683df2b55d578f0a9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 2 Aug 2013 14:07:15 +0300 Subject: HID: i2c-hid: don't push static constants on stack for %*ph There is no need to pass constants via stack. The width may be explicitly specified in the format. Signed-off-by: Andy Shevchenko Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 879b0ed701a3..3cb7d966da9e 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -824,8 +824,8 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) * bytes 2-3 -> bcdVersion (has to be 1.00) */ ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, 4); - i2c_hid_dbg(ihid, "%s, ihid->hdesc_buffer: %*ph\n", - __func__, 4, ihid->hdesc_buffer); + i2c_hid_dbg(ihid, "%s, ihid->hdesc_buffer: %4ph\n", __func__, + ihid->hdesc_buffer); if (ret) { dev_err(&client->dev, -- cgit v1.2.3-59-g8ed1b From 9854a6f929956c9099dcc837157fd344f6f1c227 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 2 Aug 2013 14:08:00 +0300 Subject: HID: hid-holtekff: don't push static constants on stack for %*ph There is no need to pass constants via stack. The width may be explicitly specified in the format. Signed-off-by: Andy Shevchenko Signed-off-by: Jiri Kosina --- drivers/hid/hid-holtekff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c index 9a8f05124525..9325545fc3ae 100644 --- a/drivers/hid/hid-holtekff.c +++ b/drivers/hid/hid-holtekff.c @@ -98,7 +98,7 @@ static void holtekff_send(struct holtekff_device *holtekff, holtekff->field->value[i] = data[i]; } - dbg_hid("sending %*ph\n", 7, data); + dbg_hid("sending %7ph\n", data); hid_hw_request(hid, holtekff->field->report, HID_REQ_SET_REPORT); } -- cgit v1.2.3-59-g8ed1b From a6be8569b6705cbc26e7ae1a8be476067cc5a78b Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 4 Aug 2013 18:50:10 +0200 Subject: HID: wiimote: work around broken DRM_KAI on GEN10 GEN10 and earlier devices seem to not support DRM_KAI if we run in basic IR mode. Use DRM_KAIE instead. This might increases overhead slightly as the extension port is read and streamed but we stream accelerometer data constantly, too, so this is negligible. Note that our parsers are hardcoded on IR-formats, so we cannot actually use 96-bit IR DRMs for basic IR data. We would have to adjust the parsers. But as only GEN20 and newer support this, we simply avoid mixed DRMs. This fixes a bug where GEN10 devices didn't provide IR data if accelerometer and IR are enabled simultaneously. As a workaround, you can enable DRM_KAIE without this patch via (disables device power-management): echo "37" >/sys/kernel/debug/hid//drm Cc: stable@vger.kernel.org Signed-off-by: David Herrmann Reported-by: Nicolas Adenis-Lamarre Signed-off-by: Jiri Kosina --- drivers/hid/hid-wiimote-core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 0c06054cab8f..660209824e56 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -212,10 +212,12 @@ static __u8 select_drm(struct wiimote_data *wdata) if (ir == WIIPROTO_FLAG_IR_BASIC) { if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) { - if (ext) - return WIIPROTO_REQ_DRM_KAIE; - else - return WIIPROTO_REQ_DRM_KAI; + /* GEN10 and ealier devices bind IR formats to DRMs. + * Hence, we cannot use DRM_KAI here as it might be + * bound to IR_EXT. Use DRM_KAIE unconditionally so we + * work with all devices and our parsers can use the + * fixed formats, too. */ + return WIIPROTO_REQ_DRM_KAIE; } else { return WIIPROTO_REQ_DRM_KIE; } -- cgit v1.2.3-59-g8ed1b From 212a871a3934beccf43431608c27ed2e05a476ec Mon Sep 17 00:00:00 2001 From: Manoj Chourasia Date: Mon, 22 Jul 2013 15:33:13 +0530 Subject: HID: hidraw: correctly deallocate memory on device disconnect This changes puts the commit 4fe9f8e203f back in place with the fixes for slab corruption because of the commit. When a device is unplugged, wait for all processes that have opened the device to close before deallocating the device. This commit was solving kernel crash because of the corruption in rb tree of vmalloc. The rootcause was the device data pointer was geting excessed after the memory associated with hidraw was freed. The commit 4fe9f8e203f was buggy as it was also freeing the hidraw first and then calling delete operation on the list associated with that hidraw leading to slab corruption. Signed-off-by: Manoj Chourasia Tested-by: Peter Wu Cc: stable@vger.kernel.org Signed-off-by: Jiri Kosina --- drivers/hid/hidraw.c | 60 ++++++++++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index a7451632ceb4..612a655bc9f0 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -113,7 +113,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, __u8 *buf; int ret = 0; - if (!hidraw_table[minor]) { + if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { ret = -ENODEV; goto out; } @@ -261,7 +261,7 @@ static int hidraw_open(struct inode *inode, struct file *file) } mutex_lock(&minors_lock); - if (!hidraw_table[minor]) { + if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { err = -ENODEV; goto out_unlock; } @@ -302,39 +302,38 @@ static int hidraw_fasync(int fd, struct file *file, int on) return fasync_helper(fd, file, on, &list->fasync); } +static void drop_ref(struct hidraw *hidraw, int exists_bit) +{ + if (exists_bit) { + hid_hw_close(hidraw->hid); + hidraw->exist = 0; + if (hidraw->open) + wake_up_interruptible(&hidraw->wait); + } else { + --hidraw->open; + } + + if (!hidraw->open && !hidraw->exist) { + device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); + hidraw_table[hidraw->minor] = NULL; + kfree(hidraw); + } +} + static int hidraw_release(struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); - struct hidraw *dev; struct hidraw_list *list = file->private_data; - int ret; - int i; mutex_lock(&minors_lock); - if (!hidraw_table[minor]) { - ret = -ENODEV; - goto unlock; - } list_del(&list->node); - dev = hidraw_table[minor]; - if (!--dev->open) { - if (list->hidraw->exist) { - hid_hw_power(dev->hid, PM_HINT_NORMAL); - hid_hw_close(dev->hid); - } else { - kfree(list->hidraw); - } - } - - for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i) - kfree(list->buffer[i].value); kfree(list); - ret = 0; -unlock: - mutex_unlock(&minors_lock); - return ret; + drop_ref(hidraw_table[minor], 0); + + mutex_unlock(&minors_lock); + return 0; } static long hidraw_ioctl(struct file *file, unsigned int cmd, @@ -539,18 +538,9 @@ void hidraw_disconnect(struct hid_device *hid) struct hidraw *hidraw = hid->hidraw; mutex_lock(&minors_lock); - hidraw->exist = 0; - - device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); - hidraw_table[hidraw->minor] = NULL; + drop_ref(hidraw, 1); - if (hidraw->open) { - hid_hw_close(hid); - wake_up_interruptible(&hidraw->wait); - } else { - kfree(hidraw); - } mutex_unlock(&minors_lock); } EXPORT_SYMBOL_GPL(hidraw_disconnect); -- cgit v1.2.3-59-g8ed1b From 6e0fe2e5723cec36eb24959dd99a16e445816a6a Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 15 Jun 2013 15:32:44 +0200 Subject: input: document gamepad API and add extra keycodes Until today all gamepad input drivers report their data differently. It is nearly impossible to write applications for more than one device in a generic way. Therefore, this patch introduces a uniform gamepad API which will be used for all new drivers. Instead of mapping buttons by their labels, we now map them by position. This allows applications to work with any gamepad regardless of the labels on the buttons. Furthermore, we standardize the ABS_* codes for analog triggers and sticks. For D-Pads the long overdue BTN_DPAD_* codes are introduced. They should be fairly obvious how to use. To avoid confusion, the action buttons now have BTN_EAST/SOUTH/WEST/NORTH aliases. Reported-by: Todd Showalter Signed-off-by: David Herrmann Acked-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- Documentation/input/gamepad.txt | 156 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 Documentation/input/gamepad.txt diff --git a/Documentation/input/gamepad.txt b/Documentation/input/gamepad.txt new file mode 100644 index 000000000000..8002c894c6b0 --- /dev/null +++ b/Documentation/input/gamepad.txt @@ -0,0 +1,156 @@ + Linux Gamepad API +---------------------------------------------------------------------------- + +1. Intro +~~~~~~~~ +Linux provides many different input drivers for gamepad hardware. To avoid +having user-space deal with different button-mappings for each gamepad, this +document defines how gamepads are supposed to report their data. + +2. Geometry +~~~~~~~~~~~ +As "gamepad" we define devices which roughly look like this: + + ____________________________ __ + / [__ZL__] [__ZR__] \ | + / [__ TL __] [__ TR __] \ | Front Triggers + __/________________________________\__ __| + / _ \ | + / /\ __ (N) \ | + / || __ |MO| __ _ _ \ | Main Pad + | <===DP===> |SE| |ST| (W) -|- (E) | | + \ || ___ ___ _ / | + /\ \/ / \ / \ (S) /\ __| + / \________ | LS | ____ | RS | ________/ \ | + | / \ \___/ / \ \___/ / \ | | Control Sticks + | / \_____/ \_____/ \ | __| + | / \ | + \_____/ \_____/ + + |________|______| |______|___________| + D-Pad Left Right Action Pad + Stick Stick + + |_____________| + Menu Pad + +Most gamepads have the following features: + - Action-Pad + 4 buttons in diamonds-shape (on the right side). The buttons are + differently labeled on most devices so we define them as NORTH, + SOUTH, WEST and EAST. + - D-Pad (Direction-pad) + 4 buttons (on the left side) that point up, down, left and right. + - Menu-Pad + Different constellations, but most-times 2 buttons: SELECT - START + Furthermore, many gamepads have a fancy branded button that is used as + special system-button. It often looks different to the other buttons and + is used to pop up system-menus or system-settings. + - Analog-Sticks + Analog-sticks provide freely moveable sticks to control directions. Not + all devices have both or any, but they are present at most times. + Analog-sticks may also provide a digital button if you press them. + - Triggers + Triggers are located on the upper-side of the pad in vertical direction. + Not all devices provide them, but the upper buttons are normally named + Left- and Right-Triggers, the lower buttons Z-Left and Z-Right. + - Rumble + Many devices provide force-feedback features. But are mostly just + simple rumble motors. + +3. Detection +~~~~~~~~~~~~ +All gamepads that follow the protocol described here map BTN_GAMEPAD. This is +an alias for BTN_SOUTH/BTN_A. It can be used to identify a gamepad as such. +However, not all gamepads provide all features, so you need to test for all +features that you need, first. How each feature is mapped is described below. + +Legacy drivers often don't comply to these rules. As we cannot change them +for backwards-compatibility reasons, you need to provide fixup mappings in +user-space yourself. Some of them might also provide module-options that +change the mappings so you can adivce users to set these. + +All new gamepads are supposed to comply with this mapping. Please report any +bugs, if they don't. + +There are a lot of less-featured/less-powerful devices out there, which re-use +the buttons from this protocol. However, they try to do this in a compatible +fashion. For example, the "Nintendo Wii Nunchuk" provides two trigger buttons +and one analog stick. It reports them as if it were a gamepad with only one +analog stick and two trigger buttons on the right side. +But that means, that if you only support "real" gamepads, you must test +devices for _all_ reported events that you need. Otherwise, you will also get +devices that report a small subset of the events. + +No other devices, that do not look/feel like a gamepad, shall report these +events. + +4. Events +~~~~~~~~~ +Gamepads report the following events: + +Action-Pad: + Every gamepad device has at least 2 action buttons. This means, that every + device reports BTN_SOUTH (which BTN_GAMEPAD is an alias for). Regardless + of the labels on the buttons, the codes are sent according to the + physical position of the buttons. + Please note that 2- and 3-button pads are fairly rare and old. You might + want to filter gamepads that do not report all four. + 2-Button Pad: + If only 2 action-buttons are present, they are reported as BTN_SOUTH and + BTN_EAST. For vertical layouts, the upper button is BTN_EAST. For + horizontal layouts, the button more on the right is BTN_EAST. + 3-Button Pad: + If only 3 action-buttons are present, they are reported as (from left + to right): BTN_WEST, BTN_SOUTH, BTN_EAST + If the buttons are aligned perfectly vertically, they are reported as + (from top down): BTN_WEST, BTN_SOUTH, BTN_EAST + 4-Button Pad: + If all 4 action-buttons are present, they can be aligned in two + different formations. If diamond-shaped, they are reported as BTN_NORTH, + BTN_WEST, BTN_SOUTH, BTN_EAST according to their physical location. + If rectangular-shaped, the upper-left button is BTN_NORTH, lower-left + is BTN_WEST, lower-right is BTN_SOUTH and upper-right is BTN_EAST. + +D-Pad: + Every gamepad provides a D-Pad with four directions: Up, Down, Left, Right + Some of these are available as digital buttons, some as analog buttons. Some + may even report both. The kernel does not convert between these so + applications should support both and choose what is more appropriate if + both are reported. + Digital buttons are reported as: + BTN_DPAD_* + Analog buttons are reported as: + ABS_HAT0X and ABS_HAT0Y + +Analog-Sticks: + The left analog-stick is reported as ABS_X, ABS_Y. The right analog stick is + reported as ABS_RX, ABS_RY. Zero, one or two sticks may be present. + If analog-sticks provide digital buttons, they are mapped accordingly as + BTN_THUMBL (first/left) and BTN_THUMBR (second/right). + +Triggers: + Trigger buttons can be available as digital or analog buttons or both. User- + space must correctly deal with any situation and choose the most appropriate + mode. + Upper trigger buttons are reported as BTN_TR or ABS_HAT1X (right) and BTN_TL + or ABS_HAT1Y (left). Lower trigger buttons are reported as BTN_TR2 or + ABS_HAT2X (right/ZR) and BTN_TL2 or ABS_HAT2Y (left/ZL). + If only one trigger-button combination is present (upper+lower), they are + reported as "right" triggers (BTN_TR/ABS_HAT1X). + +Menu-Pad: + Menu buttons are always digital and are mapped according to their location + instead of their labels. That is: + 1-button Pad: Mapped as BTN_START + 2-button Pad: Left button mapped as BTN_SELECT, right button mapped as + BTN_START + Many pads also have a third button which is branded or has a special symbol + and meaning. Such buttons are mapped as BTN_MODE. Examples are the Nintendo + "HOME" button, the XBox "X"-button or Sony "P" button. + +Rumble: + Rumble is adverticed as FF_RUMBLE. + +---------------------------------------------------------------------------- + Written 2013 by David Herrmann -- cgit v1.2.3-59-g8ed1b From 8cd3c556b5ce58e2a6f9a084711e6fc03f375745 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Tue, 13 Aug 2013 15:30:39 -0400 Subject: HID: samples/hidraw: add .gitignore file To fix: # Untracked files: # (use "git add ..." to include in what will be committed) # # samples/hidraw/hid-example as seen in git status output after an allyesconfig build. Signed-off-by: Paul Gortmaker Signed-off-by: Jiri Kosina --- samples/hidraw/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 samples/hidraw/.gitignore diff --git a/samples/hidraw/.gitignore b/samples/hidraw/.gitignore new file mode 100644 index 000000000000..05e51a685242 --- /dev/null +++ b/samples/hidraw/.gitignore @@ -0,0 +1 @@ +hid-example -- cgit v1.2.3-59-g8ed1b From c0b20fd9b67a57327074726c60640a957dab91e3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 23 Aug 2013 11:06:19 +0800 Subject: HID: use module_hid_driver() to simplify the code module_hid_driver() makes the code simpler by eliminating boilerplate code. Signed-off-by: Wei Yongjun Signed-off-by: Jiri Kosina --- drivers/hid/hid-xinmo.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/hid/hid-xinmo.c b/drivers/hid/hid-xinmo.c index 6153e50d9721..7df5227a7e61 100644 --- a/drivers/hid/hid-xinmo.c +++ b/drivers/hid/hid-xinmo.c @@ -57,16 +57,5 @@ static struct hid_driver xinmo_driver = { .event = xinmo_event }; -static int __init xinmo_init(void) -{ - return hid_register_driver(&xinmo_driver); -} - -static void __exit xinmo_exit(void) -{ - hid_unregister_driver(&xinmo_driver); -} - -module_init(xinmo_init); -module_exit(xinmo_exit); +module_hid_driver(xinmo_driver); MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 15261f6d8d032b30f6eb7dbf1dbb9e4095df84c0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 14 Aug 2013 11:07:08 +0300 Subject: HID: hid-sensor-hub: fix style of comments This patch fixes the style of the comments to be like following /* The commentary */ There is no functional change. Signed-off-by: Andy Shevchenko Signed-off-by: Jiri Kosina --- drivers/hid/hid-sensor-hub.c | 2 +- include/linux/hid-sensor-hub.h | 2 +- include/linux/hid-sensor-ids.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index ca7498107327..ffc80cf481b4 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -416,7 +416,7 @@ static int sensor_hub_raw_event(struct hid_device *hdev, return 1; ptr = raw_data; - ptr++; /*Skip report id*/ + ptr++; /* Skip report id */ spin_lock_irqsave(&pdata->lock, flags); diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index ecefb7311dd6..32ba45158d39 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -172,7 +172,7 @@ struct hid_sensor_common { struct hid_sensor_hub_attribute_info sensitivity; }; -/*Convert from hid unit expo to regular exponent*/ +/* Convert from hid unit expo to regular exponent */ static inline int hid_sensor_convert_exponent(int unit_expo) { if (unit_expo < 0x08) diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index 6f24446e7669..4f945d3ed49f 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -37,7 +37,7 @@ #define HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS 0x200458 #define HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS 0x200459 -/*ORIENTATION: Compass 3D: (200083) */ +/* ORIENTATION: Compass 3D: (200083) */ #define HID_USAGE_SENSOR_COMPASS_3D 0x200083 #define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING 0x200471 #define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING_X 0x200472 -- cgit v1.2.3-59-g8ed1b From 06bb5219118fb098f4b0c7dcb484b28a52bf1c14 Mon Sep 17 00:00:00 2001 From: Stefan Kriwanek Date: Sun, 25 Aug 2013 10:46:13 +0200 Subject: HID: Fix Speedlink VAD Cezanne support for some devices Some devices of the "Speedlink VAD Cezanne" model need more aggressive fixing than already done. I made sure through testing that this patch would not interfere with the proper working of a device that is bug-free. (The driver drops EV_REL events with abs(val) >= 256, which are not achievable even on the highest laser resolution hardware setting.) Signed-off-by: Stefan Kriwanek Signed-off-by: Jiri Kosina --- drivers/hid/hid-speedlink.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-speedlink.c b/drivers/hid/hid-speedlink.c index a2f587d004e1..7112f3e832ee 100644 --- a/drivers/hid/hid-speedlink.c +++ b/drivers/hid/hid-speedlink.c @@ -3,7 +3,7 @@ * Fixes "jumpy" cursor and removes nonexistent keyboard LEDS from * the HID descriptor. * - * Copyright (c) 2011 Stefan Kriwanek + * Copyright (c) 2011, 2013 Stefan Kriwanek */ /* @@ -46,8 +46,13 @@ static int speedlink_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { /* No other conditions due to usage_table. */ - /* Fix "jumpy" cursor (invalid events sent by device). */ - if (value == 256) + + /* This fixes the "jumpy" cursor occuring due to invalid events sent + * by the device. Some devices only send them with value==+256, others + * don't. However, catching abs(value)>=256 is restrictive enough not + * to interfere with devices that were bug-free (has been tested). + */ + if (abs(value) >= 256) return 1; /* Drop useless distance 0 events (on button clicks etc.) as well */ if (value == 0) -- cgit v1.2.3-59-g8ed1b From 277fe44dd862412ee034470ad1c13a79d24e533b Mon Sep 17 00:00:00 2001 From: Yonghua Zheng Date: Mon, 26 Aug 2013 23:38:35 +0800 Subject: HID: hidraw: Add spinlock in struct hidraw to protect list It is unsafe to call list_for_each_entry in hidraw_report_event to traverse each hidraw_list node without a lock protection, the list could be modified if someone calls hidraw_release and list_del to remove itself from the list, this can cause hidraw_report_event to touch a deleted list struct and panic. To prevent this, introduce a spinlock in struct hidraw to protect list from concurrent access. Signed-off-by: Yonghua Zheng Signed-off-by: Jiri Kosina --- drivers/hid/hidraw.c | 20 +++++++++++++++----- include/linux/hidraw.h | 1 + 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 612a655bc9f0..194a5660a389 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -253,6 +253,7 @@ static int hidraw_open(struct inode *inode, struct file *file) unsigned int minor = iminor(inode); struct hidraw *dev; struct hidraw_list *list; + unsigned long flags; int err = 0; if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { @@ -266,11 +267,6 @@ static int hidraw_open(struct inode *inode, struct file *file) goto out_unlock; } - list->hidraw = hidraw_table[minor]; - mutex_init(&list->read_mutex); - list_add_tail(&list->node, &hidraw_table[minor]->list); - file->private_data = list; - dev = hidraw_table[minor]; if (!dev->open++) { err = hid_hw_power(dev->hid, PM_HINT_FULLON); @@ -283,9 +279,16 @@ static int hidraw_open(struct inode *inode, struct file *file) if (err < 0) { hid_hw_power(dev->hid, PM_HINT_NORMAL); dev->open--; + goto out_unlock; } } + list->hidraw = hidraw_table[minor]; + mutex_init(&list->read_mutex); + spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); + list_add_tail(&list->node, &hidraw_table[minor]->list); + spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); + file->private_data = list; out_unlock: mutex_unlock(&minors_lock); out: @@ -324,10 +327,13 @@ static int hidraw_release(struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); struct hidraw_list *list = file->private_data; + unsigned long flags; mutex_lock(&minors_lock); + spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); list_del(&list->node); + spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); kfree(list); drop_ref(hidraw_table[minor], 0); @@ -456,7 +462,9 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len) struct hidraw *dev = hid->hidraw; struct hidraw_list *list; int ret = 0; + unsigned long flags; + spin_lock_irqsave(&dev->list_lock, flags); list_for_each_entry(list, &dev->list, node) { int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); @@ -471,6 +479,7 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len) list->head = new_head; kill_fasync(&list->fasync, SIGIO, POLL_IN); } + spin_unlock_irqrestore(&dev->list_lock, flags); wake_up_interruptible(&dev->wait); return ret; @@ -519,6 +528,7 @@ int hidraw_connect(struct hid_device *hid) mutex_unlock(&minors_lock); init_waitqueue_head(&dev->wait); + spin_lock_init(&dev->list_lock); INIT_LIST_HEAD(&dev->list); dev->hid = hid; diff --git a/include/linux/hidraw.h b/include/linux/hidraw.h index 2451662c728a..ddf52612eed8 100644 --- a/include/linux/hidraw.h +++ b/include/linux/hidraw.h @@ -23,6 +23,7 @@ struct hidraw { wait_queue_head_t wait; struct hid_device *hid; struct device *dev; + spinlock_t list_lock; struct list_head list; }; -- cgit v1.2.3-59-g8ed1b From 3dc8fc083dbfeede7b63a0c07581192e97711365 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 22 Aug 2013 14:51:07 +0200 Subject: HID: Use hid_parser for pre-scanning the report descriptors The Win 8 detection is sufficiently complex to warrant use of the full parser code, in spite of the inferred memory usage. Therefore, we can use the existing HID parser in hid-core for hid_scan_report() by re-using the code from hid_open_report(). hid_parser_global, hid_parser_local and hid_parser_reserved does not have any side effects. We just need to reimplement the MAIN_ITEM callback to have a proper parsing without side effects. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Tested-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 102 +++++++++++++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 38 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index e39dac68063c..ddd95f3e33c0 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -677,12 +677,52 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) return NULL; } -static void hid_scan_usage(struct hid_device *hid, u32 usage) +static void hid_scan_input_usage(struct hid_parser *parser, u32 usage) { + struct hid_device *hid = parser->device; + if (usage == HID_DG_CONTACTID) hid->group = HID_GROUP_MULTITOUCH; } +static void hid_scan_collection(struct hid_parser *parser, unsigned type) +{ + struct hid_device *hid = parser->device; + + if (((parser->global.usage_page << 16) == HID_UP_SENSOR) && + type == HID_COLLECTION_PHYSICAL) + hid->group = HID_GROUP_SENSOR_HUB; +} + +static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) +{ + __u32 data; + int i; + + data = item_udata(item); + + switch (item->tag) { + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: + hid_scan_collection(parser, data & 0xff); + break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: + break; + case HID_MAIN_ITEM_TAG_INPUT: + for (i = 0; i < parser->local.usage_index; i++) + hid_scan_input_usage(parser, parser->local.usage[i]); + break; + case HID_MAIN_ITEM_TAG_OUTPUT: + break; + case HID_MAIN_ITEM_TAG_FEATURE: + break; + } + + /* Reset the local parser environment */ + memset(&parser->local, 0, sizeof(parser->local)); + + return 0; +} + /* * Scan a report descriptor before the device is added to the bus. * Sets device groups and other properties that determine what driver @@ -690,48 +730,34 @@ static void hid_scan_usage(struct hid_device *hid, u32 usage) */ static int hid_scan_report(struct hid_device *hid) { - unsigned int page = 0, delim = 0; + struct hid_parser *parser; + struct hid_item item; __u8 *start = hid->dev_rdesc; __u8 *end = start + hid->dev_rsize; - unsigned int u, u_min = 0, u_max = 0; - struct hid_item item; + static int (*dispatch_type[])(struct hid_parser *parser, + struct hid_item *item) = { + hid_scan_main, + hid_parser_global, + hid_parser_local, + hid_parser_reserved + }; + + parser = vzalloc(sizeof(struct hid_parser)); + if (!parser) + return -ENOMEM; + parser->device = hid; hid->group = HID_GROUP_GENERIC; - while ((start = fetch_item(start, end, &item)) != NULL) { - if (item.format != HID_ITEM_FORMAT_SHORT) - return -EINVAL; - if (item.type == HID_ITEM_TYPE_GLOBAL) { - if (item.tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE) - page = item_udata(&item) << 16; - } else if (item.type == HID_ITEM_TYPE_LOCAL) { - if (delim > 1) - break; - u = item_udata(&item); - if (item.size <= 2) - u += page; - switch (item.tag) { - case HID_LOCAL_ITEM_TAG_DELIMITER: - delim += !!u; - break; - case HID_LOCAL_ITEM_TAG_USAGE: - hid_scan_usage(hid, u); - break; - case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: - u_min = u; - break; - case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: - u_max = u; - for (u = u_min; u <= u_max; u++) - hid_scan_usage(hid, u); - break; - } - } else if (page == HID_UP_SENSOR && - item.type == HID_ITEM_TYPE_MAIN && - item.tag == HID_MAIN_ITEM_TAG_BEGIN_COLLECTION && - (item_udata(&item) & 0xff) == HID_COLLECTION_PHYSICAL) - hid->group = HID_GROUP_SENSOR_HUB; - } + /* + * The parsing is simpler than the one in hid_open_report() as we should + * be robust against hid errors. Those errors will be raised by + * hid_open_report() anyway. + */ + while ((start = fetch_item(start, end, &item)) != NULL) + dispatch_type[item.type](parser, &item); + + vfree(parser); return 0; } -- cgit v1.2.3-59-g8ed1b From f961bd3516e4f699bbacff5d7f5247d6d87c59f0 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 22 Aug 2013 14:51:08 +0200 Subject: HID: detect Win 8 multitouch devices in core Detecting Win 8 multitouch devices in core allows us to set quirks before the device is parsed through hid_hw_start(). It also simplifies the detection of those devices in hid-multitouch and makes the handling of those devices cleaner. As Win 8 multitouch panels are in the group multitouch and rely on a special feature to be detected, this patch adds a bitfield in the parser. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Tested-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 16 ++++++++++++++++ drivers/hid/hid-multitouch.c | 24 +++++++++++------------- include/linux/hid.h | 4 ++++ 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ddd95f3e33c0..660dce964162 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -685,6 +685,13 @@ static void hid_scan_input_usage(struct hid_parser *parser, u32 usage) hid->group = HID_GROUP_MULTITOUCH; } +static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage) +{ + if (usage == 0xff0000c5 && parser->global.report_count == 256 && + parser->global.report_size == 8) + parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8; +} + static void hid_scan_collection(struct hid_parser *parser, unsigned type) { struct hid_device *hid = parser->device; @@ -714,6 +721,8 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) case HID_MAIN_ITEM_TAG_OUTPUT: break; case HID_MAIN_ITEM_TAG_FEATURE: + for (i = 0; i < parser->local.usage_index; i++) + hid_scan_feature_usage(parser, parser->local.usage[i]); break; } @@ -757,6 +766,13 @@ static int hid_scan_report(struct hid_device *hid) while ((start = fetch_item(start, end, &item)) != NULL) dispatch_type[item.type](parser, &item); + /* + * Handle special flags set during scanning. + */ + if ((parser->scan_flags & HID_SCAN_FLAG_MT_WIN_8) && + (hid->group == HID_GROUP_MULTITOUCH)) + hid->group = HID_GROUP_MULTITOUCH_WIN_8; + vfree(parser); return 0; } diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 0fe00e2552f2..c28ef86c7c67 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -133,6 +133,7 @@ static void mt_post_parse(struct mt_device *td); #define MT_CLS_NSMU 0x000a #define MT_CLS_DUAL_CONTACT_NUMBER 0x0010 #define MT_CLS_DUAL_CONTACT_ID 0x0011 +#define MT_CLS_WIN_8 0x0012 /* vendor specific classes */ #define MT_CLS_3M 0x0101 @@ -205,6 +206,11 @@ static struct mt_class mt_classes[] = { MT_QUIRK_CONTACT_CNT_ACCURATE | MT_QUIRK_SLOT_IS_CONTACTID, .maxcontacts = 2 }, + { .name = MT_CLS_WIN_8, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_IGNORE_DUPLICATES | + MT_QUIRK_HOVERING | + MT_QUIRK_CONTACT_CNT_ACCURATE }, /* * vendor specific classes @@ -332,19 +338,6 @@ static void mt_feature_mapping(struct hid_device *hdev, td->maxcontacts = td->mtclass.maxcontacts; break; - case 0xff0000c5: - if (field->report_count == 256 && field->report_size == 8) { - /* Win 8 devices need special quirks */ - __s32 *quirks = &td->mtclass.quirks; - *quirks |= MT_QUIRK_ALWAYS_VALID; - *quirks |= MT_QUIRK_IGNORE_DUPLICATES; - *quirks |= MT_QUIRK_HOVERING; - *quirks |= MT_QUIRK_CONTACT_CNT_ACCURATE; - *quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; - *quirks &= ~MT_QUIRK_VALID_IS_INRANGE; - *quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; - } - break; } } @@ -1346,6 +1339,11 @@ static const struct hid_device_id mt_devices[] = { /* Generic MT device */ { HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, + + /* Generic Win 8 certified MT device */ + { .driver_data = MT_CLS_WIN_8, + HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH_WIN_8, + HID_ANY_ID, HID_ANY_ID) }, { } }; MODULE_DEVICE_TABLE(hid, mt_devices); diff --git a/include/linux/hid.h b/include/linux/hid.h index 0c48991b0402..cef1e9b86cc4 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -295,6 +295,7 @@ struct hid_item { #define HID_GROUP_GENERIC 0x0001 #define HID_GROUP_MULTITOUCH 0x0002 #define HID_GROUP_SENSOR_HUB 0x0003 +#define HID_GROUP_MULTITOUCH_WIN_8 0x0004 /* * This is the global environment of the parser. This information is @@ -532,6 +533,8 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data) #define HID_GLOBAL_STACK_SIZE 4 #define HID_COLLECTION_STACK_SIZE 4 +#define HID_SCAN_FLAG_MT_WIN_8 0x00000001 + struct hid_parser { struct hid_global global; struct hid_global global_stack[HID_GLOBAL_STACK_SIZE]; @@ -540,6 +543,7 @@ struct hid_parser { unsigned collection_stack[HID_COLLECTION_STACK_SIZE]; unsigned collection_stack_ptr; struct hid_device *device; + unsigned scan_flags; }; struct hid_class_descriptor { -- cgit v1.2.3-59-g8ed1b From 595e9276ce68791317484ec7f0f9f2e0457c3b34 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 22 Aug 2013 14:51:09 +0200 Subject: HID: do not init input reports for Win 8 multitouch devices Some multitouch screens do not like to be polled for input reports. However, the Win8 spec says that all touches should be sent during each report, making the initialization of reports unnecessary. The Win7 spec is less precise, so do not use this for those devices. Add the quirk HID_QUIRK_NO_INIT_INPUT_REPORTS so that we do not have to introduce a quirk for each problematic device. This quirk makes the driver behave the same way the Win 8 does. It actually retrieves the features, but not the inputs. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Tested-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 12 ++++++++++++ drivers/hid/usbhid/hid-core.c | 11 ++++++++--- include/linux/hid.h | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index c28ef86c7c67..ac28f08c3866 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -951,6 +951,18 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) hdev->quirks |= HID_QUIRK_MULTI_INPUT; hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT; + /* + * Handle special quirks for Windows 8 certified devices. + */ + if (id->group == HID_GROUP_MULTITOUCH_WIN_8) + /* + * Some multitouch screens do not like to be polled for input + * reports. Fortunately, the Win8 spec says that all touches + * should be sent during each report, making the initialization + * of input reports unnecessary. + */ + hdev->quirks |= HID_QUIRK_NO_INIT_INPUT_REPORTS; + td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL); if (!td) { dev_err(&hdev->dev, "cannot allocate multitouch data\n"); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 99418285222c..55ea9c40140e 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -807,12 +807,17 @@ void usbhid_init_reports(struct hid_device *hid) { struct hid_report *report; struct usbhid_device *usbhid = hid->driver_data; + struct hid_report_enum *report_enum; int err, ret; - list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) - usbhid_submit_report(hid, report, USB_DIR_IN); + if (!(hid->quirks & HID_QUIRK_NO_INIT_INPUT_REPORTS)) { + report_enum = &hid->report_enum[HID_INPUT_REPORT]; + list_for_each_entry(report, &report_enum->report_list, list) + usbhid_submit_report(hid, report, USB_DIR_IN); + } - list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) + report_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(report, &report_enum->report_list, list) usbhid_submit_report(hid, report, USB_DIR_IN); err = 0; diff --git a/include/linux/hid.h b/include/linux/hid.h index cef1e9b86cc4..bc132d2a20aa 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -283,6 +283,7 @@ struct hid_item { #define HID_QUIRK_MULTI_INPUT 0x00000040 #define HID_QUIRK_HIDINPUT_FORCE 0x00000080 #define HID_QUIRK_NO_EMPTY_INPUT 0x00000100 +#define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 #define HID_QUIRK_NO_INIT_REPORTS 0x20000000 -- cgit v1.2.3-59-g8ed1b From 43622021d2e2b82ea03d883926605bdd0525e1d1 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:29:55 +0200 Subject: HID: validate HID report id size The "Report ID" field of a HID report is used to build indexes of reports. The kernel's index of these is limited to 256 entries, so any malicious device that sets a Report ID greater than 255 will trigger memory corruption on the host: [ 1347.156239] BUG: unable to handle kernel paging request at ffff88094958a878 [ 1347.156261] IP: [] hid_register_report+0x2a/0x8b CVE-2013-2888 Signed-off-by: Kees Cook Cc: stable@kernel.org Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 10 +++++++--- include/linux/hid.h | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 36668d1aca8f..5ea7d51e45b9 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -63,6 +63,8 @@ struct hid_report *hid_register_report(struct hid_device *device, unsigned type, struct hid_report_enum *report_enum = device->report_enum + type; struct hid_report *report; + if (id >= HID_MAX_IDS) + return NULL; if (report_enum->report_id_hash[id]) return report_enum->report_id_hash[id]; @@ -404,8 +406,10 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_ID: parser->global.report_id = item_udata(item); - if (parser->global.report_id == 0) { - hid_err(parser->device, "report_id 0 is invalid\n"); + if (parser->global.report_id == 0 || + parser->global.report_id >= HID_MAX_IDS) { + hid_err(parser->device, "report_id %u is invalid\n", + parser->global.report_id); return -1; } return 0; @@ -575,7 +579,7 @@ static void hid_close_report(struct hid_device *device) for (i = 0; i < HID_REPORT_TYPES; i++) { struct hid_report_enum *report_enum = device->report_enum + i; - for (j = 0; j < 256; j++) { + for (j = 0; j < HID_MAX_IDS; j++) { struct hid_report *report = report_enum->report_id_hash[j]; if (report) hid_free_report(report); diff --git a/include/linux/hid.h b/include/linux/hid.h index 0c48991b0402..ff545cc33c3a 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -393,10 +393,12 @@ struct hid_report { struct hid_device *device; /* associated device */ }; +#define HID_MAX_IDS 256 + struct hid_report_enum { unsigned numbered; struct list_head report_list; - struct hid_report *report_id_hash[256]; + struct hid_report *report_id_hash[HID_MAX_IDS]; }; #define HID_REPORT_TYPES 3 -- cgit v1.2.3-59-g8ed1b From 60cbd53e4bf623fe978e6f23a6da642e730fde3a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 1 Sep 2013 11:02:46 -0700 Subject: HID: uhid: add devname module alias For simple device node creation, add the devname module alias. Signed-off-by: Marcel Holtmann Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index fc307e0422af..9ab7dfc6c72c 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -659,3 +659,4 @@ module_exit(uhid_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Herrmann "); MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); +MODULE_ALIAS("devname:" UHID_NAME); -- cgit v1.2.3-59-g8ed1b From 9e0bf92c223dabe0789714f8f85f6e26f8f9cda4 Mon Sep 17 00:00:00 2001 From: Vasily Titskiy Date: Fri, 30 Aug 2013 18:25:04 -0400 Subject: HID: usbhid: quirk for N-Trig DuoSense Touch Screen The DuoSense touchscreen device causes a 10 second timeout. This fix removes the delay. Signed-off-by: Vasily Titskiy Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/usbhid/hid-quirks.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 83622f89fa4b..01f1b876fb9d 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -656,6 +656,7 @@ #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16 0x0012 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17 0x0013 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18 0x0014 +#define USB_DEVICE_ID_NTRIG_DUOSENSE 0x1500 #define USB_VENDOR_ID_ONTRAK 0x0a07 #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 19b8360f2330..07345521f421 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -109,6 +109,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS }, + { 0, 0 } }; -- cgit v1.2.3-59-g8ed1b From 1cde501bb4655e98fb832194beb88ac73be5a05d Mon Sep 17 00:00:00 2001 From: Bruno Prémont Date: Sat, 31 Aug 2013 14:07:48 +0200 Subject: HID: picolcd: Prevent NULL pointer dereference on _remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When picolcd is switched into bootloader mode (for FW flashing) make sure not to try to dereference NULL-pointers of feature-devices during unplug/unbind. This fixes following BUG: BUG: unable to handle kernel NULL pointer dereference at 00000298 IP: [] picolcd_exit_framebuffer+0x1b/0x80 [hid_picolcd] *pde = 00000000 Oops: 0000 [#1] Modules linked in: hid_picolcd syscopyarea sysfillrect sysimgblt fb_sys_fops CPU: 0 PID: 15 Comm: khubd Not tainted 3.11.0-rc7-00002-g50d62d4 #2 EIP: 0060:[] EFLAGS: 00010292 CPU: 0 EIP is at picolcd_exit_framebuffer+0x1b/0x80 [hid_picolcd] Call Trace: [] picolcd_remove+0xcb/0x120 [hid_picolcd] [] hid_device_remove+0x59/0xc0 [] __device_release_driver+0x5a/0xb0 [] device_release_driver+0x1f/0x30 [] bus_remove_device+0x9d/0xd0 [] device_del+0xd5/0x150 [] hid_destroy_device+0x24/0x60 [] usbhid_disconnect+0x1b/0x40 ... Signed-off-by: Bruno Prémont Cc: stable@kernel.org Signed-off-by: Jiri Kosina --- drivers/hid/hid-picolcd_cir.c | 3 ++- drivers/hid/hid-picolcd_fb.c | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c index e346038f0f11..59d5eb1e742c 100644 --- a/drivers/hid/hid-picolcd_cir.c +++ b/drivers/hid/hid-picolcd_cir.c @@ -145,6 +145,7 @@ void picolcd_exit_cir(struct picolcd_data *data) struct rc_dev *rdev = data->rc_dev; data->rc_dev = NULL; - rc_unregister_device(rdev); + if (rdev) + rc_unregister_device(rdev); } diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index 591f6b22aa94..c930ab8554ea 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c @@ -593,10 +593,14 @@ err_nomem: void picolcd_exit_framebuffer(struct picolcd_data *data) { struct fb_info *info = data->fb_info; - struct picolcd_fb_data *fbdata = info->par; + struct picolcd_fb_data *fbdata; unsigned long flags; + if (!info) + return; + device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); + fbdata = info->par; /* disconnect framebuffer from HID dev */ spin_lock_irqsave(&fbdata->lock, flags); -- cgit v1.2.3-59-g8ed1b From a4be0ed39f2b1ea990804ea54e39bc42d17ed5a5 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Fri, 30 Aug 2013 14:10:07 +0200 Subject: HID: roccat: add support for KonePureOptical v2 KonePureOptical is a KonePure with different sensor. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-roccat-konepure.c | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 627fea6593a3..3c28e5bf3675 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1694,6 +1694,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 01f1b876fb9d..6ad292eee4ce 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -715,6 +715,7 @@ #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 #define USB_DEVICE_ID_ROCCAT_KONEPURE 0x2dbe +#define USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL 0x2db4 #define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22 #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 #define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c index c79d0b06c143..5850959d48f5 100644 --- a/drivers/hid/hid-roccat-konepure.c +++ b/drivers/hid/hid-roccat-konepure.c @@ -262,6 +262,7 @@ static int konepure_raw_event(struct hid_device *hdev, static const struct hid_device_id konepure_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) }, { } }; @@ -300,5 +301,5 @@ module_init(konepure_init); module_exit(konepure_exit); MODULE_AUTHOR("Stefan Achatz"); -MODULE_DESCRIPTION("USB Roccat KonePure driver"); +MODULE_DESCRIPTION("USB Roccat KonePure/Optical driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 6c2794a2984f4c17a58117a68703cc7640f01c5a Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 2 Sep 2013 13:43:00 +0200 Subject: HID: battery: don't do DMA from stack Instead of using data from stack for DMA in hidinput_get_battery_property(), allocate the buffer dynamically. Cc: stable@kernel.org Reported-by: Richard Ryniker Reported-by: Alan Stern Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7480799e535c..3fc4034a4367 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -340,7 +340,7 @@ static int hidinput_get_battery_property(struct power_supply *psy, { struct hid_device *dev = container_of(psy, struct hid_device, battery); int ret = 0; - __u8 buf[2] = {}; + __u8 *buf; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: @@ -349,12 +349,19 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: + + buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + break; + } ret = dev->hid_get_raw_report(dev, dev->battery_report_id, - buf, sizeof(buf), + buf, 2, dev->battery_report_type); if (ret != 2) { ret = -ENODATA; + kfree(buf); break; } ret = 0; @@ -364,6 +371,7 @@ static int hidinput_get_battery_property(struct power_supply *psy, buf[1] <= dev->battery_max) val->intval = (100 * (buf[1] - dev->battery_min)) / (dev->battery_max - dev->battery_min); + kfree(buf); break; case POWER_SUPPLY_PROP_MODEL_NAME: -- cgit v1.2.3-59-g8ed1b From 61e00655e9cb82e034eb72b95a51072e718d14a7 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 26 Aug 2013 19:14:46 +0200 Subject: Input: introduce BTN/ABS bits for drums and guitars There are a bunch of guitar and drums devices out there that all report similar data. To avoid reporting this as BTN_MISC or ABS_MISC, we allocate some proper namespace for them. Note that most of these devices are toys and we cannot report any sophisticated physics via this API. I did some google-images research and tried to provide definitions that work with all common devices. That's why I went with 4 toms, 4 cymbals, one bass, one hi-hat. I haven't seen other drums and I doubt that we need any additions to that. Anyway, the naming-scheme is intentionally done in an extensible way. For guitars, we support 5 frets (normally aligned vertically, compared to the real horizontal layouts), a single strum-bar with up/down directions, an optional fret-board and a whammy-bar. Most of the devices provide pressure values so I went with ABS_* bits. If we ever support devices which only provide digital input, we have to decide whether to emulate pressure data or add additional BTN_* bits. If someone is not familiar with these devices, here are two pictures which provide almost all introduced interfaces (or try the given keywords with a google-image search): Guitar: ("guitar hero world tour guitar") http://images1.wikia.nocookie.net/__cb20120911023442/applezone/es/images/f/f9/Wii_Guitar.jpg Drums: ("guitar hero drums") http://oyster.ignimgs.com/franchises/images/03/55/35526_band-hero-drum-set-hands-on-20090929040735768.jpg Signed-off-by: David Herrmann Acked-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- include/linux/mod_devicetable.h | 2 +- include/uapi/linux/input.h | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index b62d4af6c667..65f8a8c4ebbc 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -277,7 +277,7 @@ struct pcmcia_device_id { #define INPUT_DEVICE_ID_KEY_MIN_INTERESTING 0x71 #define INPUT_DEVICE_ID_KEY_MAX 0x2ff #define INPUT_DEVICE_ID_REL_MAX 0x0f -#define INPUT_DEVICE_ID_ABS_MAX 0x3f +#define INPUT_DEVICE_ID_ABS_MAX 0x4f #define INPUT_DEVICE_ID_MSC_MAX 0x07 #define INPUT_DEVICE_ID_LED_MAX 0x0f #define INPUT_DEVICE_ID_SND_MAX 0x07 diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index d584047b072b..76457eef172a 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -716,6 +716,14 @@ struct input_keymap_entry { #define BTN_DPAD_LEFT 0x222 #define BTN_DPAD_RIGHT 0x223 +#define BTN_FRET_FAR_UP 0x224 +#define BTN_FRET_UP 0x225 +#define BTN_FRET_MID 0x226 +#define BTN_FRET_LOW 0x227 +#define BTN_FRET_FAR_LOW 0x228 +#define BTN_STRUM_BAR_UP 0x229 +#define BTN_STRUM_BAR_DOWN 0x22a + #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY2 0x2c1 @@ -829,8 +837,21 @@ struct input_keymap_entry { #define ABS_MT_TOOL_X 0x3c /* Center X tool position */ #define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ - -#define ABS_MAX 0x3f +/* Drums and guitars (mostly toys) */ +#define ABS_TOM_FAR_LEFT 0x40 +#define ABS_TOM_LEFT 0x41 +#define ABS_TOM_RIGHT 0x42 +#define ABS_TOM_FAR_RIGHT 0x43 +#define ABS_CYMBAL_FAR_LEFT 0x44 +#define ABS_CYMBAL_LEFT 0x45 +#define ABS_CYMBAL_RIGHT 0x46 +#define ABS_CYMBAL_FAR_RIGHT 0x47 +#define ABS_BASS 0x48 +#define ABS_HI_HAT 0x49 +#define ABS_FRET_BOARD 0x4a /* Guitar fret board, vertical pos */ +#define ABS_WHAMMY_BAR 0x4b /* Guitar whammy bar (or vibrato) */ + +#define ABS_MAX 0x4f #define ABS_CNT (ABS_MAX+1) /* -- cgit v1.2.3-59-g8ed1b From 73f8645db1913ab2475ec3c1cee8d5f748963aa7 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 26 Aug 2013 19:14:47 +0200 Subject: HID: wiimote: add support for Guitar-Hero drums Guitar-Hero comes with a drums extension. Use the newly introduced input drums-bits to report this back to user-space. This is a usual extension like any other device. Nothing special to take care of. We report this to user-space as "Nintendo Wii Remote Drums". There are other drums (like "RockBand" drums) which we currently do not support and maybe will at some point. However, it is quite likely that we can report these via the same interface. This allows user-space to work with them without knowing the exact branding. I couldn't find anyone who owns a "RockBand" device, though. Initial-work-by: Nicolas Adenis-Lamarre Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-wiimote-core.c | 7 ++ drivers/hid/hid-wiimote-modules.c | 218 ++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-wiimote.h | 2 + 3 files changed, 227 insertions(+) diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 660209824e56..946cdeff4798 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -456,6 +456,9 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem) return WIIMOTE_EXT_BALANCE_BOARD; if (rmem[4] == 0x01 && rmem[5] == 0x20) return WIIMOTE_EXT_PRO_CONTROLLER; + if (rmem[0] == 0x01 && rmem[1] == 0x00 && + rmem[4] == 0x01 && rmem[5] == 0x03) + return WIIMOTE_EXT_GUITAR_HERO_DRUMS; return WIIMOTE_EXT_UNKNOWN; } @@ -489,6 +492,7 @@ static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype) /* map MP with correct pass-through mode */ switch (exttype) { case WIIMOTE_EXT_CLASSIC_CONTROLLER: + case WIIMOTE_EXT_GUITAR_HERO_DRUMS: wmem = 0x07; break; case WIIMOTE_EXT_NUNCHUK: @@ -1079,6 +1083,7 @@ static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = { [WIIMOTE_EXT_CLASSIC_CONTROLLER] = "Nintendo Wii Classic Controller", [WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board", [WIIMOTE_EXT_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller", + [WIIMOTE_EXT_GUITAR_HERO_DRUMS] = "Nintendo Wii Guitar Hero Drums", }; /* @@ -1665,6 +1670,8 @@ static ssize_t wiimote_ext_show(struct device *dev, return sprintf(buf, "balanceboard\n"); case WIIMOTE_EXT_PRO_CONTROLLER: return sprintf(buf, "procontroller\n"); + case WIIMOTE_EXT_GUITAR_HERO_DRUMS: + return sprintf(buf, "drums\n"); case WIIMOTE_EXT_UNKNOWN: /* fallthrough */ default: diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c index 2e7d644dba18..08a44a7fc5fd 100644 --- a/drivers/hid/hid-wiimote-modules.c +++ b/drivers/hid/hid-wiimote-modules.c @@ -1833,6 +1833,223 @@ static const struct wiimod_ops wiimod_pro = { .in_ext = wiimod_pro_in_ext, }; +/* + * Drums + * Guitar-Hero, Rock-Band and other games came bundled with drums which can + * be plugged as extension to a Wiimote. Drum-reports are still not entirely + * figured out, but the most important information is known. + * We create a separate device for drums and report all information via this + * input device. + */ + +static inline void wiimod_drums_report_pressure(struct wiimote_data *wdata, + __u8 none, __u8 which, + __u8 pressure, __u8 onoff, + __u8 *store, __u16 code, + __u8 which_code) +{ + static const __u8 default_pressure = 3; + + if (!none && which == which_code) { + *store = pressure; + input_report_abs(wdata->extension.input, code, *store); + } else if (onoff != !!*store) { + *store = onoff ? default_pressure : 0; + input_report_abs(wdata->extension.input, code, *store); + } +} + +static void wiimod_drums_in_ext(struct wiimote_data *wdata, const __u8 *ext) +{ + __u8 pressure, which, none, hhp, sx, sy; + __u8 o, r, y, g, b, bass, bm, bp; + + /* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | 0 | 0 | SX <5:0> | + * 2 | 0 | 0 | SY <5:0> | + * -----+-----+-----+-----------------------------+-----+ + * 3 | HPP | NON | WHICH <5:1> | ? | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 4 | SOFT <7:5> | 0 | 1 | 1 | 0 | ? | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | ? | 1 | 1 | B- | 1 | B+ | 1 | ? | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | O | R | Y | G | B | BSS | 1 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * All buttons are 0 if pressed + * + * With Motion+ enabled, the following bits will get invalid: + * Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | 0 | 0 | SX <5:1> |XXXXX| + * 2 | 0 | 0 | SY <5:1> |XXXXX| + * -----+-----+-----+-----------------------------+-----+ + * 3 | HPP | NON | WHICH <5:1> | ? | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 4 | SOFT <7:5> | 0 | 1 | 1 | 0 | ? | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | ? | 1 | 1 | B- | 1 | B+ | 1 |XXXXX| + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | O | R | Y | G | B | BSS |XXXXX|XXXXX| + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + */ + + pressure = 7 - (ext[3] >> 5); + which = (ext[2] >> 1) & 0x1f; + none = !!(ext[2] & 0x40); + hhp = !(ext[2] & 0x80); + sx = ext[0] & 0x3f; + sy = ext[1] & 0x3f; + o = !(ext[5] & 0x80); + r = !(ext[5] & 0x40); + y = !(ext[5] & 0x20); + g = !(ext[5] & 0x10); + b = !(ext[5] & 0x08); + bass = !(ext[5] & 0x04); + bm = !(ext[4] & 0x10); + bp = !(ext[4] & 0x04); + + wiimod_drums_report_pressure(wdata, none, which, pressure, + o, &wdata->state.pressure_drums[0], + ABS_CYMBAL_RIGHT, 0x0e); + wiimod_drums_report_pressure(wdata, none, which, pressure, + r, &wdata->state.pressure_drums[1], + ABS_TOM_LEFT, 0x19); + wiimod_drums_report_pressure(wdata, none, which, pressure, + y, &wdata->state.pressure_drums[2], + ABS_CYMBAL_LEFT, 0x11); + wiimod_drums_report_pressure(wdata, none, which, pressure, + g, &wdata->state.pressure_drums[3], + ABS_TOM_FAR_RIGHT, 0x12); + wiimod_drums_report_pressure(wdata, none, which, pressure, + b, &wdata->state.pressure_drums[4], + ABS_TOM_RIGHT, 0x0f); + + /* Bass shares pressure with hi-hat (set via hhp) */ + wiimod_drums_report_pressure(wdata, none, hhp ? 0xff : which, pressure, + bass, &wdata->state.pressure_drums[5], + ABS_BASS, 0x1b); + /* Hi-hat has no on/off values, just pressure. Force to off/0. */ + wiimod_drums_report_pressure(wdata, none, hhp ? which : 0xff, pressure, + 0, &wdata->state.pressure_drums[6], + ABS_HI_HAT, 0x0e); + + input_report_abs(wdata->extension.input, ABS_X, sx - 0x20); + input_report_abs(wdata->extension.input, ABS_Y, sy - 0x20); + + input_report_key(wdata->extension.input, BTN_START, bp); + input_report_key(wdata->extension.input, BTN_SELECT, bm); + + input_sync(wdata->extension.input); +} + +static int wiimod_drums_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_drums_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static int wiimod_drums_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret; + + wdata->extension.input = input_allocate_device(); + if (!wdata->extension.input) + return -ENOMEM; + + input_set_drvdata(wdata->extension.input, wdata); + wdata->extension.input->open = wiimod_drums_open; + wdata->extension.input->close = wiimod_drums_close; + wdata->extension.input->dev.parent = &wdata->hdev->dev; + wdata->extension.input->id.bustype = wdata->hdev->bus; + wdata->extension.input->id.vendor = wdata->hdev->vendor; + wdata->extension.input->id.product = wdata->hdev->product; + wdata->extension.input->id.version = wdata->hdev->version; + wdata->extension.input->name = WIIMOTE_NAME " Drums"; + + set_bit(EV_KEY, wdata->extension.input->evbit); + set_bit(BTN_START, wdata->extension.input->keybit); + set_bit(BTN_SELECT, wdata->extension.input->keybit); + + set_bit(EV_ABS, wdata->extension.input->evbit); + set_bit(ABS_X, wdata->extension.input->absbit); + set_bit(ABS_Y, wdata->extension.input->absbit); + set_bit(ABS_TOM_LEFT, wdata->extension.input->absbit); + set_bit(ABS_TOM_RIGHT, wdata->extension.input->absbit); + set_bit(ABS_TOM_FAR_RIGHT, wdata->extension.input->absbit); + set_bit(ABS_CYMBAL_LEFT, wdata->extension.input->absbit); + set_bit(ABS_CYMBAL_RIGHT, wdata->extension.input->absbit); + set_bit(ABS_BASS, wdata->extension.input->absbit); + set_bit(ABS_HI_HAT, wdata->extension.input->absbit); + input_set_abs_params(wdata->extension.input, + ABS_X, -32, 31, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_Y, -32, 31, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_TOM_LEFT, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_TOM_RIGHT, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_TOM_FAR_RIGHT, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_CYMBAL_LEFT, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_CYMBAL_RIGHT, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_BASS, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_HI_HAT, 0, 7, 0, 0); + + ret = input_register_device(wdata->extension.input); + if (ret) + goto err_free; + + return 0; + +err_free: + input_free_device(wdata->extension.input); + wdata->extension.input = NULL; + return ret; +} + +static void wiimod_drums_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->extension.input) + return; + + input_unregister_device(wdata->extension.input); + wdata->extension.input = NULL; +} + +static const struct wiimod_ops wiimod_drums = { + .flags = 0, + .arg = 0, + .probe = wiimod_drums_probe, + .remove = wiimod_drums_remove, + .in_ext = wiimod_drums_in_ext, +}; + /* * Builtin Motion Plus * This module simply sets the WIIPROTO_FLAG_BUILTIN_MP protocol flag which @@ -2083,4 +2300,5 @@ const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = { [WIIMOTE_EXT_CLASSIC_CONTROLLER] = &wiimod_classic, [WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard, [WIIMOTE_EXT_PRO_CONTROLLER] = &wiimod_pro, + [WIIMOTE_EXT_GUITAR_HERO_DRUMS] = &wiimod_drums, }; diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index f1474f372c0b..6e18b55951fb 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -88,6 +88,7 @@ enum wiimote_exttype { WIIMOTE_EXT_CLASSIC_CONTROLLER, WIIMOTE_EXT_BALANCE_BOARD, WIIMOTE_EXT_PRO_CONTROLLER, + WIIMOTE_EXT_GUITAR_HERO_DRUMS, WIIMOTE_EXT_NUM, }; @@ -135,6 +136,7 @@ struct wiimote_state { /* calibration data */ __u16 calib_bboard[4][3]; + __u8 pressure_drums[7]; }; struct wiimote_data { -- cgit v1.2.3-59-g8ed1b From 8e22ecb603c88b7397ab2e80b7b0811b8a1318f5 Mon Sep 17 00:00:00 2001 From: Nicolas Adenis-Lamarre Date: Mon, 26 Aug 2013 19:14:48 +0200 Subject: HID: wiimote: add support for Guitar-Hero guitars Apart from drums, Guitar-Hero also ships with guitars. Use the recently introduced input ABS/BTN-bits to report this to user-space. Devices are reported as "Nintendo Wii Remote Guitar". If I ever get my hands on "RockBand" guitars, I will try to report them via the same interface so user-space does not have to bother which device it deals with. Signed-off-by: Nicolas.Adenis-Lamarre (add commit-msg and adjust to new BTN_* IDs) Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-wiimote-core.c | 7 ++ drivers/hid/hid-wiimote-modules.c | 174 ++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-wiimote.h | 1 + 3 files changed, 182 insertions(+) diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 946cdeff4798..d3055d1dbc06 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -459,6 +459,9 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem) if (rmem[0] == 0x01 && rmem[1] == 0x00 && rmem[4] == 0x01 && rmem[5] == 0x03) return WIIMOTE_EXT_GUITAR_HERO_DRUMS; + if (rmem[0] == 0x00 && rmem[1] == 0x00 && + rmem[4] == 0x01 && rmem[5] == 0x03) + return WIIMOTE_EXT_GUITAR_HERO_GUITAR; return WIIMOTE_EXT_UNKNOWN; } @@ -493,6 +496,7 @@ static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype) switch (exttype) { case WIIMOTE_EXT_CLASSIC_CONTROLLER: case WIIMOTE_EXT_GUITAR_HERO_DRUMS: + case WIIMOTE_EXT_GUITAR_HERO_GUITAR: wmem = 0x07; break; case WIIMOTE_EXT_NUNCHUK: @@ -1084,6 +1088,7 @@ static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = { [WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board", [WIIMOTE_EXT_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller", [WIIMOTE_EXT_GUITAR_HERO_DRUMS] = "Nintendo Wii Guitar Hero Drums", + [WIIMOTE_EXT_GUITAR_HERO_GUITAR] = "Nintendo Wii Guitar Hero Guitar", }; /* @@ -1672,6 +1677,8 @@ static ssize_t wiimote_ext_show(struct device *dev, return sprintf(buf, "procontroller\n"); case WIIMOTE_EXT_GUITAR_HERO_DRUMS: return sprintf(buf, "drums\n"); + case WIIMOTE_EXT_GUITAR_HERO_GUITAR: + return sprintf(buf, "guitar\n"); case WIIMOTE_EXT_UNKNOWN: /* fallthrough */ default: diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c index 08a44a7fc5fd..7e124c351e67 100644 --- a/drivers/hid/hid-wiimote-modules.c +++ b/drivers/hid/hid-wiimote-modules.c @@ -2050,6 +2050,179 @@ static const struct wiimod_ops wiimod_drums = { .in_ext = wiimod_drums_in_ext, }; +/* + * Guitar + * Guitar-Hero, Rock-Band and other games came bundled with guitars which can + * be plugged as extension to a Wiimote. + * We create a separate device for guitars and report all information via this + * input device. + */ + +static void wiimod_guitar_in_ext(struct wiimote_data *wdata, const __u8 *ext) +{ + __u8 sx, sy, tb, wb, bd, bm, bp, bo, br, bb, bg, by, bu; + + /* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | 0 | 0 | SX <5:0> | + * 2 | 0 | 0 | SY <5:0> | + * -----+-----+-----+-----+-----------------------------+ + * 3 | 0 | 0 | 0 | TB <4:0> | + * -----+-----+-----+-----+-----------------------------+ + * 4 | 0 | 0 | 0 | WB <4:0> | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | 1 | BD | 1 | B- | 1 | B+ | 1 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | BO | BR | BB | BG | BY | 1 | 1 | BU | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * All buttons are 0 if pressed + * + * With Motion+ enabled, the following bits will get invalid: + * Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | 0 | 0 | SX <5:1> |XXXXX| + * 2 | 0 | 0 | SY <5:1> |XXXXX| + * -----+-----+-----+-----+-----------------------+-----+ + * 3 | 0 | 0 | 0 | TB <4:0> | + * -----+-----+-----+-----+-----------------------------+ + * 4 | 0 | 0 | 0 | WB <4:0> | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | 1 | BD | 1 | B- | 1 | B+ | 1 |XXXXX| + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | BO | BR | BB | BG | BY | 1 |XXXXX|XXXXX| + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + */ + + sx = ext[0] & 0x3f; + sy = ext[1] & 0x3f; + tb = ext[2] & 0x1f; + wb = ext[3] & 0x1f; + bd = !(ext[4] & 0x40); + bm = !(ext[4] & 0x10); + bp = !(ext[4] & 0x04); + bo = !(ext[5] & 0x80); + br = !(ext[5] & 0x40); + bb = !(ext[5] & 0x20); + bg = !(ext[5] & 0x10); + by = !(ext[5] & 0x08); + bu = !(ext[5] & 0x01); + + input_report_abs(wdata->extension.input, ABS_X, sx - 0x20); + input_report_abs(wdata->extension.input, ABS_Y, sy - 0x20); + input_report_abs(wdata->extension.input, ABS_FRET_BOARD, tb); + input_report_abs(wdata->extension.input, ABS_WHAMMY_BAR, wb - 0x10); + + input_report_key(wdata->extension.input, BTN_MODE, bm); + input_report_key(wdata->extension.input, BTN_START, bp); + input_report_key(wdata->extension.input, BTN_STRUM_BAR_UP, bu); + input_report_key(wdata->extension.input, BTN_STRUM_BAR_DOWN, bd); + input_report_key(wdata->extension.input, BTN_FRET_FAR_UP, bg); + input_report_key(wdata->extension.input, BTN_FRET_UP, br); + input_report_key(wdata->extension.input, BTN_FRET_MID, by); + input_report_key(wdata->extension.input, BTN_FRET_LOW, bb); + input_report_key(wdata->extension.input, BTN_FRET_FAR_LOW, bo); + + input_sync(wdata->extension.input); +} + +static int wiimod_guitar_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_guitar_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static int wiimod_guitar_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret; + + wdata->extension.input = input_allocate_device(); + if (!wdata->extension.input) + return -ENOMEM; + + input_set_drvdata(wdata->extension.input, wdata); + wdata->extension.input->open = wiimod_guitar_open; + wdata->extension.input->close = wiimod_guitar_close; + wdata->extension.input->dev.parent = &wdata->hdev->dev; + wdata->extension.input->id.bustype = wdata->hdev->bus; + wdata->extension.input->id.vendor = wdata->hdev->vendor; + wdata->extension.input->id.product = wdata->hdev->product; + wdata->extension.input->id.version = wdata->hdev->version; + wdata->extension.input->name = WIIMOTE_NAME " Guitar"; + + set_bit(EV_KEY, wdata->extension.input->evbit); + set_bit(BTN_MODE, wdata->extension.input->keybit); + set_bit(BTN_START, wdata->extension.input->keybit); + set_bit(BTN_FRET_FAR_UP, wdata->extension.input->keybit); + set_bit(BTN_FRET_UP, wdata->extension.input->keybit); + set_bit(BTN_FRET_MID, wdata->extension.input->keybit); + set_bit(BTN_FRET_LOW, wdata->extension.input->keybit); + set_bit(BTN_FRET_FAR_LOW, wdata->extension.input->keybit); + set_bit(BTN_STRUM_BAR_UP, wdata->extension.input->keybit); + set_bit(BTN_STRUM_BAR_DOWN, wdata->extension.input->keybit); + + set_bit(EV_ABS, wdata->extension.input->evbit); + set_bit(ABS_X, wdata->extension.input->absbit); + set_bit(ABS_Y, wdata->extension.input->absbit); + set_bit(ABS_FRET_BOARD, wdata->extension.input->absbit); + set_bit(ABS_WHAMMY_BAR, wdata->extension.input->absbit); + input_set_abs_params(wdata->extension.input, + ABS_X, -32, 31, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_Y, -32, 31, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_FRET_BOARD, 0, 0x1f, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_WHAMMY_BAR, 0, 0x0f, 1, 1); + + ret = input_register_device(wdata->extension.input); + if (ret) + goto err_free; + + return 0; + +err_free: + input_free_device(wdata->extension.input); + wdata->extension.input = NULL; + return ret; +} + +static void wiimod_guitar_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->extension.input) + return; + + input_unregister_device(wdata->extension.input); + wdata->extension.input = NULL; +} + +static const struct wiimod_ops wiimod_guitar = { + .flags = 0, + .arg = 0, + .probe = wiimod_guitar_probe, + .remove = wiimod_guitar_remove, + .in_ext = wiimod_guitar_in_ext, +}; + /* * Builtin Motion Plus * This module simply sets the WIIPROTO_FLAG_BUILTIN_MP protocol flag which @@ -2301,4 +2474,5 @@ const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = { [WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard, [WIIMOTE_EXT_PRO_CONTROLLER] = &wiimod_pro, [WIIMOTE_EXT_GUITAR_HERO_DRUMS] = &wiimod_drums, + [WIIMOTE_EXT_GUITAR_HERO_GUITAR] = &wiimod_guitar, }; diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index 6e18b55951fb..379cdfb6bd25 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -89,6 +89,7 @@ enum wiimote_exttype { WIIMOTE_EXT_BALANCE_BOARD, WIIMOTE_EXT_PRO_CONTROLLER, WIIMOTE_EXT_GUITAR_HERO_DRUMS, + WIIMOTE_EXT_GUITAR_HERO_GUITAR, WIIMOTE_EXT_NUM, }; -- cgit v1.2.3-59-g8ed1b From 8c89cc17b91992845bd635813cd162fe8dfcec6e Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sun, 1 Sep 2013 15:31:44 +0200 Subject: HID: Correct the USB IDs for the new Macbook Air 6 A recent patch (9d9a04ee) added support for the new machine, but got the sequence of USB ids wrong. Reports from both Ian and Linus T show that the 0x0291 id is for ISO, not ANSI, which should have the missing number 0x0290. This patchs moves the three numbers accordingly, fixing the problem. Reported-and-tested-by: Ian Munsie Tested-by: Linus G Thiel Signed-off-by: Henrik Rydberg Acked-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 6 +++--- drivers/input/mouse/bcm5974.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 9386df57f195..e60e8d530697 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -135,9 +135,9 @@ #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 -#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0291 -#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0292 -#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0293 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b #define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index 4ef4d5e198ae..a73f9618b0ad 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -89,9 +89,9 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a #define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b /* MacbookAir6,2 (unibody, June 2013) */ -#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0291 -#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0292 -#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0293 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 #define BCM5974_DEVICE(prod) { \ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ -- cgit v1.2.3-59-g8ed1b From f5e4e7fdd57691d5308cf854dd0dbcfd58799e9a Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 1 Sep 2013 23:45:18 +0200 Subject: HID: uhid: improve uhid example client This extends the uhid example client. It properly documents the built-in report-descriptor an adds explicit report-numbers. Furthermore, LED output reports are added to utilize the new UHID output reports of the kernel. Support for 3 basic LEDs is added and a small report-parser to print debug messages if output reports were received. To test this, simply write the EV_LED+LED_CAPSL+1 event to the evdev device-node of the uhid-device and the kernel will forward it to your uhid client. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- samples/uhid/uhid-example.c | 123 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 20 deletions(-) diff --git a/samples/uhid/uhid-example.c b/samples/uhid/uhid-example.c index 03ce3c059a5e..7d58a4b8d324 100644 --- a/samples/uhid/uhid-example.c +++ b/samples/uhid/uhid-example.c @@ -1,14 +1,15 @@ /* * UHID Example * - * Copyright (c) 2012 David Herrmann + * Copyright (c) 2012-2013 David Herrmann * * The code may be used by anyone for any purpose, * and can serve as a starting point for developing * applications using uhid. */ -/* UHID Example +/* + * UHID Example * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this * program as root and then use the following keys to control the mouse: * q: Quit the application @@ -22,6 +23,11 @@ * r: Move wheel up * f: Move wheel down * + * Additionally to 3 button mouse, 3 keyboard LEDs are also supported (LED_NUML, + * LED_CAPSL and LED_SCROLLL). The device doesn't generate any related keyboard + * events, though. You need to manually write the EV_LED/LED_XY/1 activation + * input event to the evdev device to see it being sent to this device. + * * If uhid is not available as /dev/uhid, then you can pass a different path as * first argument. * If is not installed in /usr, then compile this with: @@ -41,11 +47,12 @@ #include #include -/* HID Report Desciptor - * We emulate a basic 3 button mouse with wheel. This is the report-descriptor - * as the kernel will parse it: +/* + * HID Report Desciptor + * We emulate a basic 3 button mouse with wheel and 3 keyboard LEDs. This is + * the report-descriptor as the kernel will parse it: * - * INPUT[INPUT] + * INPUT(1)[INPUT] * Field(0) * Physical(GenericDesktop.Pointer) * Application(GenericDesktop.Mouse) @@ -72,6 +79,19 @@ * Report Count(3) * Report Offset(8) * Flags( Variable Relative ) + * OUTPUT(2)[OUTPUT] + * Field(0) + * Application(GenericDesktop.Keyboard) + * Usage(3) + * LED.NumLock + * LED.CapsLock + * LED.ScrollLock + * Logical Minimum(0) + * Logical Maximum(1) + * Report Size(1) + * Report Count(3) + * Report Offset(0) + * Flags( Variable Absolute ) * * This is the mapping that we expect: * Button.0001 ---> Key.LeftBtn @@ -80,19 +100,59 @@ * GenericDesktop.X ---> Relative.X * GenericDesktop.Y ---> Relative.Y * GenericDesktop.Wheel ---> Relative.Wheel + * LED.NumLock ---> LED.NumLock + * LED.CapsLock ---> LED.CapsLock + * LED.ScrollLock ---> LED.ScrollLock * * This information can be verified by reading /sys/kernel/debug/hid//rdesc * This file should print the same information as showed above. */ static unsigned char rdesc[] = { - 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, - 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, - 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, - 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, - 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, - 0x15, 0x80, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, - 0x81, 0x06, 0xc0, 0xc0, + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x02, /* USAGE (Mouse) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x09, 0x01, /* USAGE (Pointer) */ + 0xa1, 0x00, /* COLLECTION (Physical) */ + 0x85, 0x01, /* REPORT_ID (1) */ + 0x05, 0x09, /* USAGE_PAGE (Button) */ + 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x81, 0x01, /* INPUT (Cnst,Var,Abs) */ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x30, /* USAGE (X) */ + 0x09, 0x31, /* USAGE (Y) */ + 0x09, 0x38, /* USAGE (WHEEL) */ + 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ + 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x81, 0x06, /* INPUT (Data,Var,Rel) */ + 0xc0, /* END_COLLECTION */ + 0xc0, /* END_COLLECTION */ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x06, /* USAGE (Keyboard) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x85, 0x02, /* REPORT_ID (2) */ + 0x05, 0x08, /* USAGE_PAGE (Led) */ + 0x19, 0x01, /* USAGE_MINIMUM (1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x91, 0x01, /* Output (Cnst,Var,Abs) */ + 0xc0, /* END_COLLECTION */ }; static int uhid_write(int fd, const struct uhid_event *ev) @@ -140,6 +200,27 @@ static void destroy(int fd) uhid_write(fd, &ev); } +/* This parses raw output reports sent by the kernel to the device. A normal + * uhid program shouldn't do this but instead just forward the raw report. + * However, for ducomentational purposes, we try to detect LED events here and + * print debug messages for it. */ +static void handle_output(struct uhid_event *ev) +{ + /* LED messages are adverised via OUTPUT reports; ignore the rest */ + if (ev->u.output.rtype != UHID_OUTPUT_REPORT) + return; + /* LED reports have length 2 bytes */ + if (ev->u.output.size != 2) + return; + /* first byte is report-id which is 0x02 for LEDs in our rdesc */ + if (ev->u.output.data[0] != 0x2) + return; + + /* print flags payload */ + fprintf(stderr, "LED output report received with flags %x\n", + ev->u.output.data[1]); +} + static int event(int fd) { struct uhid_event ev; @@ -174,6 +255,7 @@ static int event(int fd) break; case UHID_OUTPUT: fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); + handle_output(&ev); break; case UHID_OUTPUT_EV: fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); @@ -198,18 +280,19 @@ static int send_event(int fd) memset(&ev, 0, sizeof(ev)); ev.type = UHID_INPUT; - ev.u.input.size = 4; + ev.u.input.size = 5; + ev.u.input.data[0] = 0x1; if (btn1_down) - ev.u.input.data[0] |= 0x1; + ev.u.input.data[1] |= 0x1; if (btn2_down) - ev.u.input.data[0] |= 0x2; + ev.u.input.data[1] |= 0x2; if (btn3_down) - ev.u.input.data[0] |= 0x4; + ev.u.input.data[1] |= 0x4; - ev.u.input.data[1] = abs_hor; - ev.u.input.data[2] = abs_ver; - ev.u.input.data[3] = wheel; + ev.u.input.data[2] = abs_hor; + ev.u.input.data[3] = abs_ver; + ev.u.input.data[4] = wheel; return uhid_write(fd, &ev); } -- cgit v1.2.3-59-g8ed1b From 95f712662d96ef7e50f5ca882c3f83d16f1e28f0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 3 Sep 2013 17:48:26 +0300 Subject: HID: hid-wiimote: print small buffers via %*phC Instead of passing each byte through stack let's use %*phC specifier to dump buffer as a hex string. Signed-off-by: Andy Shevchenko Acked-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-wiimote-core.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index d3055d1dbc06..bd2bc4a1f378 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -441,8 +441,7 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem) if (ret != 6) return WIIMOTE_EXT_NONE; - hid_dbg(wdata->hdev, "extension ID: %02x:%02x %02x:%02x %02x:%02x\n", - rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]); + hid_dbg(wdata->hdev, "extension ID: %6phC\n", rmem); if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff && rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff) @@ -520,14 +519,12 @@ static bool wiimote_cmd_read_mp(struct wiimote_data *wdata, __u8 *rmem) if (ret != 6) return false; - hid_dbg(wdata->hdev, "motion plus ID: %02x:%02x %02x:%02x %02x:%02x\n", - rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]); + hid_dbg(wdata->hdev, "motion plus ID: %6phC\n", rmem); if (rmem[5] == 0x05) return true; - hid_info(wdata->hdev, "unknown motion plus ID: %02x:%02x %02x:%02x %02x:%02x\n", - rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]); + hid_info(wdata->hdev, "unknown motion plus ID: %6phC\n", rmem); return false; } @@ -543,8 +540,7 @@ static __u8 wiimote_cmd_read_mp_mapped(struct wiimote_data *wdata) if (ret != 6) return WIIMOTE_MP_NONE; - hid_dbg(wdata->hdev, "mapped motion plus ID: %02x:%02x %02x:%02x %02x:%02x\n", - rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]); + hid_dbg(wdata->hdev, "mapped motion plus ID: %6phC\n", rmem); if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff && rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff) @@ -1138,9 +1134,8 @@ static void wiimote_init_hotplug(struct wiimote_data *wdata) wiimote_ext_unload(wdata); if (exttype == WIIMOTE_EXT_UNKNOWN) { - hid_info(wdata->hdev, "cannot detect extension; %02x:%02x %02x:%02x %02x:%02x\n", - extdata[0], extdata[1], extdata[2], - extdata[3], extdata[4], extdata[5]); + hid_info(wdata->hdev, "cannot detect extension; %6phC\n", + extdata); } else if (exttype == WIIMOTE_EXT_NONE) { spin_lock_irq(&wdata->state.lock); wdata->state.exttype = WIIMOTE_EXT_NONE; -- cgit v1.2.3-59-g8ed1b From 412f30105ec6735224535791eed5cdc02888ecb4 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:30:49 +0200 Subject: HID: pantherlord: validate output report details A HID device could send a malicious output report that would cause the pantherlord HID driver to write beyond the output report allocation during initialization, causing a heap overflow: [ 310.939483] usb 1-1: New USB device found, idVendor=0e8f, idProduct=0003 ... [ 315.980774] BUG kmalloc-192 (Tainted: G W ): Redzone overwritten CVE-2013-2892 Signed-off-by: Kees Cook Cc: stable@kernel.org Signed-off-by: Jiri Kosina --- drivers/hid/hid-pl.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index d29112fa5cd5..2dcd7d98dbd6 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -132,8 +132,14 @@ static int plff_init(struct hid_device *hid) strong = &report->field[0]->value[2]; weak = &report->field[0]->value[3]; debug("detected single-field device"); - } else if (report->maxfield >= 4 && report->field[0]->maxusage == 1 && - report->field[0]->usage[0].hid == (HID_UP_LED | 0x43)) { + } else if (report->field[0]->maxusage == 1 && + report->field[0]->usage[0].hid == + (HID_UP_LED | 0x43) && + report->maxfield >= 4 && + report->field[0]->report_count >= 1 && + report->field[1]->report_count >= 1 && + report->field[2]->report_count >= 1 && + report->field[3]->report_count >= 1) { report->field[0]->value[0] = 0x00; report->field[1]->value[0] = 0x00; strong = &report->field[2]->value[0]; -- cgit v1.2.3-59-g8ed1b From 875b4e3763dbc941f15143dd1a18d10bb0be303b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:31:28 +0200 Subject: HID: ntrig: validate feature report details A HID device could send a malicious feature report that would cause the ntrig HID driver to trigger a NULL dereference during initialization: [57383.031190] usb 3-1: New USB device found, idVendor=1b96, idProduct=0001 ... [57383.315193] BUG: unable to handle kernel NULL pointer dereference at 0000000000000030 [57383.315308] IP: [] ntrig_probe+0x25e/0x420 [hid_ntrig] CVE-2013-2896 Signed-off-by: Kees Cook Cc: stable@kernel.org Signed-off-by: Rafi Rubin Signed-off-by: Jiri Kosina --- drivers/hid/hid-ntrig.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 98d1fdf7d8cd..600f2075512f 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -115,7 +115,8 @@ static inline int ntrig_get_mode(struct hid_device *hdev) struct hid_report *report = hdev->report_enum[HID_FEATURE_REPORT]. report_id_hash[0x0d]; - if (!report) + if (!report || report->maxfield < 1 || + report->field[0]->report_count < 1) return -EINVAL; hid_hw_request(hdev, report, HID_REQ_GET_REPORT); -- cgit v1.2.3-59-g8ed1b From 9e8910257397372633e74b333ef891f20c800ee4 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:31:44 +0200 Subject: HID: sensor-hub: validate feature report details A HID device could send a malicious feature report that would cause the sensor-hub HID driver to read past the end of heap allocation, leaking kernel memory contents to the caller. CVE-2013-2898 Signed-off-by: Kees Cook Cc: stable@kernel.org Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-sensor-hub.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index ffc80cf481b4..6fca30eb377d 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -221,7 +221,8 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, mutex_lock(&data->mutex); report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); - if (!report || (field_index >= report->maxfield)) { + if (!report || (field_index >= report->maxfield) || + report->field[field_index]->report_count < 1) { ret = -EINVAL; goto done_proc; } -- cgit v1.2.3-59-g8ed1b From 1e87a2456b0227ca4ab881e19a11bb99d164e792 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:31:52 +0200 Subject: HID: picolcd_core: validate output report details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A HID device could send a malicious output report that would cause the picolcd HID driver to trigger a NULL dereference during attr file writing. [jkosina@suse.cz: changed report->maxfield < 1 to report->maxfield != 1 as suggested by Bruno]. CVE-2013-2899 Signed-off-by: Kees Cook Cc: stable@kernel.org Reviewed-by: Bruno Prémont Acked-by: Bruno Prémont Signed-off-by: Jiri Kosina --- drivers/hid/hid-picolcd_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index b48092d0e139..acbb021065ec 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -290,7 +290,7 @@ static ssize_t picolcd_operation_mode_store(struct device *dev, buf += 10; cnt -= 10; } - if (!report) + if (!report || report->maxfield != 1) return -EINVAL; while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r')) -- cgit v1.2.3-59-g8ed1b From be67b68d52fa28b9b721c47bb42068f0c1214855 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:32:01 +0200 Subject: HID: check for NULL field when setting values Defensively check that the field to be worked on is not NULL. Signed-off-by: Kees Cook Cc: stable@kernel.org Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ebf57813d31a..dcd60eb3d5ea 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1169,7 +1169,12 @@ EXPORT_SYMBOL_GPL(hid_alloc_report_buf); int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) { - unsigned size = field->report_size; + unsigned size; + + if (!field) + return -1; + + size = field->report_size; hid_dump_input(field->report->device, field->usage + offset, value); -- cgit v1.2.3-59-g8ed1b From 39054a5afbb05f83aaa26f6cf55027e269744ba3 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 2 Sep 2013 14:00:05 +0200 Subject: HID: move HID_REPORT_TYPES closer to the report-definitions HID_REPORT_TYPES defines the number of available report-types. Move it closer to the actualy definition of the report-types so we can see the relation more clearly (and hopefully will never forget to update it). Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- include/linux/hid.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/hid.h b/include/linux/hid.h index d50137691a78..729bf27aac8f 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -252,6 +252,8 @@ struct hid_item { #define HID_OUTPUT_REPORT 1 #define HID_FEATURE_REPORT 2 +#define HID_REPORT_TYPES 3 + /* * HID connect requests */ @@ -401,8 +403,6 @@ struct hid_report_enum { struct hid_report *report_id_hash[HID_MAX_IDS]; }; -#define HID_REPORT_TYPES 3 - #define HID_MIN_BUFFER_SIZE 64 /* make sure there is at least a packet size of space */ #define HID_MAX_BUFFER_SIZE 4096 /* 4kb */ #define HID_CONTROL_FIFO_SIZE 256 /* to init devices with >100 reports */ -- cgit v1.2.3-59-g8ed1b From 27f1d2f9acf2a73cc817addfdb4d4043d0991b87 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Thu, 5 Sep 2013 06:36:01 +0200 Subject: HID: MAINTAINERS: add roccat drivers Adding maintainer for Roccat hid drivers Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 97053182fcf1..55d946be6789 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6921,6 +6921,14 @@ M: Maxim Levitsky S: Maintained F: drivers/memstick/host/r592.* +ROCCAT DRIVERS +M: Stefan Achatz +W: http://sourceforge.net/projects/roccat/ +S: Maintained +F: drivers/hid/hid-roccat* +F: include/linux/hid-roccat* +F: Documentation/ABI/*/sysfs-driver-hid-roccat* + ROCKETPORT DRIVER P: Comtrol Corp. W: http://www.comtrol.com -- cgit v1.2.3-59-g8ed1b