From 81ba992689ce1ab223e1df7673d4bab1e257ba11 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Thu, 5 Jun 2014 21:20:51 +0200 Subject: HID: usbhid: remove unneeded initialization of quirks_param[] The quirks_param array is located in the BSS, no need to explicitly initialize it with NULL. Signed-off-by: Mathias Krause Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 7b88f4c..37d2a95 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -58,7 +58,7 @@ module_param_named(ignoreled, ignoreled, uint, 0644); MODULE_PARM_DESC(ignoreled, "Autosuspend with active leds"); /* Quirks specified at module load time */ -static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL }; +static char *quirks_param[MAX_USBHID_BOOT_QUIRKS]; module_param_array_named(quirks, quirks_param, charp, NULL, 0444); MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying " " quirks=vendorID:productID:quirks" -- cgit v1.1 From e2c76a8bad2fbeafcfb80aa749212b4b16e60257 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 10 Jun 2014 14:35:36 -0700 Subject: HID: Kconfig: drop remove Unicode non-breaking space from Kconfig There is a UTF-8 non-breaking space character (0xc2 0xa0) after the "Y" in "Say Y here". This is probably not intentional. Replace it with a standard ASCII space (0x20). If you can't see a difference here, I don't blame you :) Signed-off-by: Brian Norris Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 800c8b6..08c9693 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -785,7 +785,7 @@ config HID_XINMO 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 + standard. Currently only supports the Xin-Mo Dual Arcade. Say Y here if you have a Xin-Mo Dual Arcade controller. config HID_ZEROPLUS -- cgit v1.1 From 50764650c9e6e8498d273ad714f68ee8eec96692 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 24 Jun 2014 13:28:40 +0200 Subject: HID: sony: Use the SIXAXIS_CONTROLLER constant when possible Instead of checking for SIXAXIS_CONTROLLER_USB and SIXAXIS_CONTROLLER_BT separately, a check on SIXAXIS_CONTROLLER can be used when setting connect_mask. Signed-off-by: Antonio Ospite Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 2259eaa..a77269b 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1830,9 +1830,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) if (sc->quirks & VAIO_RDESC_CONSTANT) connect_mask |= HID_CONNECT_HIDDEV_FORCE; - else if (sc->quirks & SIXAXIS_CONTROLLER_USB) - connect_mask |= HID_CONNECT_HIDDEV_FORCE; - else if (sc->quirks & SIXAXIS_CONTROLLER_BT) + else if (sc->quirks & SIXAXIS_CONTROLLER) connect_mask |= HID_CONNECT_HIDDEV_FORCE; ret = hid_hw_start(hdev, connect_mask); -- cgit v1.1 From c607fb8d671693ddc699b56d18cbd185e4a91ad2 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 24 Jun 2014 13:28:41 +0200 Subject: HID: sony: Always override the Sixaxis descriptor Simplify the logic of overriding the Sixaxis HID descriptor, this will make it easier to amend the descriptor in future commits. The current code does this: if (original sixaxis via USB or BT) fixup only a part of the descriptor else if (sixaxis compatible controller) override the _whole_ descriptor but the end result is exactly the same, except for the trailing zero in the case of original BT controllers. So let's just regularize the process, and always override the HID descriptor. Always overriding the descriptor changes the current semantic a little bit, before this change the BT descriptor still had the trailing zero byte, while now it is exactly the same as the descriptor of the controller via USB, but that does not affect proper operation of the device. Note that overriding the whole descriptor for original devices is not strictly necessary for now, but it simplifies the code and in the future the report descriptor will be patched further and keys will be remapped, so it's handy to have only one place to patch. Signed-off-by: Antonio Ospite Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index a77269b..11026b5 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -56,13 +56,7 @@ #define MAX_LEDS 4 -static const u8 sixaxis_rdesc_fixup[] = { - 0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C, - 0x81, 0x01, 0x75, 0x10, 0x95, 0x04, 0x26, 0xFF, - 0x03, 0x46, 0xFF, 0x03, 0x09, 0x01, 0x81, 0x02 -}; - -static const u8 sixaxis_rdesc_fixup2[] = { +static __u8 sixaxis_rdesc[] = { 0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0xa1, 0x02, 0x85, 0x01, 0x75, 0x08, 0x95, 0x01, 0x15, 0x00, 0x26, 0xff, 0x00, 0x81, 0x03, 0x75, 0x01, 0x95, @@ -778,6 +772,13 @@ struct sony_sc { __u8 led_count; }; +static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + *rsize = sizeof(sixaxis_rdesc); + return sixaxis_rdesc; +} + static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { @@ -857,20 +858,8 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(dualshock4_bt_rdesc); } - /* The HID descriptor exposed over BT has a trailing zero byte */ - if ((((sc->quirks & SIXAXIS_CONTROLLER_USB) && *rsize == 148) || - ((sc->quirks & SIXAXIS_CONTROLLER_BT) && *rsize == 149)) && - rdesc[83] == 0x75) { - hid_info(hdev, "Fixing up Sony Sixaxis report descriptor\n"); - memcpy((void *)&rdesc[83], (void *)&sixaxis_rdesc_fixup, - sizeof(sixaxis_rdesc_fixup)); - } else if (sc->quirks & SIXAXIS_CONTROLLER_USB && - *rsize > sizeof(sixaxis_rdesc_fixup2)) { - hid_info(hdev, "Sony Sixaxis clone detected. Using original report descriptor (size: %d clone; %d new)\n", - *rsize, (int)sizeof(sixaxis_rdesc_fixup2)); - *rsize = sizeof(sixaxis_rdesc_fixup2); - memcpy(rdesc, &sixaxis_rdesc_fixup2, *rsize); - } + if (sc->quirks & SIXAXIS_CONTROLLER) + return sixaxis_fixup(hdev, rdesc, rsize); if (sc->quirks & PS3REMOTE) return ps3remote_fixup(hdev, rdesc, rsize); -- cgit v1.1 From fb705a6dc8e997ea26f0bd93f58c43e2f0592d3a Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 24 Jun 2014 13:28:42 +0200 Subject: HID: sony: Format and comment sixaxis_rdesc Reformat sixaxis_rdesc to reflect its HID structure, and comment each field. This will make it easier to validate changes to the descriptor in the future. No functional changes are introduced, the descriptor is exactly the same as before byte by byte. The heavy lifting has been done with the help of hidrd-convert: https://github.com/DIGImend/hidrd Signed-off-by: Antonio Ospite Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 93 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 19 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 11026b5..22ba5dd 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -57,25 +57,80 @@ #define MAX_LEDS 4 static __u8 sixaxis_rdesc[] = { - 0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0xa1, 0x02, - 0x85, 0x01, 0x75, 0x08, 0x95, 0x01, 0x15, 0x00, - 0x26, 0xff, 0x00, 0x81, 0x03, 0x75, 0x01, 0x95, - 0x13, 0x15, 0x00, 0x25, 0x01, 0x35, 0x00, 0x45, - 0x01, 0x05, 0x09, 0x19, 0x01, 0x29, 0x13, 0x81, - 0x02, 0x75, 0x01, 0x95, 0x0d, 0x06, 0x00, 0xff, - 0x81, 0x03, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05, - 0x01, 0x09, 0x01, 0xa1, 0x00, 0x75, 0x08, 0x95, - 0x04, 0x35, 0x00, 0x46, 0xff, 0x00, 0x09, 0x30, - 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x81, 0x02, - 0xc0, 0x05, 0x01, 0x95, 0x13, 0x09, 0x01, 0x81, - 0x02, 0x95, 0x0c, 0x81, 0x01, 0x75, 0x10, 0x95, - 0x04, 0x26, 0xff, 0x03, 0x46, 0xff, 0x03, 0x09, - 0x01, 0x81, 0x02, 0xc0, 0xa1, 0x02, 0x85, 0x02, - 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, - 0xc0, 0xa1, 0x02, 0x85, 0xee, 0x75, 0x08, 0x95, - 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, - 0x85, 0xef, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, - 0xb1, 0x02, 0xc0, 0xc0, + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x04, /* Usage (Joystik), */ + 0xA1, 0x01, /* Collection (Application), */ + 0xA1, 0x02, /* Collection (Logical), */ + 0x85, 0x01, /* Report ID (1), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x13, /* Report Count (19), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x35, 0x00, /* Physical Minimum (0), */ + 0x45, 0x01, /* Physical Maximum (1), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x13, /* Usage Maximum (13h), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x0D, /* Report Count (13), */ + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xA1, 0x00, /* Collection (Physical), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x04, /* Report Count (4), */ + 0x35, 0x00, /* Physical Minimum (0), */ + 0x46, 0xFF, 0x00, /* Physical Maximum (255), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x09, 0x32, /* Usage (Z), */ + 0x09, 0x35, /* Usage (Rz), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x95, 0x13, /* Report Count (19), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x0C, /* Report Count (12), */ + 0x81, 0x01, /* Input (Constant), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x04, /* Report Count (4), */ + 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ + 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xA1, 0x02, /* Collection (Logical), */ + 0x85, 0x02, /* Report ID (2), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0, /* End Collection, */ + 0xA1, 0x02, /* Collection (Logical), */ + 0x85, 0xEE, /* Report ID (238), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0, /* End Collection, */ + 0xA1, 0x02, /* Collection (Logical), */ + 0x85, 0xEF, /* Report ID (239), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ }; /* -- cgit v1.1 From ab030726b64382d3548fd157db0a1c678c792122 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 24 Jun 2014 13:28:43 +0200 Subject: HID: sony: Remove an old and redundant comment Remove an old redundant comment before sony_report_fixup(), it must have been a leftover from the first version of the driver: sony_report_fixup() now handles all the supported devices, not only the Sony Vaio VGX. The comment is also redundant as the same information provided by it is also present in the body of the function. Signed-off-by: Antonio Ospite Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 22ba5dd..bb1e969 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -875,8 +875,6 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi, return 1; } - -/* Sony Vaio VGX has wrongly mouse pointer declared as constant */ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { -- cgit v1.1 From 0a9597b70c76234babf7650437578abaeb9259e1 Mon Sep 17 00:00:00 2001 From: Himangi Saraogi Date: Thu, 26 Jun 2014 22:58:52 +0530 Subject: HID: roccat: Drop cast This patch removes the cast on data of type void* as it is not needed. The following Coccinelle semantic patch was used for making the change: @r@ expression x; void* e; type T; identifier f; @@ ( *((T *)e) | ((T *)x)[...] | ((T *)x)->f | - (T *) e ) Signed-off-by: Himangi Saraogi Acked-by: Julia Lawall Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat-lua.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c index 6adc0fa..65e2e76 100644 --- a/drivers/hid/hid-roccat-lua.c +++ b/drivers/hid/hid-roccat-lua.c @@ -61,7 +61,7 @@ static ssize_t lua_sysfs_write(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&lua->lua_lock); - retval = roccat_common2_send(usb_dev, command, (void *)buf, real_size); + retval = roccat_common2_send(usb_dev, command, buf, real_size); mutex_unlock(&lua->lua_lock); return retval ? retval : real_size; -- cgit v1.1 From ff2019456d745d4ebc1307eba128393a39790217 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sat, 28 Jun 2014 15:35:03 +0200 Subject: HID: picolcd: remove unnecessary NULL test before debugfs_remove Fix checkpatch warning: WARNING: debugfs_remove(NULL) is safe this check is probably not required Signed-off-by: Fabian Frederick Signed-off-by: Jiri Kosina --- drivers/hid/hid-picolcd_debugfs.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c index 024cdf3..3c13af6 100644 --- a/drivers/hid/hid-picolcd_debugfs.c +++ b/drivers/hid/hid-picolcd_debugfs.c @@ -883,16 +883,13 @@ void picolcd_exit_devfs(struct picolcd_data *data) dent = data->debug_reset; data->debug_reset = NULL; - if (dent) - debugfs_remove(dent); + debugfs_remove(dent); dent = data->debug_eeprom; data->debug_eeprom = NULL; - if (dent) - debugfs_remove(dent); + debugfs_remove(dent); dent = data->debug_flash; data->debug_flash = NULL; - if (dent) - debugfs_remove(dent); + debugfs_remove(dent); mutex_destroy(&data->mutex_flash); } -- cgit v1.1 From beb9d007a846e661cfaa7719cab6b004b3380418 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Sun, 29 Jun 2014 14:13:48 +0800 Subject: HID: cp2112: fix gpio value in gpio_direction_output CP2112 does not offer an atomic method to set both gpio direction and value. Also it does not permit to set gpio value before putting gpio in output. In fact, accordingly to Silicon Labs AN495, Rev. 0.2, cpt. 4.4, the HID report to set gpio values "does not affect any pins that are not configured as outputs". This is confirmed on evaluation board CP2112-EK. With current driver, after execute: echo in > /sys/class/gpio/gpio248/direction echo low > /sys/class/gpio/gpio248/direction gpio output is still high. Only after a following echo low > /sys/class/gpio/gpio248/direction gpio output gets low. Fix driver by changing order of operations; first set direction then set value. The drawback of this new sequence is that we can have a pulse on gpio pin when direction is changed from input to output-low, but this cannot be avoided on current CP2112. Signed-off-by: Antonio Borneo Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-cp2112.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index 56be85a..3952d90 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c @@ -240,8 +240,6 @@ static int cp2112_gpio_direction_output(struct gpio_chip *chip, u8 buf[5]; int ret; - cp2112_gpio_set(chip, offset, value); - ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf), HID_FEATURE_REPORT, HID_REQ_GET_REPORT); @@ -260,6 +258,12 @@ static int cp2112_gpio_direction_output(struct gpio_chip *chip, return ret; } + /* + * Set gpio value when output direction is already set, + * as specified in AN495, Rev. 0.2, cpt. 4.4 + */ + cp2112_gpio_set(chip, offset, value); + return 0; } -- cgit v1.1 From 9e2c327ee85e37264a1e04109efc7b97844afd0b Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Fri, 11 Jul 2014 14:35:50 -0700 Subject: HID: rmi: make compututation of the address of Query 12 more careful There are additional queries which are optional and may not be present depending on the configuration of the firmware. Knowing which queries are present is needed to properly compute the address of Query 12 and all subsequent queries. Additional bits in Query 1 are used to indicate the presence of these optional queries. Signed-off-by: Andrew Duggan Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-rmi.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 578bbe6..3221a95 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -549,10 +549,12 @@ static int rmi_populate_f11(struct hid_device *hdev) u8 buf[20]; int ret; bool has_query9; - bool has_query10; + bool has_query10 = false; bool has_query11; bool has_query12; bool has_physical_props; + bool has_gestures; + bool has_rel; unsigned x_size, y_size; u16 query12_offset; @@ -589,19 +591,32 @@ static int rmi_populate_f11(struct hid_device *hdev) return -ENODEV; } - /* query 8 to find out if query 10 exists */ - ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf); - if (ret) { - hid_err(hdev, "can not read gesture information: %d.\n", ret); - return ret; + has_rel = !!(buf[0] & BIT(3)); + has_gestures = !!(buf[0] & BIT(5)); + + if (has_gestures) { + /* query 8 to find out if query 10 exists */ + ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf); + if (ret) { + hid_err(hdev, "can not read gesture information: %d.\n", + ret); + return ret; + } + has_query10 = !!(buf[0] & BIT(2)); } - has_query10 = !!(buf[0] & BIT(2)); /* - * At least 8 queries are guaranteed to be present in F11 - * +1 for query12. + * At least 4 queries are guaranteed to be present in F11 + * +1 for query 5 which is present since absolute events are + * reported and +1 for query 12. */ - query12_offset = 9; + query12_offset = 6; + + if (has_rel) + ++query12_offset; /* query 6 is present */ + + if (has_gestures) + query12_offset += 2; /* query 7 and 8 are present */ if (has_query9) ++query12_offset; -- cgit v1.1 From 01a5f8a401a2a525b9546d65ddf69aaba37f288d Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Fri, 11 Jul 2014 14:35:51 -0700 Subject: HID: rmi: change logging level of log messages related to unexpected reports Userspace tools may use hidraw to perform operations on the device from userspace while hid-rmi is bound to the device. This can cause hid-rmi to print error messages when its ->raw_event() callback gets called as the reports pass through the HID stack. In this case receiving responses which were not initiated by hid-rmi is not actually an error so the resulting error messages are incorrect and misleading. This patch changes the log messages to debug so that the messages can be turned on in the event that there is a problem and there is not a userspace tool running. Signed-off-by: Andrew Duggan Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-rmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 3221a95..2c3524b 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -377,7 +377,7 @@ static int rmi_input_event(struct hid_device *hdev, u8 *data, int size) irq_mask |= hdata->f30.irq_mask; if (data[1] & ~irq_mask) - hid_warn(hdev, "unknown intr source:%02lx %s:%d\n", + hid_dbg(hdev, "unknown intr source:%02lx %s:%d\n", data[1] & ~irq_mask, __FILE__, __LINE__); if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) { @@ -400,7 +400,7 @@ static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size) struct rmi_data *hdata = hid_get_drvdata(hdev); if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) { - hid_err(hdev, "no read request pending\n"); + hid_dbg(hdev, "no read request pending\n"); return 0; } -- cgit v1.1 From 109571cf3ec78a39477eedd6b11927f52cbcb1e8 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Fri, 11 Jul 2014 16:34:18 -0700 Subject: HID: i2c-hid: call the hid driver's suspend and resume callbacks Currently, the i2c-hid driver does not call the suspend, resume, and reset_resume callbacks in the hid_driver struct when those events occur. This means that HID drivers for i2c-hid devices will not be able to execute commands which may be needed during suspend or resume. One example is when a touchpad using the hid-multitouch driver gets reset by i2c-hid coming out of resume. Since the reset_resume callback never gets called the device is never put back into the correct input mode. This patch calls the suspend and resume callbacks and tries to duplicate the functionality of the usb-hid driver. Signed-off-by: Andrew Duggan Signed-off-by: Vincent Huang Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 21aafc8..747d544 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -1054,21 +1054,29 @@ static int i2c_hid_remove(struct i2c_client *client) static int i2c_hid_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct i2c_hid *ihid = i2c_get_clientdata(client); + struct hid_device *hid = ihid->hid; + int ret = 0; disable_irq(client->irq); if (device_may_wakeup(&client->dev)) enable_irq_wake(client->irq); + if (hid->driver && hid->driver->suspend) + ret = hid->driver->suspend(hid, PMSG_SUSPEND); + /* Save some power */ i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); - return 0; + return ret; } static int i2c_hid_resume(struct device *dev) { int ret; struct i2c_client *client = to_i2c_client(dev); + struct i2c_hid *ihid = i2c_get_clientdata(client); + struct hid_device *hid = ihid->hid; enable_irq(client->irq); ret = i2c_hid_hwreset(client); @@ -1078,6 +1086,11 @@ static int i2c_hid_resume(struct device *dev) if (device_may_wakeup(&client->dev)) disable_irq_wake(client->irq); + if (hid->driver && hid->driver->reset_resume) { + ret = hid->driver->reset_resume(hid); + return ret; + } + return 0; } #endif -- cgit v1.1 From 30c6fd4277ebab2a32ae5635d34283354b1bc8f2 Mon Sep 17 00:00:00 2001 From: Wangzhao Cai Date: Mon, 14 Jul 2014 09:13:32 +0800 Subject: HID: add quirk for 0x04d9:0xa096 device I am using a USB keyborad that give me "usb_submit_urb(ctrl) failed: -1" error when I plugin it. and I need to wait for 10s for this device to be ready. By adding this quirks, the usb keyborad is usable right after plugin Signed-off-by: Wangzhao Cai Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/usbhid/hid-quirks.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 6d00bb9..1ac5ed4 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -478,6 +478,7 @@ #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070 0xa070 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072 0xa072 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081 0xa081 +#define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096 0xa096 #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 59badc1..90638a4 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -121,6 +121,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_INPUT_REPORTS }, { 0, 0 } }; -- cgit v1.1 From e917e98f4829a5842ea8283548bf5dedfd88b2d0 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Wed, 23 Jul 2014 19:31:54 +0300 Subject: HID: huion: Use "tablet" instead of specific model Use word "tablet" in identifiers and comments instead of referring to specific Huion tablet model name, as the product ID is used by at least several tablet models. Signed-off-by: Nikolai Kondrashov Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 +- drivers/hid/hid-huion.c | 25 +++++++++++++------------ drivers/hid/hid-ids.h | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8ed66fd..84ed7d4 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1782,7 +1782,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, - { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, diff --git a/drivers/hid/hid-huion.c b/drivers/hid/hid-huion.c index cbf4da4..25d01cd 100644 --- a/drivers/hid/hid-huion.c +++ b/drivers/hid/hid-huion.c @@ -2,6 +2,7 @@ * HID driver for Huion devices not fully compliant with HID standard * * Copyright (c) 2013 Martin Rusko + * Copyright (c) 2014 Nikolai Kondrashov */ /* @@ -19,11 +20,11 @@ #include "hid-ids.h" -/* Original Huion 580 report descriptor size */ -#define HUION_580_RDESC_ORIG_SIZE 177 +/* Original tablet report descriptor size */ +#define HUION_TABLET_RDESC_ORIG_SIZE 177 -/* Fixed Huion 580 report descriptor */ -static __u8 huion_580_rdesc_fixed[] = { +/* Fixed tablet report descriptor */ +static __u8 huion_tablet_rdesc_fixed[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -72,10 +73,10 @@ static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { switch (hdev->product) { - case USB_DEVICE_ID_HUION_580: - if (*rsize == HUION_580_RDESC_ORIG_SIZE) { - rdesc = huion_580_rdesc_fixed; - *rsize = sizeof(huion_580_rdesc_fixed); + case USB_DEVICE_ID_HUION_TABLET: + if (*rsize == HUION_TABLET_RDESC_ORIG_SIZE) { + rdesc = huion_tablet_rdesc_fixed; + *rsize = sizeof(huion_tablet_rdesc_fixed); } break; } @@ -108,11 +109,11 @@ static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) int ret; struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - /* Ignore interfaces 1 (mouse) and 2 (keyboard) for Huion 580 tablet, + /* Ignore interfaces 1 (mouse) and 2 (keyboard) for tablet, * as they are not used */ switch (id->product) { - case USB_DEVICE_ID_HUION_580: + case USB_DEVICE_ID_HUION_TABLET: if (intf->cur_altsetting->desc.bInterfaceNumber != 0x00) return -ENODEV; break; @@ -131,7 +132,7 @@ static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) } switch (id->product) { - case USB_DEVICE_ID_HUION_580: + case USB_DEVICE_ID_HUION_TABLET: ret = huion_tablet_enable(hdev); if (ret) { hid_err(hdev, "tablet enabling failed\n"); @@ -158,7 +159,7 @@ static int huion_raw_event(struct hid_device *hdev, struct hid_report *report, } static const struct hid_device_id huion_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, { } }; MODULE_DEVICE_TABLE(hid, huion_devices); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 48b66bb..fe2f163 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -448,7 +448,7 @@ #define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 #define USB_VENDOR_ID_HUION 0x256c -#define USB_DEVICE_ID_HUION_580 0x006e +#define USB_DEVICE_ID_HUION_TABLET 0x006e #define USB_VENDOR_ID_IDEACOM 0x1cb6 #define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650 -- cgit v1.1 From fb853296d8ce3056c3668f2e51dee8533c32a9d5 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Wed, 23 Jul 2014 19:31:55 +0300 Subject: HID: huion: Don't ignore other interfaces Don't ignore non pen-reporting interfaces as they may be used by some models reusing the same product ID. Signed-off-by: Nikolai Kondrashov Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-huion.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-huion.c b/drivers/hid/hid-huion.c index 25d01cd..46c425b 100644 --- a/drivers/hid/hid-huion.c +++ b/drivers/hid/hid-huion.c @@ -107,17 +107,6 @@ static int huion_tablet_enable(struct hid_device *hdev) static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - - /* Ignore interfaces 1 (mouse) and 2 (keyboard) for tablet, - * as they are not used - */ - switch (id->product) { - case USB_DEVICE_ID_HUION_TABLET: - if (intf->cur_altsetting->desc.bInterfaceNumber != 0x00) - return -ENODEV; - break; - } ret = hid_parse(hdev); if (ret) { @@ -151,8 +140,13 @@ err: static int huion_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { - /* If this is a pen input report then invert the in-range bit */ - if (report->type == HID_INPUT_REPORT && report->id == 0x07 && size >= 2) + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + /* If this is a pen input report */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 0 && + report->type == HID_INPUT_REPORT && + report->id == 0x07 && size >= 2) + /* Invert the in-range bit */ data[1] ^= 0x40; return 0; -- cgit v1.1 From f8dd5cb2c6d0dae6585cc2961e1f721fb0fc5cf8 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Wed, 23 Jul 2014 19:31:56 +0300 Subject: HID: huion: Switch to generating report descriptor Switch to generating tablet pen report descriptor from a template and parameters retrieved from string descriptor 0x64. Signed-off-by: Nikolai Kondrashov Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-huion.c | 245 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 167 insertions(+), 78 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-huion.c b/drivers/hid/hid-huion.c index 46c425b..6c811c1 100644 --- a/drivers/hid/hid-huion.c +++ b/drivers/hid/hid-huion.c @@ -16,67 +16,89 @@ #include #include #include +#include #include "usbhid/usbhid.h" #include "hid-ids.h" -/* Original tablet report descriptor size */ -#define HUION_TABLET_RDESC_ORIG_SIZE 177 - -/* Fixed tablet report descriptor */ -static __u8 huion_tablet_rdesc_fixed[] = { - 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x02, /* Usage (Pen), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x07, /* Report ID (7), */ - 0x09, 0x20, /* Usage (Stylus), */ - 0xA0, /* Collection (Physical), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x75, 0x01, /* Report Size (1), */ - 0x09, 0x42, /* Usage (Tip Switch), */ - 0x09, 0x44, /* Usage (Barrel Switch), */ - 0x09, 0x46, /* Usage (Tablet Pick), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x03, /* Input (Constant, Variable), */ - 0x09, 0x32, /* Usage (In Range), */ - 0x95, 0x01, /* Report Count (1), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x01, /* Report Count (1), */ - 0x81, 0x03, /* Input (Constant, Variable), */ - 0x75, 0x10, /* Report Size (16), */ - 0x95, 0x01, /* Report Count (1), */ - 0xA4, /* Push, */ - 0x05, 0x01, /* Usage Page (Desktop), */ - 0x65, 0x13, /* Unit (Inch), */ - 0x55, 0xFD, /* Unit Exponent (-3), */ - 0x34, /* Physical Minimum (0), */ - 0x09, 0x30, /* Usage (X), */ - 0x46, 0x40, 0x1F, /* Physical Maximum (8000), */ - 0x26, 0x00, 0x7D, /* Logical Maximum (32000), */ - 0x81, 0x02, /* Input (Variable), */ - 0x09, 0x31, /* Usage (Y), */ - 0x46, 0x88, 0x13, /* Physical Maximum (5000), */ - 0x26, 0x20, 0x4E, /* Logical Maximum (20000), */ - 0x81, 0x02, /* Input (Variable), */ - 0xB4, /* Pop, */ - 0x09, 0x30, /* Usage (Tip Pressure), */ - 0x26, 0xFF, 0x07, /* Logical Maximum (2047), */ - 0x81, 0x02, /* Input (Variable), */ - 0xC0, /* End Collection, */ - 0xC0 /* End Collection */ +/* Report descriptor template placeholder head */ +#define HUION_PH_HEAD 0xFE, 0xED, 0x1D + +/* Report descriptor template placeholder IDs */ +enum huion_ph_id { + HUION_PH_ID_X_LM, + HUION_PH_ID_X_PM, + HUION_PH_ID_Y_LM, + HUION_PH_ID_Y_PM, + HUION_PH_ID_PRESSURE_LM, + HUION_PH_ID_NUM +}; + +/* Report descriptor template placeholder */ +#define HUION_PH(_ID) HUION_PH_HEAD, HUION_PH_ID_##_ID + +/* Fixed report descriptor template */ +static const __u8 huion_tablet_rdesc_template[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x07, /* Report ID (7), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x34, /* Physical Minimum (0), */ + 0x09, 0x30, /* Usage (X), */ + 0x27, HUION_PH(X_LM), /* Logical Maximum (PLACEHOLDER), */ + 0x47, HUION_PH(X_PM), /* Physical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x27, HUION_PH(Y_LM), /* Logical Maximum (PLACEHOLDER), */ + 0x47, HUION_PH(Y_PM), /* Physical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x27, + HUION_PH(PRESSURE_LM), /* Logical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +/* Driver data */ +struct huion_drvdata { + __u8 *rdesc; + unsigned int rsize; }; static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { + struct huion_drvdata *drvdata = hid_get_drvdata(hdev); switch (hdev->product) { case USB_DEVICE_ID_HUION_TABLET: - if (*rsize == HUION_TABLET_RDESC_ORIG_SIZE) { - rdesc = huion_tablet_rdesc_fixed; - *rsize = sizeof(huion_tablet_rdesc_fixed); + if (drvdata->rdesc != NULL) { + rdesc = drvdata->rdesc; + *rsize = drvdata->rsize; } break; } @@ -84,57 +106,124 @@ static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, } /** - * Enable fully-functional tablet mode by reading special string - * descriptor. + * Enable fully-functional tablet mode and determine device parameters. * * @hdev: HID device - * - * The specific string descriptor and data were discovered by sniffing - * the Windows driver traffic. */ static int huion_tablet_enable(struct hid_device *hdev) { int rc; - char buf[22]; + struct usb_device *usb_dev = hid_to_usb_dev(hdev); + struct huion_drvdata *drvdata = hid_get_drvdata(hdev); + u16 buf[6]; - rc = usb_string(hid_to_usb_dev(hdev), 0x64, buf, sizeof(buf)); - if (rc < 0) - return rc; + /* + * Read string descriptor containing tablet parameters. The specific + * string descriptor and data were discovered by sniffing the Windows + * driver traffic. + * NOTE: This enables fully-functional tablet mode. + */ + rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + 0x64, + 0x0409, buf, sizeof(buf), + USB_CTRL_GET_TIMEOUT); + if (rc == -EPIPE) + hid_warn(hdev, "device parameters not found\n"); + else if (rc < 0) + hid_warn(hdev, "failed to get device parameters: %d\n", rc); + else if (rc != sizeof(buf)) + hid_warn(hdev, "invalid device parameters\n"); + else { + s32 params[HUION_PH_ID_NUM]; + s32 resolution; + __u8 *p; + s32 v; + + /* Extract device parameters */ + params[HUION_PH_ID_X_LM] = le16_to_cpu(buf[1]); + params[HUION_PH_ID_Y_LM] = le16_to_cpu(buf[2]); + params[HUION_PH_ID_PRESSURE_LM] = le16_to_cpu(buf[4]); + resolution = le16_to_cpu(buf[5]); + if (resolution == 0) { + params[HUION_PH_ID_X_PM] = 0; + params[HUION_PH_ID_Y_PM] = 0; + } else { + params[HUION_PH_ID_X_PM] = params[HUION_PH_ID_X_LM] * + 1000 / resolution; + params[HUION_PH_ID_Y_PM] = params[HUION_PH_ID_Y_LM] * + 1000 / resolution; + } + + /* Allocate fixed report descriptor */ + drvdata->rdesc = devm_kmalloc(&hdev->dev, + sizeof(huion_tablet_rdesc_template), + GFP_KERNEL); + if (drvdata->rdesc == NULL) { + hid_err(hdev, "failed to allocate fixed rdesc\n"); + return -ENOMEM; + } + drvdata->rsize = sizeof(huion_tablet_rdesc_template); + + /* Format fixed report descriptor */ + memcpy(drvdata->rdesc, huion_tablet_rdesc_template, + drvdata->rsize); + for (p = drvdata->rdesc; + p <= drvdata->rdesc + drvdata->rsize - 4;) { + if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D && + p[3] < sizeof(params)) { + v = params[p[3]]; + put_unaligned(cpu_to_le32(v), (s32 *)p); + p += 4; + } else { + p++; + } + } + } return 0; } static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) { - int ret; - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "parse failed\n"); - goto err; - } + int rc; + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct huion_drvdata *drvdata; - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) { - hid_err(hdev, "hw start failed\n"); - goto err; + /* Allocate and assign driver data */ + drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (drvdata == NULL) { + hid_err(hdev, "failed to allocate driver data\n"); + return -ENOMEM; } + hid_set_drvdata(hdev, drvdata); switch (id->product) { case USB_DEVICE_ID_HUION_TABLET: - ret = huion_tablet_enable(hdev); - if (ret) { - hid_err(hdev, "tablet enabling failed\n"); - goto enabling_err; + /* If this is the pen interface */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + rc = huion_tablet_enable(hdev); + if (rc) { + hid_err(hdev, "tablet enabling failed\n"); + return rc; + } } break; } + rc = hid_parse(hdev); + if (rc) { + hid_err(hdev, "parse failed\n"); + return rc; + } + + rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (rc) { + hid_err(hdev, "hw start failed\n"); + return rc; + } + return 0; -enabling_err: - hid_hw_stop(hdev); -err: - return ret; } static int huion_raw_event(struct hid_device *hdev, struct hid_report *report, -- cgit v1.1 From 3f1f333232fa3c36bbf20de1c6ffefcc96892d07 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Wed, 23 Jul 2014 19:31:57 +0300 Subject: HID: huion: Handle tablets with UC-Logic vendor ID Add handling of Huion tablets with UC-Logic vendor ID, but the same product ID to hid-huion driver. A Huion H580 was seen using it. Signed-off-by: Nikolai Kondrashov Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-huion.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-huion.c b/drivers/hid/hid-huion.c index 6c811c1..438b54e 100644 --- a/drivers/hid/hid-huion.c +++ b/drivers/hid/hid-huion.c @@ -243,6 +243,7 @@ static int huion_raw_event(struct hid_device *hdev, struct hid_report *report, static const struct hid_device_id huion_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, { } }; MODULE_DEVICE_TABLE(hid, huion_devices); -- cgit v1.1 From 94723bfa7bf1b647fe49c9717ee02e99d7a59957 Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Wed, 23 Jul 2014 23:30:45 +0100 Subject: HID: lenovo: Rename hid-lenovo-tpkbd to hid-lenovo Rename module and all functions within so we can add support for other keyboards in the same file. Rename the _tp postfix to _tpkbd, to signify functions relevant to the TP USB keyboard. Signed-off-by: Jamie Lentin Reviewed-by: Antonio Ospite Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 14 +- drivers/hid/Makefile | 2 +- drivers/hid/hid-core.c | 2 +- drivers/hid/hid-lenovo-tpkbd.c | 462 ---------------------------------------- drivers/hid/hid-lenovo.c | 463 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 472 insertions(+), 471 deletions(-) delete mode 100644 drivers/hid/hid-lenovo-tpkbd.c create mode 100644 drivers/hid/hid-lenovo.c (limited to 'drivers/hid') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5e79c6a..687d7a1 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -331,18 +331,18 @@ config HID_LCPOWER ---help--- Support for LC-Power RC1000MCE RF remote control. -config HID_LENOVO_TPKBD - tristate "Lenovo ThinkPad USB Keyboard with TrackPoint" +config HID_LENOVO + tristate "Lenovo / Thinkpad devices" depends on HID select NEW_LEDS select LEDS_CLASS ---help--- - Support for the Lenovo ThinkPad USB Keyboard with TrackPoint. + Support for Lenovo devices that are not fully compliant with HID standard. - Say Y here if you have a Lenovo ThinkPad USB Keyboard with TrackPoint - and would like to use device-specific features like changing the - sensitivity of the trackpoint, using the microphone mute button or - controlling the mute and microphone mute LEDs. + Say Y if you want support for the non-compliant features of the Lenovo + Thinkpad standalone keyboards, e.g: + - ThinkPad USB Keyboard with TrackPoint (supports extra LEDs and trackpoint + configuration) config HID_LOGITECH tristate "Logitech devices" if EXPERT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index a6fa6ba..5e96be3 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -59,7 +59,7 @@ obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o -obj-$(CONFIG_HID_LENOVO_TPKBD) += hid-lenovo-tpkbd.o +obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8ed66fd..55841bd 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1796,7 +1796,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, -#if IS_ENABLED(CONFIG_HID_LENOVO_TPKBD) +#if IS_ENABLED(CONFIG_HID_LENOVO) { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, #endif { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c deleted file mode 100644 index 2d25b6c..0000000 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ /dev/null @@ -1,462 +0,0 @@ -/* - * HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint - * - * Copyright (c) 2012 Bernhard Seibold - */ - -/* - * 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 -#include - -#include "hid-ids.h" - -/* This is only used for the trackpoint part of the driver, hence _tp */ -struct tpkbd_data_pointer { - int led_state; - struct led_classdev led_mute; - struct led_classdev led_micmute; - int press_to_select; - int dragging; - int release_to_select; - int select_right; - int sensitivity; - int press_speed; -}; - -#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) - -static int tpkbd_input_mapping(struct hid_device *hdev, - struct hid_input *hi, struct hid_field *field, - struct hid_usage *usage, unsigned long **bit, int *max) -{ - if (usage->hid == (HID_UP_BUTTON | 0x0010)) { - /* mark the device as pointer */ - hid_set_drvdata(hdev, (void *)1); - map_key_clear(KEY_MICMUTE); - return 1; - } - return 0; -} - -#undef map_key_clear - -static int tpkbd_features_set(struct hid_device *hdev) -{ - struct hid_report *report; - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4]; - - report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02; - report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08; - report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20; - report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40; - report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver - report->field[2]->value[0] = data_pointer->sensitivity; - report->field[3]->value[0] = data_pointer->press_speed; - - hid_hw_request(hdev, report, HID_REQ_SET_REPORT); - return 0; -} - -static ssize_t pointer_press_to_select_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); -} - -static ssize_t pointer_press_to_select_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value)) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - - data_pointer->press_to_select = value; - tpkbd_features_set(hdev); - - return count; -} - -static ssize_t pointer_dragging_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); -} - -static ssize_t pointer_dragging_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value)) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - - data_pointer->dragging = value; - tpkbd_features_set(hdev); - - return count; -} - -static ssize_t pointer_release_to_select_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); -} - -static ssize_t pointer_release_to_select_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value)) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - - data_pointer->release_to_select = value; - tpkbd_features_set(hdev); - - return count; -} - -static ssize_t pointer_select_right_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); -} - -static ssize_t pointer_select_right_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value)) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - - data_pointer->select_right = value; - tpkbd_features_set(hdev); - - return count; -} - -static ssize_t pointer_sensitivity_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", - data_pointer->sensitivity); -} - -static ssize_t pointer_sensitivity_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) - return -EINVAL; - - data_pointer->sensitivity = value; - tpkbd_features_set(hdev); - - return count; -} - -static ssize_t pointer_press_speed_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", - data_pointer->press_speed); -} - -static ssize_t pointer_press_speed_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) - return -EINVAL; - - data_pointer->press_speed = value; - tpkbd_features_set(hdev); - - return count; -} - -static struct device_attribute dev_attr_pointer_press_to_select = - __ATTR(press_to_select, S_IWUSR | S_IRUGO, - pointer_press_to_select_show, - pointer_press_to_select_store); - -static struct device_attribute dev_attr_pointer_dragging = - __ATTR(dragging, S_IWUSR | S_IRUGO, - pointer_dragging_show, - pointer_dragging_store); - -static struct device_attribute dev_attr_pointer_release_to_select = - __ATTR(release_to_select, S_IWUSR | S_IRUGO, - pointer_release_to_select_show, - pointer_release_to_select_store); - -static struct device_attribute dev_attr_pointer_select_right = - __ATTR(select_right, S_IWUSR | S_IRUGO, - pointer_select_right_show, - pointer_select_right_store); - -static struct device_attribute dev_attr_pointer_sensitivity = - __ATTR(sensitivity, S_IWUSR | S_IRUGO, - pointer_sensitivity_show, - pointer_sensitivity_store); - -static struct device_attribute dev_attr_pointer_press_speed = - __ATTR(press_speed, S_IWUSR | S_IRUGO, - pointer_press_speed_show, - pointer_press_speed_store); - -static struct attribute *tpkbd_attributes_pointer[] = { - &dev_attr_pointer_press_to_select.attr, - &dev_attr_pointer_dragging.attr, - &dev_attr_pointer_release_to_select.attr, - &dev_attr_pointer_select_right.attr, - &dev_attr_pointer_sensitivity.attr, - &dev_attr_pointer_press_speed.attr, - NULL -}; - -static const struct attribute_group tpkbd_attr_group_pointer = { - .attrs = tpkbd_attributes_pointer, -}; - -static enum led_brightness tpkbd_led_brightness_get( - struct led_classdev *led_cdev) -{ - struct device *dev = led_cdev->dev->parent; - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int led_nr = 0; - - if (led_cdev == &data_pointer->led_micmute) - led_nr = 1; - - return data_pointer->led_state & (1 << led_nr) - ? LED_FULL - : LED_OFF; -} - -static void tpkbd_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - struct device *dev = led_cdev->dev->parent; - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - struct hid_report *report; - int led_nr = 0; - - if (led_cdev == &data_pointer->led_micmute) - led_nr = 1; - - if (value == LED_OFF) - data_pointer->led_state &= ~(1 << led_nr); - else - data_pointer->led_state |= 1 << led_nr; - - report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3]; - report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1; - report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1; - hid_hw_request(hdev, report, HID_REQ_SET_REPORT); -} - -static int tpkbd_probe_tp(struct hid_device *hdev) -{ - struct device *dev = &hdev->dev; - struct tpkbd_data_pointer *data_pointer; - size_t name_sz = strlen(dev_name(dev)) + 16; - char *name_mute, *name_micmute; - int i; - - /* Validate required reports. */ - for (i = 0; i < 4; i++) { - if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1)) - return -ENODEV; - } - if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2)) - return -ENODEV; - - if (sysfs_create_group(&hdev->dev.kobj, - &tpkbd_attr_group_pointer)) { - hid_warn(hdev, "Could not create sysfs group\n"); - } - - data_pointer = devm_kzalloc(&hdev->dev, - sizeof(struct tpkbd_data_pointer), - GFP_KERNEL); - if (data_pointer == NULL) { - hid_err(hdev, "Could not allocate memory for driver data\n"); - return -ENOMEM; - } - - // set same default values as windows driver - data_pointer->sensitivity = 0xa0; - data_pointer->press_speed = 0x38; - - name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); - name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); - if (name_mute == NULL || name_micmute == NULL) { - hid_err(hdev, "Could not allocate memory for led data\n"); - return -ENOMEM; - } - snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev)); - snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev)); - - hid_set_drvdata(hdev, data_pointer); - - data_pointer->led_mute.name = name_mute; - data_pointer->led_mute.brightness_get = tpkbd_led_brightness_get; - data_pointer->led_mute.brightness_set = tpkbd_led_brightness_set; - data_pointer->led_mute.dev = dev; - led_classdev_register(dev, &data_pointer->led_mute); - - data_pointer->led_micmute.name = name_micmute; - data_pointer->led_micmute.brightness_get = tpkbd_led_brightness_get; - data_pointer->led_micmute.brightness_set = tpkbd_led_brightness_set; - data_pointer->led_micmute.dev = dev; - led_classdev_register(dev, &data_pointer->led_micmute); - - tpkbd_features_set(hdev); - - return 0; -} - -static int tpkbd_probe(struct hid_device *hdev, - const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "hid_parse failed\n"); - goto err; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) { - hid_err(hdev, "hid_hw_start failed\n"); - goto err; - } - - if (hid_get_drvdata(hdev)) { - hid_set_drvdata(hdev, NULL); - ret = tpkbd_probe_tp(hdev); - if (ret) - goto err_hid; - } - - return 0; -err_hid: - hid_hw_stop(hdev); -err: - return ret; -} - -static void tpkbd_remove_tp(struct hid_device *hdev) -{ - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - sysfs_remove_group(&hdev->dev.kobj, - &tpkbd_attr_group_pointer); - - led_classdev_unregister(&data_pointer->led_micmute); - led_classdev_unregister(&data_pointer->led_mute); - - hid_set_drvdata(hdev, NULL); -} - -static void tpkbd_remove(struct hid_device *hdev) -{ - if (hid_get_drvdata(hdev)) - tpkbd_remove_tp(hdev); - - hid_hw_stop(hdev); -} - -static const struct hid_device_id tpkbd_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, - { } -}; - -MODULE_DEVICE_TABLE(hid, tpkbd_devices); - -static struct hid_driver tpkbd_driver = { - .name = "lenovo_tpkbd", - .id_table = tpkbd_devices, - .input_mapping = tpkbd_input_mapping, - .probe = tpkbd_probe, - .remove = tpkbd_remove, -}; -module_hid_driver(tpkbd_driver); - -MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c new file mode 100644 index 0000000..0320b96 --- /dev/null +++ b/drivers/hid/hid-lenovo.c @@ -0,0 +1,463 @@ +/* + * HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint + * + * Copyright (c) 2012 Bernhard Seibold + */ + +/* + * 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 +#include + +#include "hid-ids.h" + +struct lenovo_drvdata_tpkbd { + int led_state; + struct led_classdev led_mute; + struct led_classdev led_micmute; + int press_to_select; + int dragging; + int release_to_select; + int select_right; + int sensitivity; + int press_speed; +}; + +#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) + +static int lenovo_input_mapping_tpkbd(struct hid_device *hdev, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + if (usage->hid == (HID_UP_BUTTON | 0x0010)) { + /* mark the device as pointer */ + hid_set_drvdata(hdev, (void *)1); + map_key_clear(KEY_MICMUTE); + return 1; + } + return 0; +} + +#undef map_key_clear + +static int lenovo_features_set_tpkbd(struct hid_device *hdev) +{ + struct hid_report *report; + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + + report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4]; + + report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02; + report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08; + report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20; + report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40; + report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver + report->field[2]->value[0] = data_pointer->sensitivity; + report->field[3]->value[0] = data_pointer->press_speed; + + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); + return 0; +} + +static ssize_t attr_press_to_select_show_tpkbd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); +} + +static ssize_t attr_press_to_select_store_tpkbd(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + int value; + + if (kstrtoint(buf, 10, &value)) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + + data_pointer->press_to_select = value; + lenovo_features_set_tpkbd(hdev); + + return count; +} + +static ssize_t attr_dragging_show_tpkbd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); +} + +static ssize_t attr_dragging_store_tpkbd(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + int value; + + if (kstrtoint(buf, 10, &value)) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + + data_pointer->dragging = value; + lenovo_features_set_tpkbd(hdev); + + return count; +} + +static ssize_t attr_release_to_select_show_tpkbd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); +} + +static ssize_t attr_release_to_select_store_tpkbd(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + int value; + + if (kstrtoint(buf, 10, &value)) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + + data_pointer->release_to_select = value; + lenovo_features_set_tpkbd(hdev); + + return count; +} + +static ssize_t attr_select_right_show_tpkbd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); +} + +static ssize_t attr_select_right_store_tpkbd(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + int value; + + if (kstrtoint(buf, 10, &value)) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + + data_pointer->select_right = value; + lenovo_features_set_tpkbd(hdev); + + return count; +} + +static ssize_t attr_sensitivity_show_tpkbd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + data_pointer->sensitivity); +} + +static ssize_t attr_sensitivity_store_tpkbd(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + int value; + + if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) + return -EINVAL; + + data_pointer->sensitivity = value; + lenovo_features_set_tpkbd(hdev); + + return count; +} + +static ssize_t attr_press_speed_show_tpkbd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + data_pointer->press_speed); +} + +static ssize_t attr_press_speed_store_tpkbd(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + int value; + + if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) + return -EINVAL; + + data_pointer->press_speed = value; + lenovo_features_set_tpkbd(hdev); + + return count; +} + +static struct device_attribute dev_attr_press_to_select_tpkbd = + __ATTR(press_to_select, S_IWUSR | S_IRUGO, + attr_press_to_select_show_tpkbd, + attr_press_to_select_store_tpkbd); + +static struct device_attribute dev_attr_dragging_tpkbd = + __ATTR(dragging, S_IWUSR | S_IRUGO, + attr_dragging_show_tpkbd, + attr_dragging_store_tpkbd); + +static struct device_attribute dev_attr_release_to_select_tpkbd = + __ATTR(release_to_select, S_IWUSR | S_IRUGO, + attr_release_to_select_show_tpkbd, + attr_release_to_select_store_tpkbd); + +static struct device_attribute dev_attr_select_right_tpkbd = + __ATTR(select_right, S_IWUSR | S_IRUGO, + attr_select_right_show_tpkbd, + attr_select_right_store_tpkbd); + +static struct device_attribute dev_attr_sensitivity_tpkbd = + __ATTR(sensitivity, S_IWUSR | S_IRUGO, + attr_sensitivity_show_tpkbd, + attr_sensitivity_store_tpkbd); + +static struct device_attribute dev_attr_press_speed_tpkbd = + __ATTR(press_speed, S_IWUSR | S_IRUGO, + attr_press_speed_show_tpkbd, + attr_press_speed_store_tpkbd); + +static struct attribute *lenovo_attributes_tpkbd[] = { + &dev_attr_press_to_select_tpkbd.attr, + &dev_attr_dragging_tpkbd.attr, + &dev_attr_release_to_select_tpkbd.attr, + &dev_attr_select_right_tpkbd.attr, + &dev_attr_sensitivity_tpkbd.attr, + &dev_attr_press_speed_tpkbd.attr, + NULL +}; + +static const struct attribute_group lenovo_attr_group_tpkbd = { + .attrs = lenovo_attributes_tpkbd, +}; + +static enum led_brightness lenovo_led_brightness_get_tpkbd( + struct led_classdev *led_cdev) +{ + struct device *dev = led_cdev->dev->parent; + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + int led_nr = 0; + + if (led_cdev == &data_pointer->led_micmute) + led_nr = 1; + + return data_pointer->led_state & (1 << led_nr) + ? LED_FULL + : LED_OFF; +} + +static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct device *dev = led_cdev->dev->parent; + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + struct hid_report *report; + int led_nr = 0; + + if (led_cdev == &data_pointer->led_micmute) + led_nr = 1; + + if (value == LED_OFF) + data_pointer->led_state &= ~(1 << led_nr); + else + data_pointer->led_state |= 1 << led_nr; + + report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3]; + report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1; + report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1; + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); +} + +static int lenovo_probe_tpkbd(struct hid_device *hdev) +{ + struct device *dev = &hdev->dev; + struct lenovo_drvdata_tpkbd *data_pointer; + size_t name_sz = strlen(dev_name(dev)) + 16; + char *name_mute, *name_micmute; + int i; + + /* Validate required reports. */ + for (i = 0; i < 4; i++) { + if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1)) + return -ENODEV; + } + if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2)) + return -ENODEV; + + if (sysfs_create_group(&hdev->dev.kobj, + &lenovo_attr_group_tpkbd)) { + hid_warn(hdev, "Could not create sysfs group\n"); + } + + data_pointer = devm_kzalloc(&hdev->dev, + sizeof(struct lenovo_drvdata_tpkbd), + GFP_KERNEL); + if (data_pointer == NULL) { + hid_err(hdev, "Could not allocate memory for driver data\n"); + return -ENOMEM; + } + + // set same default values as windows driver + data_pointer->sensitivity = 0xa0; + data_pointer->press_speed = 0x38; + + name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); + name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); + if (name_mute == NULL || name_micmute == NULL) { + hid_err(hdev, "Could not allocate memory for led data\n"); + return -ENOMEM; + } + snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev)); + snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev)); + + hid_set_drvdata(hdev, data_pointer); + + data_pointer->led_mute.name = name_mute; + data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd; + data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd; + data_pointer->led_mute.dev = dev; + led_classdev_register(dev, &data_pointer->led_mute); + + data_pointer->led_micmute.name = name_micmute; + data_pointer->led_micmute.brightness_get = + lenovo_led_brightness_get_tpkbd; + data_pointer->led_micmute.brightness_set = + lenovo_led_brightness_set_tpkbd; + data_pointer->led_micmute.dev = dev; + led_classdev_register(dev, &data_pointer->led_micmute); + + lenovo_features_set_tpkbd(hdev); + + return 0; +} + +static int lenovo_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "hid_parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hid_hw_start failed\n"); + goto err; + } + + if (hid_get_drvdata(hdev)) { + hid_set_drvdata(hdev, NULL); + ret = lenovo_probe_tpkbd(hdev); + if (ret) + goto err_hid; + } + + return 0; +err_hid: + hid_hw_stop(hdev); +err: + return ret; +} + +static void lenovo_remove_tpkbd(struct hid_device *hdev) +{ + struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + + sysfs_remove_group(&hdev->dev.kobj, + &lenovo_attr_group_tpkbd); + + led_classdev_unregister(&data_pointer->led_micmute); + led_classdev_unregister(&data_pointer->led_mute); + + hid_set_drvdata(hdev, NULL); +} + +static void lenovo_remove(struct hid_device *hdev) +{ + if (hid_get_drvdata(hdev)) + lenovo_remove_tpkbd(hdev); + + hid_hw_stop(hdev); +} + +static const struct hid_device_id lenovo_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, lenovo_devices); + +static struct hid_driver lenovo_driver = { + .name = "lenovo", + .id_table = lenovo_devices, + .input_mapping = lenovo_input_mapping_tpkbd, + .probe = lenovo_probe, + .remove = lenovo_remove, +}; +module_hid_driver(lenovo_driver); + +MODULE_LICENSE("GPL"); -- cgit v1.1 From 6a5b414b4216be0fb7d26aadcf35de0fa24a03e3 Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Wed, 23 Jul 2014 23:30:46 +0100 Subject: HID: lenovo: Prepare support for adding other devices Ensure all tpkbd specifics are within a postfixed function, the main functions for the driver should just switch to the appropriate function depending on product ID. Given this, we can add extra devices by including extra postfixed functions. Signed-off-by: Jamie Lentin Reviewed-by: Antonio Ospite Signed-off-by: Jiri Kosina --- drivers/hid/hid-lenovo.c | 53 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index 0320b96..a56b9e7 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -1,5 +1,6 @@ /* - * HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint + * HID driver for Lenovo: + * - ThinkPad USB Keyboard with TrackPoint (tpkbd) * * Copyright (c) 2012 Bernhard Seibold */ @@ -39,7 +40,7 @@ static int lenovo_input_mapping_tpkbd(struct hid_device *hdev, struct hid_usage *usage, unsigned long **bit, int *max) { if (usage->hid == (HID_UP_BUTTON | 0x0010)) { - /* mark the device as pointer */ + /* This sub-device contains trackpoint, mark it */ hid_set_drvdata(hdev, (void *)1); map_key_clear(KEY_MICMUTE); return 1; @@ -47,6 +48,19 @@ static int lenovo_input_mapping_tpkbd(struct hid_device *hdev, return 0; } +static int lenovo_input_mapping(struct hid_device *hdev, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + switch (hdev->product) { + case USB_DEVICE_ID_LENOVO_TPKBD: + return lenovo_input_mapping_tpkbd(hdev, hi, field, + usage, bit, max); + default: + return 0; + } +} + #undef map_key_clear static int lenovo_features_set_tpkbd(struct hid_device *hdev) @@ -337,6 +351,15 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev) char *name_mute, *name_micmute; int i; + /* + * Only register extra settings against subdevice where input_mapping + * set drvdata to 1, i.e. the trackpoint. + */ + if (!hid_get_drvdata(hdev)) + return 0; + + hid_set_drvdata(hdev, NULL); + /* Validate required reports. */ for (i = 0; i < 4; i++) { if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1)) @@ -409,12 +432,16 @@ static int lenovo_probe(struct hid_device *hdev, goto err; } - if (hid_get_drvdata(hdev)) { - hid_set_drvdata(hdev, NULL); + switch (hdev->product) { + case USB_DEVICE_ID_LENOVO_TPKBD: ret = lenovo_probe_tpkbd(hdev); - if (ret) - goto err_hid; + break; + default: + ret = 0; + break; } + if (ret) + goto err_hid; return 0; err_hid: @@ -427,6 +454,13 @@ static void lenovo_remove_tpkbd(struct hid_device *hdev) { struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); + /* + * Only the trackpoint half of the keyboard has drvdata and stuff that + * needs unregistering. + */ + if (data_pointer == NULL) + return; + sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd); @@ -438,8 +472,11 @@ static void lenovo_remove_tpkbd(struct hid_device *hdev) static void lenovo_remove(struct hid_device *hdev) { - if (hid_get_drvdata(hdev)) + switch (hdev->product) { + case USB_DEVICE_ID_LENOVO_TPKBD: lenovo_remove_tpkbd(hdev); + break; + } hid_hw_stop(hdev); } @@ -454,7 +491,7 @@ MODULE_DEVICE_TABLE(hid, lenovo_devices); static struct hid_driver lenovo_driver = { .name = "lenovo", .id_table = lenovo_devices, - .input_mapping = lenovo_input_mapping_tpkbd, + .input_mapping = lenovo_input_mapping, .probe = lenovo_probe, .remove = lenovo_remove, }; -- cgit v1.1 From e0a6aad6014be2d4fbb24dfb8e046308da1df8c8 Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Sat, 26 Jul 2014 15:42:27 +0100 Subject: HID: lenovo: Don't call function in condition, show error codes Signed-off-by: Jamie Lentin Reviewed-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/hid-lenovo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index a56b9e7..f96bf09 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -350,6 +350,7 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev) size_t name_sz = strlen(dev_name(dev)) + 16; char *name_mute, *name_micmute; int i; + int ret; /* * Only register extra settings against subdevice where input_mapping @@ -368,10 +369,9 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev) if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2)) return -ENODEV; - if (sysfs_create_group(&hdev->dev.kobj, - &lenovo_attr_group_tpkbd)) { - hid_warn(hdev, "Could not create sysfs group\n"); - } + ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd); + if (ret) + hid_warn(hdev, "Could not create sysfs group: %d\n", ret); data_pointer = devm_kzalloc(&hdev->dev, sizeof(struct lenovo_drvdata_tpkbd), -- cgit v1.1 From f3d4ff0e04cc4450bdc7a4140020913b1280d205 Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Wed, 23 Jul 2014 23:30:48 +0100 Subject: HID: lenovo: Add support for Compact (BT|USB) keyboard Add support for both ThinkPad Compact Bluetooth Keyboard with TrackPoint and ThinkPad Compact USB Keyboard with TrackPoint. Signed-off-by: Jamie Lentin Reviewed-by: Antonio Ospite Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 2 + drivers/hid/hid-core.c | 2 + drivers/hid/hid-ids.h | 2 + drivers/hid/hid-lenovo.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 214 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 687d7a1..21cce19 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -343,6 +343,8 @@ config HID_LENOVO Thinkpad standalone keyboards, e.g: - ThinkPad USB Keyboard with TrackPoint (supports extra LEDs and trackpoint configuration) + - ThinkPad Compact Bluetooth Keyboard with TrackPoint (supports Fn keys) + - ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys) config HID_LOGITECH tristate "Logitech devices" if EXPERT diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 55841bd..81b3bb6 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1798,6 +1798,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, #if IS_ENABLED(CONFIG_HID_LENOVO) { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) }, #endif { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 48b66bb..315891d 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -561,6 +561,8 @@ #define USB_VENDOR_ID_LENOVO 0x17ef #define USB_DEVICE_ID_LENOVO_TPKBD 0x6009 +#define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047 +#define USB_DEVICE_ID_LENOVO_CBTKBD 0x6048 #define USB_VENDOR_ID_LG 0x1fd2 #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index f96bf09..bf227f7 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -1,8 +1,11 @@ /* * HID driver for Lenovo: * - ThinkPad USB Keyboard with TrackPoint (tpkbd) + * - ThinkPad Compact Bluetooth Keyboard with TrackPoint (cptkbd) + * - ThinkPad Compact USB Keyboard with TrackPoint (cptkbd) * * Copyright (c) 2012 Bernhard Seibold + * Copyright (c) 2014 Jamie Lentin */ /* @@ -33,6 +36,10 @@ struct lenovo_drvdata_tpkbd { int press_speed; }; +struct lenovo_drvdata_cptkbd { + bool fn_lock; +}; + #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) static int lenovo_input_mapping_tpkbd(struct hid_device *hdev, @@ -48,6 +55,49 @@ static int lenovo_input_mapping_tpkbd(struct hid_device *hdev, return 0; } +static int lenovo_input_mapping_cptkbd(struct hid_device *hdev, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + /* HID_UP_LNVENDOR = USB, HID_UP_MSVENDOR = BT */ + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR || + (usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) { + set_bit(EV_REP, hi->input->evbit); + switch (usage->hid & HID_USAGE) { + case 0x00f1: /* Fn-F4: Mic mute */ + map_key_clear(KEY_MICMUTE); + return 1; + case 0x00f2: /* Fn-F5: Brightness down */ + map_key_clear(KEY_BRIGHTNESSDOWN); + return 1; + case 0x00f3: /* Fn-F6: Brightness up */ + map_key_clear(KEY_BRIGHTNESSUP); + return 1; + case 0x00f4: /* Fn-F7: External display (projector) */ + map_key_clear(KEY_SWITCHVIDEOMODE); + return 1; + case 0x00f5: /* Fn-F8: Wireless */ + map_key_clear(KEY_WLAN); + return 1; + case 0x00f6: /* Fn-F9: Control panel */ + map_key_clear(KEY_CONFIG); + return 1; + case 0x00f8: /* Fn-F11: View open applications (3 boxes) */ + map_key_clear(KEY_SCALE); + return 1; + case 0x00fa: /* Fn-Esc: Fn-lock toggle */ + map_key_clear(KEY_FN_ESC); + return 1; + case 0x00fb: /* Fn-F12: Open My computer (6 boxes) USB-only */ + /* NB: This mapping is invented in raw_event below */ + map_key_clear(KEY_FILE); + return 1; + } + } + + return 0; +} + static int lenovo_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) @@ -56,6 +106,10 @@ static int lenovo_input_mapping(struct hid_device *hdev, case USB_DEVICE_ID_LENOVO_TPKBD: return lenovo_input_mapping_tpkbd(hdev, hi, field, usage, bit, max); + case USB_DEVICE_ID_LENOVO_CUSBKBD: + case USB_DEVICE_ID_LENOVO_CBTKBD: + return lenovo_input_mapping_cptkbd(hdev, hi, field, + usage, bit, max); default: return 0; } @@ -63,6 +117,103 @@ static int lenovo_input_mapping(struct hid_device *hdev, #undef map_key_clear +/* Send a config command to the keyboard */ +static int lenovo_send_cmd_cptkbd(struct hid_device *hdev, + unsigned char byte2, unsigned char byte3) +{ + int ret; + unsigned char buf[] = {0x18, byte2, byte3}; + + switch (hdev->product) { + case USB_DEVICE_ID_LENOVO_CUSBKBD: + ret = hid_hw_raw_request(hdev, 0x13, buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + break; + case USB_DEVICE_ID_LENOVO_CBTKBD: + ret = hid_hw_output_report(hdev, buf, sizeof(buf)); + break; + default: + ret = -EINVAL; + break; + } + + return ret < 0 ? ret : 0; /* BT returns 0, USB returns sizeof(buf) */ +} + +static void lenovo_features_set_cptkbd(struct hid_device *hdev) +{ + int ret; + struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); + + ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock); + if (ret) + hid_err(hdev, "Fn-lock setting failed: %d\n", ret); +} + +static ssize_t attr_fn_lock_show_cptkbd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", cptkbd_data->fn_lock); +} + +static ssize_t attr_fn_lock_store_cptkbd(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); + int value; + + if (kstrtoint(buf, 10, &value)) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + + cptkbd_data->fn_lock = !!value; + lenovo_features_set_cptkbd(hdev); + + return count; +} + +static struct device_attribute dev_attr_fn_lock_cptkbd = + __ATTR(fn_lock, S_IWUSR | S_IRUGO, + attr_fn_lock_show_cptkbd, + attr_fn_lock_store_cptkbd); + +static struct attribute *lenovo_attributes_cptkbd[] = { + &dev_attr_fn_lock_cptkbd.attr, + NULL +}; + +static const struct attribute_group lenovo_attr_group_cptkbd = { + .attrs = lenovo_attributes_cptkbd, +}; + +static int lenovo_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + /* + * Compact USB keyboard's Fn-F12 report holds down many other keys, and + * its own key is outside the usage page range. Remove extra + * keypresses and remap to inside usage page. + */ + if (unlikely(hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD + && size == 3 + && data[0] == 0x15 + && data[1] == 0x94 + && data[2] == 0x01)) { + data[1] = 0x0; + data[2] = 0x4; + } + + return 0; +} + static int lenovo_features_set_tpkbd(struct hid_device *hdev) { struct hid_report *report; @@ -415,6 +566,46 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev) return 0; } +static int lenovo_probe_cptkbd(struct hid_device *hdev) +{ + int ret; + struct lenovo_drvdata_cptkbd *cptkbd_data; + + /* All the custom action happens on the USBMOUSE device for USB */ + if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD + && hdev->type != HID_TYPE_USBMOUSE) { + hid_dbg(hdev, "Ignoring keyboard half of device\n"); + return 0; + } + + cptkbd_data = devm_kzalloc(&hdev->dev, + sizeof(*cptkbd_data), + GFP_KERNEL); + if (cptkbd_data == NULL) { + hid_err(hdev, "can't alloc keyboard descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, cptkbd_data); + + /* + * Tell the keyboard a driver understands it, and turn F7, F9, F11 into + * regular keys + */ + ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03); + if (ret) + hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret); + + /* Turn Fn-Lock on by default */ + cptkbd_data->fn_lock = true; + lenovo_features_set_cptkbd(hdev); + + ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_cptkbd); + if (ret) + hid_warn(hdev, "Could not create sysfs group: %d\n", ret); + + return 0; +} + static int lenovo_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -436,6 +627,10 @@ static int lenovo_probe(struct hid_device *hdev, case USB_DEVICE_ID_LENOVO_TPKBD: ret = lenovo_probe_tpkbd(hdev); break; + case USB_DEVICE_ID_LENOVO_CUSBKBD: + case USB_DEVICE_ID_LENOVO_CBTKBD: + ret = lenovo_probe_cptkbd(hdev); + break; default: ret = 0; break; @@ -470,12 +665,22 @@ static void lenovo_remove_tpkbd(struct hid_device *hdev) hid_set_drvdata(hdev, NULL); } +static void lenovo_remove_cptkbd(struct hid_device *hdev) +{ + sysfs_remove_group(&hdev->dev.kobj, + &lenovo_attr_group_cptkbd); +} + static void lenovo_remove(struct hid_device *hdev) { switch (hdev->product) { case USB_DEVICE_ID_LENOVO_TPKBD: lenovo_remove_tpkbd(hdev); break; + case USB_DEVICE_ID_LENOVO_CUSBKBD: + case USB_DEVICE_ID_LENOVO_CBTKBD: + lenovo_remove_cptkbd(hdev); + break; } hid_hw_stop(hdev); @@ -483,6 +688,8 @@ static void lenovo_remove(struct hid_device *hdev) static const struct hid_device_id lenovo_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) }, { } }; @@ -494,6 +701,7 @@ static struct hid_driver lenovo_driver = { .input_mapping = lenovo_input_mapping, .probe = lenovo_probe, .remove = lenovo_remove, + .raw_event = lenovo_raw_event, }; module_hid_driver(lenovo_driver); -- cgit v1.1 From dd3edeb6a0267029bc3ffc480fb41dffb9081844 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 17 Jul 2014 16:14:44 -0700 Subject: HID: rmi: check that report ids exist in the report_id_hash before accessing their size It is possible that the hid-rmi driver could get loaded onto a device which does not have the expected report ids. This should not happen because it would indicate that the hid-rmi driver is not compatible with that device. However, if it does happen it should return an error from probe instead of dereferencing a null pointer. related bug: https://bugzilla.kernel.org/show_bug.cgi?id=80091 Signed-off-by: Andrew Duggan Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-rmi.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 2c3524b..0dc2514 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -848,6 +848,8 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) struct rmi_data *data = NULL; int ret; size_t alloc_size; + struct hid_report *input_report; + struct hid_report *output_report; data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL); if (!data) @@ -866,12 +868,26 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } - data->input_report_size = (hdev->report_enum[HID_INPUT_REPORT] - .report_id_hash[RMI_ATTN_REPORT_ID]->size >> 3) - + 1 /* report id */; - data->output_report_size = (hdev->report_enum[HID_OUTPUT_REPORT] - .report_id_hash[RMI_WRITE_REPORT_ID]->size >> 3) - + 1 /* report id */; + input_report = hdev->report_enum[HID_INPUT_REPORT] + .report_id_hash[RMI_ATTN_REPORT_ID]; + if (!input_report) { + hid_err(hdev, "device does not have expected input report\n"); + ret = -ENODEV; + return ret; + } + + data->input_report_size = (input_report->size >> 3) + 1 /* report id */; + + output_report = hdev->report_enum[HID_OUTPUT_REPORT] + .report_id_hash[RMI_WRITE_REPORT_ID]; + if (!output_report) { + hid_err(hdev, "device does not have expected output report\n"); + ret = -ENODEV; + return ret; + } + + data->output_report_size = (output_report->size >> 3) + + 1 /* report id */; alloc_size = data->output_report_size + data->input_report_size; -- cgit v1.1 From e19ff99f256aeeff6c07b373e01883b72e049552 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 17 Jul 2014 16:14:45 -0700 Subject: HID: rmi: only bind the hid-rmi driver to the mouse interface of composite USB devices On composite HID devices there may be multiple HID devices on separate interfaces, but hid-rmi should only bind to the mouse interface. One example is the Dell Venue 11 Pro's keyboard dock which contains a composite USB device with a HID touchpad and HID keyboard on separate intefaces. Since the USB Vendor ID is Synaptic's, hid-core is currently trying to bind hid-rmi to all\of the HID devices. This patch ensures that hid-rmi only binds to the mouse interface. related bug: https://bugzilla.kernel.org/show_bug.cgi?id=80091 Signed-off-by: Andrew Duggan Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8ed66fd..f10e768 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -783,7 +783,9 @@ static int hid_scan_report(struct hid_device *hid) * Vendor specific handlings */ if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) && - (hid->group == HID_GROUP_GENERIC)) + (hid->group == HID_GROUP_GENERIC) && + /* only bind to the mouse interface of composite USB devices */ + (hid->bus != BUS_USB || hid->type == HID_TYPE_USBMOUSE)) /* hid-rmi should take care of them, not hid-generic */ hid->group = HID_GROUP_RMI; -- cgit v1.1 From d90b1cf0c4cbe6fdcf4c4e0b0c9c97fe32b9f8a1 Mon Sep 17 00:00:00 2001 From: Wen-chien Jesse Sung Date: Mon, 7 Jul 2014 17:44:02 -0700 Subject: HID: use multi input quirk for 22b9:2968 This device generates ABS_Z and ABS_RX events instead of ABS_X and ABS_Y. Signed-off-by: Wen-chien Jesse Sung Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/usbhid/hid-quirks.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 1ac5ed4..c6ae460 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -323,6 +323,7 @@ #define USB_VENDOR_ID_ETURBOTOUCH 0x22b9 #define USB_DEVICE_ID_ETURBOTOUCH 0x0006 +#define USB_DEVICE_ID_ETURBOTOUCH_2968 0x2968 #define USB_VENDOR_ID_EZKEY 0x0518 #define USB_DEVICE_ID_BTC_8193 0x0002 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 90638a4..88e5065 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -49,6 +49,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH_2968, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, -- cgit v1.1 From b9029345ed6483fcadadc4834b44a5656dd56d70 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Tue, 8 Jul 2014 07:25:39 +0800 Subject: HID: cp2112: add I2C mode cp2112 supports single I2C read/write transactions. It can't combine I2C transactions. Add master_xfer, using similar code flow as for smbus_xfer. Signed-off-by: Antonio Borneo Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-cp2112.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index 3952d90..a822db5 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c @@ -429,6 +429,105 @@ static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data, return data_length + 4; } +static int cp2112_i2c_write_req(void *buf, u8 slave_address, u8 *data, + u8 data_length) +{ + struct cp2112_write_req_report *report = buf; + + if (data_length > sizeof(report->data)) + return -EINVAL; + + report->report = CP2112_DATA_WRITE_REQUEST; + report->slave_address = slave_address << 1; + report->length = data_length; + memcpy(report->data, data, data_length); + return data_length + 3; +} + +static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data; + struct hid_device *hdev = dev->hdev; + u8 buf[64]; + ssize_t count; + unsigned int retries; + int ret; + + hid_dbg(hdev, "I2C %d messages\n", num); + + if (num != 1) { + hid_err(hdev, + "Multi-message I2C transactions not supported\n"); + return -EOPNOTSUPP; + } + + if (msgs->flags & I2C_M_RD) + count = cp2112_read_req(buf, msgs->addr, msgs->len); + else + count = cp2112_i2c_write_req(buf, msgs->addr, msgs->buf, + msgs->len); + + if (count < 0) + return count; + + ret = hid_hw_power(hdev, PM_HINT_FULLON); + if (ret < 0) { + hid_err(hdev, "power management error: %d\n", ret); + return ret; + } + + ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT); + if (ret < 0) { + hid_warn(hdev, "Error starting transaction: %d\n", ret); + goto power_normal; + } + + for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) { + ret = cp2112_xfer_status(dev); + if (-EBUSY == ret) + continue; + if (ret < 0) + goto power_normal; + break; + } + + if (XFER_STATUS_RETRIES <= retries) { + hid_warn(hdev, "Transfer timed out, cancelling.\n"); + buf[0] = CP2112_CANCEL_TRANSFER; + buf[1] = 0x01; + + ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT); + if (ret < 0) + hid_warn(hdev, "Error cancelling transaction: %d\n", + ret); + + ret = -ETIMEDOUT; + goto power_normal; + } + + if (!(msgs->flags & I2C_M_RD)) + goto finish; + + ret = cp2112_read(dev, msgs->buf, msgs->len); + if (ret < 0) + goto power_normal; + if (ret != msgs->len) { + hid_warn(hdev, "short read: %d < %d\n", ret, msgs->len); + ret = -EIO; + goto power_normal; + } + +finish: + /* return the number of transferred messages */ + ret = 1; + +power_normal: + hid_hw_power(hdev, PM_HINT_NORMAL); + hid_dbg(hdev, "I2C transfer finished: %d\n", ret); + return ret; +} + static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) @@ -595,7 +694,8 @@ power_normal: static u32 cp2112_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_SMBUS_BYTE | + return I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA | @@ -605,6 +705,7 @@ static u32 cp2112_functionality(struct i2c_adapter *adap) } static const struct i2c_algorithm smbus_algorithm = { + .master_xfer = cp2112_i2c_xfer, .smbus_xfer = cp2112_xfer, .functionality = cp2112_functionality, }; -- cgit v1.1 From 43c1a0a9db2d9213812766065f6a07a215ed70ad Mon Sep 17 00:00:00 2001 From: Patrick Plattes Date: Sat, 5 Jul 2014 08:58:06 +0200 Subject: HID: ignore jabra gn9350e Ignore Jabra GN9350E HID interface. USB audio is working nicely, but registering as HID blocks USB mouse buttons. Since special userspace programs are needed we will avoid attaching usbhid drivers in general. Signed-off-by: Patrick Plattes Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8ed66fd..dbe9510 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2266,6 +2266,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_410) }, { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_510) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_GN9350E) }, { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c6ae460..5b2c553 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -490,6 +490,7 @@ #define USB_VENDOR_ID_JABRA 0x0b0e #define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 #define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420 +#define USB_DEVICE_ID_JABRA_GN9350E 0x9350 #define USB_VENDOR_ID_JESS 0x0c45 #define USB_DEVICE_ID_JESS_YUREX 0x1010 -- cgit v1.1 From 46df9dedabc1541f9c45f94ecd2c3c7ab0c3bf23 Mon Sep 17 00:00:00 2001 From: Reyad Attiyat Date: Fri, 25 Jul 2014 00:13:01 -0500 Subject: HID: usbhid: Use flag HID_DISCONNECTED when a usb device is removed Set disconnected flag in struct usbhid when a usb device is removed. Check for disconnected flag before sending urb requests. This prevents a kernel panic when a hid driver calls hid_hw_request() after removing a usb device. BUG: unable to handle kernel NULL pointer dereference at 0000000000000058 IP: [] hid_submit_ctrl+0x7f/0x290 PGD 0 Oops: 0002 [#1] PREEMPT SMP CPU: 2 PID: 39 Comm: khubd Tainted: G IO 3.16.0-rc5+ #112 Hardware name: Microsoft Corporation Surface Pro 2/Surface Pro 2, BIOS 2.03.0250 09/06/2013 task: ffff880118aba6e0 ti: ffff8800daf80000 task.ti: ffff8800daf80000 RIP: 0010:[] [] hid_submit_ctrl+0x7f/0x290 RSP: 0018:ffff8800daf83750 EFLAGS: 00010086 RAX: 0000000080000300 RBX: ffff88003f60c000 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff880117f78000 RBP: ffff8800daf83788 R08: 0000000000000001 R09: 0000000000000001 R10: 0000000000000001 R11: 0000000000000000 R12: ffff880117f78000 R13: ffff88003f11a290 R14: 000000000000000c R15: ffff880091cb3ab8 FS: 0000000000000000(0000) GS:ffff88011b000000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000058 CR3: 0000000001c11000 CR4: 00000000001407e0 Stack: ffff880117f3dcd0 ffff880117f78000 ffff88003f60c000 ffff880117f78000 ffff880117f78000 ffff88003f11a290 0000000000000000 ffff8800daf837b0 ffffffff81617707 ffff880117f78000 ffff88003f60c000 0000000000000013 Call Trace: [] usbhid_restart_ctrl_queue+0x87/0x140 [] usbhid_submit_report+0x2c8/0x370 [] usbhid_request+0x1a/0x30 [] sensor_hub_set_feature+0x8b/0xd0 [hid_sensor_hub] [] hid_sensor_power_state+0x84/0x110 [hid_sensor_trigger] [] hid_sensor_data_rdy_trigger_set_state+0x19/0x20 [hid_sensor_trigger] [] iio_triggered_buffer_predisable+0xa7/0xb0 [industrialio] [] iio_disable_all_buffers+0x3a/0xc0 [industrialio] [] iio_device_unregister+0x53/0x80 [industrialio] [] hid_accel_3d_remove+0x2a/0x50 [hid_sensor_accel_3d] [] platform_drv_remove+0x1d/0x40 [] __device_release_driver+0x7f/0xf0 [] device_release_driver+0x25/0x40 [] bus_remove_device+0x11c/0x1a0 [] device_del+0x136/0x1e0 [] ? mfd_cell_disable+0x80/0x80 [] platform_device_del+0x21/0xc0 [] platform_device_unregister+0x12/0x30 [] mfd_remove_devices_fn+0x43/0x50 [] device_for_each_child+0x43/0x70 [] mfd_remove_devices+0x25/0x30 [] sensor_hub_remove+0x87/0x140 [hid_sensor_hub] [] hid_device_remove+0x6b/0xd0 [] __device_release_driver+0x7f/0xf0 [] device_release_driver+0x25/0x40 [] bus_remove_device+0x11c/0x1a0 [] device_del+0x136/0x1e0 [] hid_destroy_device+0x27/0x60 [] usbhid_disconnect+0x22/0x50 [] usb_unbind_interface+0x77/0x2b0 [] __device_release_driver+0x7f/0xf0 [] device_release_driver+0x25/0x40 [] bus_remove_device+0x11c/0x1a0 [] device_del+0x136/0x1e0 [] usb_disable_device+0x91/0x2a0 [] usb_disconnect+0x96/0x2e0 [] hub_thread+0xb5a/0x1840 Signed-off-by: Reyad Attiyat Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 37d2a95..79cf503 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -536,7 +536,8 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re int head; struct usbhid_device *usbhid = hid->driver_data; - if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) + if (((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) || + test_bit(HID_DISCONNECTED, &usbhid->iofl)) return; if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) { @@ -1366,6 +1367,9 @@ static void usbhid_disconnect(struct usb_interface *intf) return; usbhid = hid->driver_data; + spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */ + set_bit(HID_DISCONNECTED, &usbhid->iofl); + spin_unlock_irq(&usbhid->lock); hid_destroy_device(hid); kfree(usbhid); } -- cgit v1.1 From f20f6eccffcbc0d46d7b70624fea75ac5c970c47 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Tue, 29 Jul 2014 15:50:06 +0300 Subject: HID: huion: Fix sparse warnings Fix sparse warnings in hid-huion.c by using correct buffer type for retrieved string descriptor. The warnings in question were: drivers/hid/hid-huion.c:144:44: sparse: cast to restricted __le16 drivers/hid/hid-huion.c:145:44: sparse: cast to restricted __le16 drivers/hid/hid-huion.c:146:51: sparse: cast to restricted __le16 drivers/hid/hid-huion.c:147:30: sparse: cast to restricted __le16 Reported-by: fengguang.wu@intel.com Signed-off-by: Nikolai Kondrashov Signed-off-by: Jiri Kosina --- drivers/hid/hid-huion.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-huion.c b/drivers/hid/hid-huion.c index 438b54e..60f44cd 100644 --- a/drivers/hid/hid-huion.c +++ b/drivers/hid/hid-huion.c @@ -115,7 +115,7 @@ static int huion_tablet_enable(struct hid_device *hdev) int rc; struct usb_device *usb_dev = hid_to_usb_dev(hdev); struct huion_drvdata *drvdata = hid_get_drvdata(hdev); - u16 buf[6]; + __le16 buf[6]; /* * Read string descriptor containing tablet parameters. The specific -- cgit v1.1 From 5607c89a115563104e1eea8d597282c799e75c99 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 29 Jul 2014 19:55:48 -0700 Subject: HID: sony: Default initialize all elements of the LED max_brightness array to 1 Previously only the first element of the array was initialized to 1 leading to potential incorrect max brightness values for the LEDs on the Dualshock 3 and buzzer controllers. Use a designated initializer to initialize the whole array to the correct value. Signed-off-by: Frank Praznik Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index bb1e969..c372368 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1349,7 +1349,7 @@ static int sony_leds_init(struct sony_sc *sc) static const char * const ds4_name_str[] = { "red", "green", "blue", "global" }; __u8 initial_values[MAX_LEDS] = { 0 }; - __u8 max_brightness[MAX_LEDS] = { 1 }; + __u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 }; __u8 use_hw_blink[MAX_LEDS] = { 0 }; BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); -- cgit v1.1 From f1210455e78a610c7b316389b31c162c371d888c Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Fri, 1 Aug 2014 07:26:41 -0700 Subject: HID: hyperv: register as a wakeup source With this patch, we can move the mouse to wake up the VM after the VM executes "echo freeze > /sys/power/state". This addresses part of https://bugzilla.redhat.com/show_bug.cgi?id=1086100 Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Jiri Kosina --- drivers/hid/hid-hyperv.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index f52dbcb..31fad64 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -308,6 +308,9 @@ static void mousevsc_on_receive(struct hv_device *device, memcpy(input_dev->input_buf, input_report->buffer, len); hid_input_report(input_dev->hid_device, HID_INPUT_REPORT, input_dev->input_buf, len, 1); + + pm_wakeup_event(&input_dev->device->device, 0); + break; default: pr_err("unsupported hid msg type - type %d len %d", @@ -549,6 +552,8 @@ static int mousevsc_probe(struct hv_device *device, goto probe_err2; } + device_init_wakeup(&device->device, true); + input_dev->connected = true; input_dev->init_complete = true; @@ -571,6 +576,7 @@ static int mousevsc_remove(struct hv_device *dev) { struct mousevsc_dev *input_dev = hv_get_drvdata(dev); + device_init_wakeup(&dev->device, false); vmbus_close(dev->channel); hid_hw_stop(input_dev->hid_device); hid_destroy_device(input_dev->hid_device); -- cgit v1.1