From afdb5cce979b7d3100d58bdce70c8be6093a8915 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 21 Oct 2014 23:11:00 +0200 Subject: HID: input: Map unknown consumer page codes to KEY_UNKNOWN Currently unknown consumer page codes are ignored, which means that they cannot later be mapped from userspace using udev / hwdb. Map them to KEY_UNKNOWN, so that userspace can remap them for keyboards which make up their own consumer page codes. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 2df7fddbd119..7ea582be9c9a 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -862,7 +862,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x28b: map_key_clear(KEY_FORWARDMAIL); break; case 0x28c: map_key_clear(KEY_SEND); break; - default: goto ignore; + default: map_key_clear(KEY_UNKNOWN); } break; -- cgit v1.2.3 From c241c5eea822344639898512780cff823fc7d730 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:23 -0400 Subject: HID: fix merge from wacom into the HID tree While merging wacom from the input to the hid tree, some comments have been duplicated. We can also integrate the test for Synaptics devices in the switch case below, so it is clear that there will be only one place for such quirks. No functional changes are expected in this commit. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 17 +++++++---------- include/linux/hid.h | 4 ---- 2 files changed, 7 insertions(+), 14 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 73bd9e2e42bc..aef7c56ca5e7 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -779,16 +779,6 @@ static int hid_scan_report(struct hid_device *hid) (hid->group == HID_GROUP_MULTITOUCH)) hid->group = HID_GROUP_MULTITOUCH_WIN_8; - /* - * Vendor specific handlings - */ - if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) && - (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; - /* * Vendor specific handlings */ @@ -796,6 +786,13 @@ static int hid_scan_report(struct hid_device *hid) case USB_VENDOR_ID_WACOM: hid->group = HID_GROUP_WACOM; break; + case USB_VENDOR_ID_SYNAPTICS: + if ((hid->group == HID_GROUP_GENERIC) && + (hid->bus != BUS_USB || hid->type == HID_TYPE_USBMOUSE)) + /* hid-rmi should only bind to the mouse interface of + * composite USB devices */ + hid->group = HID_GROUP_RMI; + break; } vfree(parser); diff --git a/include/linux/hid.h b/include/linux/hid.h index 78ea9bf941cd..5b1ff6110e25 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -312,10 +312,6 @@ struct hid_item { * Vendor specific HID device groups */ #define HID_GROUP_RMI 0x0100 - -/* - * Vendor specific HID device groups - */ #define HID_GROUP_WACOM 0x0101 /* -- cgit v1.2.3 From 9578f41aeaee5010384f4f8484da1566e2ce4901 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:24 -0400 Subject: HID: core: do not scan reports if the group is already set This allows the transport layer (I have in mind hid-logitech-dj and uhid) to set the group before it is added to the hid bus. This way, it can bypass the hid_scan_report() call, and choose in advance which driver will handle the newly created hid device. Signed-off-by: Benjamin Tisssoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index aef7c56ca5e7..946540e1764d 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2535,7 +2535,8 @@ int hid_add_device(struct hid_device *hdev) * Scan generic devices for group information */ if (hid_ignore_special_drivers || - !hid_match_id(hdev, hid_have_special_driver)) { + (!hdev->group && + !hid_match_id(hdev, hid_have_special_driver))) { ret = hid_scan_report(hdev); if (ret) hid_warn(hdev, "bad device descriptor (%d)\n", ret); -- cgit v1.2.3 From d610274b0301e5ef35811fa736036d022f707564 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:25 -0400 Subject: HID: logitech-dj: rely on hid groups to separate receivers from dj devices Several benefits here: - we can drop the macro is_dj_device: I never been really conviced by this macro as we could fall into a null pointer anytime. Anyway time showed that this never happened. - we can simplify the hid driver logitech-djdevice, and make it aware of any new receiver VID/PID. - we can use the Wireless PID of the DJ device as the product id of the hid device, this way the sysfs will differentiate between different DJ devices. Signed-off-by: Benjamin Tisssoires Tested-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 38 ++++++++++---------------------------- drivers/hid/hid-logitech-dj.h | 10 ---------- include/linux/hid.h | 1 + 3 files changed, 11 insertions(+), 38 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 71f569292cab..42d38d50afdf 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -256,11 +256,15 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, dj_hiddev->dev.parent = &djrcv_hdev->dev; dj_hiddev->bus = BUS_USB; dj_hiddev->vendor = le16_to_cpu(usbdev->descriptor.idVendor); - dj_hiddev->product = le16_to_cpu(usbdev->descriptor.idProduct); + dj_hiddev->product = + (dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB] + << 8) | + dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB]; snprintf(dj_hiddev->name, sizeof(dj_hiddev->name), - "Logitech Unifying Device. Wireless PID:%02x%02x", - dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB], - dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB]); + "Logitech Unifying Device. Wireless PID:%04x", + dj_hiddev->product); + + dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE; usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys)); snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index); @@ -714,9 +718,6 @@ static int logi_dj_probe(struct hid_device *hdev, struct dj_receiver_dev *djrcv_dev; int retval; - if (is_dj_device((struct dj_device *)hdev->driver_data)) - return -ENODEV; - dbg_hid("%s called for ifnum %d\n", __func__, intf->cur_altsetting->desc.bInterfaceNumber); @@ -869,22 +870,6 @@ static void logi_dj_remove(struct hid_device *hdev) hid_set_drvdata(hdev, NULL); } -static int logi_djdevice_probe(struct hid_device *hdev, - const struct hid_device_id *id) -{ - int ret; - struct dj_device *dj_dev = hdev->driver_data; - - if (!is_dj_device(dj_dev)) - return -ENODEV; - - ret = hid_parse(hdev); - if (!ret) - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - - return ret; -} - static const struct hid_device_id logi_dj_receivers[] = { {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)}, @@ -908,17 +893,14 @@ static struct hid_driver logi_djreceiver_driver = { static const struct hid_device_id logi_dj_devices[] = { - {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, - USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)}, - {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, - USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)}, + { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, {} }; static struct hid_driver logi_djdevice_driver = { .name = "logitech-djdevice", .id_table = logi_dj_devices, - .probe = logi_djdevice_probe, }; diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h index daeb0aa4bee9..b1208c9c0573 100644 --- a/drivers/hid/hid-logitech-dj.h +++ b/drivers/hid/hid-logitech-dj.h @@ -112,14 +112,4 @@ struct dj_device { u8 device_index; }; -/** - * is_dj_device - know if the given dj_device is not the receiver. - * @dj_dev: the dj device to test - * - * This macro tests if a struct dj_device pointer is a device created - * by the bus enumarator. - */ -#define is_dj_device(dj_dev) \ - (&(dj_dev)->dj_receiver_dev->hdev->dev == (dj_dev)->hdev->dev.parent) - #endif diff --git a/include/linux/hid.h b/include/linux/hid.h index 5b1ff6110e25..7d6e0556302a 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -313,6 +313,7 @@ struct hid_item { */ #define HID_GROUP_RMI 0x0100 #define HID_GROUP_WACOM 0x0101 +#define HID_GROUP_LOGITECH_DJ_DEVICE 0x0102 /* * This is the global environment of the parser. This information is -- cgit v1.2.3 From 8cb3746a6ee28c7b4d061689e13b8e5be80b14cc Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:26 -0400 Subject: HID: logitech-dj: merge header file into the source There is no point in keeping the header in a separate file, nobody but hid-logitech-dj should have access to its content. Signed-off-by: Benjamin Tissoires Tested-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 88 +++++++++++++++++++++++++++++++- drivers/hid/hid-logitech-dj.h | 115 ------------------------------------------ 2 files changed, 87 insertions(+), 116 deletions(-) delete mode 100644 drivers/hid/hid-logitech-dj.h (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 42d38d50afdf..6aea16dca3ff 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -26,9 +26,95 @@ #include #include #include +#include #include #include "hid-ids.h" -#include "hid-logitech-dj.h" + +#define DJ_MAX_PAIRED_DEVICES 6 +#define DJ_MAX_NUMBER_NOTIFICATIONS 8 +#define DJ_RECEIVER_INDEX 0 +#define DJ_DEVICE_INDEX_MIN 1 +#define DJ_DEVICE_INDEX_MAX 6 + +#define DJREPORT_SHORT_LENGTH 15 +#define DJREPORT_LONG_LENGTH 32 + +#define REPORT_ID_DJ_SHORT 0x20 +#define REPORT_ID_DJ_LONG 0x21 + +#define REPORT_TYPE_RFREPORT_LAST 0x1F + +/* Command Switch to DJ mode */ +#define REPORT_TYPE_CMD_SWITCH 0x80 +#define CMD_SWITCH_PARAM_DEVBITFIELD 0x00 +#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS 0x01 +#define TIMEOUT_NO_KEEPALIVE 0x00 + +/* Command to Get the list of Paired devices */ +#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES 0x81 + +/* Device Paired Notification */ +#define REPORT_TYPE_NOTIF_DEVICE_PAIRED 0x41 +#define SPFUNCTION_MORE_NOTIF_EXPECTED 0x01 +#define SPFUNCTION_DEVICE_LIST_EMPTY 0x02 +#define DEVICE_PAIRED_PARAM_SPFUNCTION 0x00 +#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB 0x01 +#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB 0x02 +#define DEVICE_PAIRED_RF_REPORT_TYPE 0x03 + +/* Device Un-Paired Notification */ +#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40 + + +/* Connection Status Notification */ +#define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42 +#define CONNECTION_STATUS_PARAM_STATUS 0x00 +#define STATUS_LINKLOSS 0x01 + +/* Error Notification */ +#define REPORT_TYPE_NOTIF_ERROR 0x7F +#define NOTIF_ERROR_PARAM_ETYPE 0x00 +#define ETYPE_KEEPALIVE_TIMEOUT 0x01 + +/* supported DJ HID && RF report types */ +#define REPORT_TYPE_KEYBOARD 0x01 +#define REPORT_TYPE_MOUSE 0x02 +#define REPORT_TYPE_CONSUMER_CONTROL 0x03 +#define REPORT_TYPE_SYSTEM_CONTROL 0x04 +#define REPORT_TYPE_MEDIA_CENTER 0x08 +#define REPORT_TYPE_LEDS 0x0E + +/* RF Report types bitfield */ +#define STD_KEYBOARD 0x00000002 +#define STD_MOUSE 0x00000004 +#define MULTIMEDIA 0x00000008 +#define POWER_KEYS 0x00000010 +#define MEDIA_CENTER 0x00000100 +#define KBD_LEDS 0x00004000 + +struct dj_report { + u8 report_id; + u8 device_index; + u8 report_type; + u8 report_params[DJREPORT_SHORT_LENGTH - 3]; +}; + +struct dj_receiver_dev { + struct hid_device *hdev; + struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES + + DJ_DEVICE_INDEX_MIN]; + struct work_struct work; + struct kfifo notif_fifo; + spinlock_t lock; + bool querying_devices; +}; + +struct dj_device { + struct hid_device *hdev; + struct dj_receiver_dev *dj_receiver_dev; + u32 reports_supported; + u8 device_index; +}; /* Keyboard descriptor (1) */ static const char kbd_descriptor[] = { diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h deleted file mode 100644 index b1208c9c0573..000000000000 --- a/drivers/hid/hid-logitech-dj.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef __HID_LOGITECH_DJ_H -#define __HID_LOGITECH_DJ_H - -/* - * HID driver for Logitech Unifying receivers - * - * Copyright (c) 2011 Logitech - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include - -#define DJ_MAX_PAIRED_DEVICES 6 -#define DJ_MAX_NUMBER_NOTIFICATIONS 8 -#define DJ_RECEIVER_INDEX 0 -#define DJ_DEVICE_INDEX_MIN 1 -#define DJ_DEVICE_INDEX_MAX 6 - -#define DJREPORT_SHORT_LENGTH 15 -#define DJREPORT_LONG_LENGTH 32 - -#define REPORT_ID_DJ_SHORT 0x20 -#define REPORT_ID_DJ_LONG 0x21 - -#define REPORT_TYPE_RFREPORT_FIRST 0x01 -#define REPORT_TYPE_RFREPORT_LAST 0x1F - -/* Command Switch to DJ mode */ -#define REPORT_TYPE_CMD_SWITCH 0x80 -#define CMD_SWITCH_PARAM_DEVBITFIELD 0x00 -#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS 0x01 -#define TIMEOUT_NO_KEEPALIVE 0x00 - -/* Command to Get the list of Paired devices */ -#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES 0x81 - -/* Device Paired Notification */ -#define REPORT_TYPE_NOTIF_DEVICE_PAIRED 0x41 -#define SPFUNCTION_MORE_NOTIF_EXPECTED 0x01 -#define SPFUNCTION_DEVICE_LIST_EMPTY 0x02 -#define DEVICE_PAIRED_PARAM_SPFUNCTION 0x00 -#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB 0x01 -#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB 0x02 -#define DEVICE_PAIRED_RF_REPORT_TYPE 0x03 - -/* Device Un-Paired Notification */ -#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40 - - -/* Connection Status Notification */ -#define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42 -#define CONNECTION_STATUS_PARAM_STATUS 0x00 -#define STATUS_LINKLOSS 0x01 - -/* Error Notification */ -#define REPORT_TYPE_NOTIF_ERROR 0x7F -#define NOTIF_ERROR_PARAM_ETYPE 0x00 -#define ETYPE_KEEPALIVE_TIMEOUT 0x01 - -/* supported DJ HID && RF report types */ -#define REPORT_TYPE_KEYBOARD 0x01 -#define REPORT_TYPE_MOUSE 0x02 -#define REPORT_TYPE_CONSUMER_CONTROL 0x03 -#define REPORT_TYPE_SYSTEM_CONTROL 0x04 -#define REPORT_TYPE_MEDIA_CENTER 0x08 -#define REPORT_TYPE_LEDS 0x0E - -/* RF Report types bitfield */ -#define STD_KEYBOARD 0x00000002 -#define STD_MOUSE 0x00000004 -#define MULTIMEDIA 0x00000008 -#define POWER_KEYS 0x00000010 -#define MEDIA_CENTER 0x00000100 -#define KBD_LEDS 0x00004000 - -struct dj_report { - u8 report_id; - u8 device_index; - u8 report_type; - u8 report_params[DJREPORT_SHORT_LENGTH - 3]; -}; - -struct dj_receiver_dev { - struct hid_device *hdev; - struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES + - DJ_DEVICE_INDEX_MIN]; - struct work_struct work; - struct kfifo notif_fifo; - spinlock_t lock; - bool querying_devices; -}; - -struct dj_device { - struct hid_device *hdev; - struct dj_receiver_dev *dj_receiver_dev; - u32 reports_supported; - u8 device_index; -}; - -#endif -- cgit v1.2.3 From 2f31c52529103d8f0e1485272064f982d14ce54a Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:27 -0400 Subject: HID: Introduce hidpp, a module to handle Logitech hid++ devices Logitech devices use a vendor protocol to communicate various information with the device. This protocol is called HID++, and an exerpt can be found here: https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=shar The main difficulty which is related to this protocol is that it is a synchronous protocol using the input reports. So when we want to get some information from the device, we need to wait for a matching input report. This driver introduce this capabilities to be able to support the multitouch mode of the Logitech Wireless Touchpad T651 (the bluetooth one). The multitouch data is available directly from the mouse input reports, and we just need to query the device on connect about its caracteristics. HID++ and the touchpad features has a specific reporting mode which uses pure HID++ reports, but Logitech told us not to use it for this specific device. During QA, they detected that some bluetooth input reports where lost, and so the only supported mode is the pointer mode. Signed-off-by: Benjamin Tissoires Tested-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 11 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-logitech-hidpp.c | 842 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 856 insertions(+) create mode 100644 drivers/hid/hid-logitech-hidpp.c (limited to 'drivers/hid') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index f42df4dd58d2..6f299cd54e6d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -378,6 +378,17 @@ config HID_LOGITECH_DJ generic USB_HID driver and all incoming events will be multiplexed into a single mouse and a single keyboard device. +config HID_LOGITECH_HIDPP + tristate "Logitech HID++ devices support" + depends on HID_LOGITECH + ---help--- + Support for Logitech devices relyingon the HID++ Logitech specification + + Say Y if you want support for Logitech devices relying on the HID++ + specification. Such devices are the various Logitech Touchpads (T650, + T651, TK820), some mice (Zone Touch mouse), or even keyboards (Solar + Keayboard). + config LOGITECH_FF bool "Logitech force feedback support" depends on HID_LOGITECH diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index e2850d8af9ca..b102774b4e16 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.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_LOGITECH_HIDPP) += hid-logitech-hidpp.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 946540e1764d..395549fea44c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1818,6 +1818,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index cd9c9e96cf0e..2aa5091ecc08 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -575,6 +575,7 @@ #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e +#define USB_DEVICE_ID_LOGITECH_T651 0xb00c #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c new file mode 100644 index 000000000000..7dd9163f7e03 --- /dev/null +++ b/drivers/hid/hid-logitech-hidpp.c @@ -0,0 +1,842 @@ +/* + * HIDPP protocol for Logitech Unifying receivers + * + * Copyright (c) 2011 Logitech (c) + * Copyright (c) 2012-2013 Google (c) + * Copyright (c) 2013-2014 Red Hat Inc. + */ + +/* + * 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; version 2 of the License. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Benjamin Tissoires "); +MODULE_AUTHOR("Nestor Lopez Casado "); + +#define REPORT_ID_HIDPP_SHORT 0x10 +#define REPORT_ID_HIDPP_LONG 0x11 + +#define HIDPP_REPORT_SHORT_LENGTH 7 +#define HIDPP_REPORT_LONG_LENGTH 20 + +#define HIDPP_QUIRK_CLASS_WTP BIT(0) + +/* + * There are two hidpp protocols in use, the first version hidpp10 is known + * as register access protocol or RAP, the second version hidpp20 is known as + * feature access protocol or FAP + * + * Most older devices (including the Unifying usb receiver) use the RAP protocol + * where as most newer devices use the FAP protocol. Both protocols are + * compatible with the underlying transport, which could be usb, Unifiying, or + * bluetooth. The message lengths are defined by the hid vendor specific report + * descriptor for the HIDPP_SHORT report type (total message lenth 7 bytes) and + * the HIDPP_LONG report type (total message length 20 bytes) + * + * The RAP protocol uses both report types, whereas the FAP only uses HIDPP_LONG + * messages. The Unifying receiver itself responds to RAP messages (device index + * is 0xFF for the receiver), and all messages (short or long) with a device + * index between 1 and 6 are passed untouched to the corresponding paired + * Unifying device. + * + * The paired device can be RAP or FAP, it will receive the message untouched + * from the Unifiying receiver. + */ + +struct fap { + u8 feature_index; + u8 funcindex_clientid; + u8 params[HIDPP_REPORT_LONG_LENGTH - 4U]; +}; + +struct rap { + u8 sub_id; + u8 reg_address; + u8 params[HIDPP_REPORT_LONG_LENGTH - 4U]; +}; + +struct hidpp_report { + u8 report_id; + u8 device_index; + union { + struct fap fap; + struct rap rap; + u8 rawbytes[sizeof(struct fap)]; + }; +} __packed; + +struct hidpp_device { + struct hid_device *hid_dev; + struct mutex send_mutex; + void *send_receive_buf; + wait_queue_head_t wait; + bool answer_available; + u8 protocol_major; + u8 protocol_minor; + + void *private_data; + + unsigned long quirks; +}; + + +#define HIDPP_ERROR 0x8f +#define HIDPP_ERROR_SUCCESS 0x00 +#define HIDPP_ERROR_INVALID_SUBID 0x01 +#define HIDPP_ERROR_INVALID_ADRESS 0x02 +#define HIDPP_ERROR_INVALID_VALUE 0x03 +#define HIDPP_ERROR_CONNECT_FAIL 0x04 +#define HIDPP_ERROR_TOO_MANY_DEVICES 0x05 +#define HIDPP_ERROR_ALREADY_EXISTS 0x06 +#define HIDPP_ERROR_BUSY 0x07 +#define HIDPP_ERROR_UNKNOWN_DEVICE 0x08 +#define HIDPP_ERROR_RESOURCE_ERROR 0x09 +#define HIDPP_ERROR_REQUEST_UNAVAILABLE 0x0a +#define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b +#define HIDPP_ERROR_WRONG_PIN_CODE 0x0c + +static int __hidpp_send_report(struct hid_device *hdev, + struct hidpp_report *hidpp_report) +{ + int fields_count, ret; + + switch (hidpp_report->report_id) { + case REPORT_ID_HIDPP_SHORT: + fields_count = HIDPP_REPORT_SHORT_LENGTH; + break; + case REPORT_ID_HIDPP_LONG: + fields_count = HIDPP_REPORT_LONG_LENGTH; + break; + default: + return -ENODEV; + } + + /* + * set the device_index as the receiver, it will be overwritten by + * hid_hw_request if needed + */ + hidpp_report->device_index = 0xff; + + ret = hid_hw_raw_request(hdev, hidpp_report->report_id, + (u8 *)hidpp_report, fields_count, HID_OUTPUT_REPORT, + HID_REQ_SET_REPORT); + + return ret == fields_count ? 0 : -1; +} + +static int hidpp_send_message_sync(struct hidpp_device *hidpp, + struct hidpp_report *message, + struct hidpp_report *response) +{ + int ret; + + mutex_lock(&hidpp->send_mutex); + + hidpp->send_receive_buf = response; + hidpp->answer_available = false; + + /* + * So that we can later validate the answer when it arrives + * in hidpp_raw_event + */ + *response = *message; + + ret = __hidpp_send_report(hidpp->hid_dev, message); + + if (ret) { + dbg_hid("__hidpp_send_report returned err: %d\n", ret); + memset(response, 0, sizeof(struct hidpp_report)); + goto exit; + } + + if (!wait_event_timeout(hidpp->wait, hidpp->answer_available, + 5*HZ)) { + dbg_hid("%s:timeout waiting for response\n", __func__); + memset(response, 0, sizeof(struct hidpp_report)); + ret = -ETIMEDOUT; + } + + if (response->report_id == REPORT_ID_HIDPP_SHORT && + response->fap.feature_index == HIDPP_ERROR) { + ret = response->fap.params[1]; + dbg_hid("__hidpp_send_report got hidpp error %02X\n", ret); + goto exit; + } + +exit: + mutex_unlock(&hidpp->send_mutex); + return ret; + +} + +static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp, + u8 feat_index, u8 funcindex_clientid, u8 *params, int param_count, + struct hidpp_report *response) +{ + struct hidpp_report *message = kzalloc(sizeof(struct hidpp_report), + GFP_KERNEL); + int ret; + + if (param_count > sizeof(message->fap.params)) + return -EINVAL; + + message->report_id = REPORT_ID_HIDPP_LONG; + message->fap.feature_index = feat_index; + message->fap.funcindex_clientid = funcindex_clientid; + memcpy(&message->fap.params, params, param_count); + + ret = hidpp_send_message_sync(hidpp, message, response); + kfree(message); + return ret; +} + +static inline bool hidpp_match_answer(struct hidpp_report *question, + struct hidpp_report *answer) +{ + return (answer->fap.feature_index == question->fap.feature_index) && + (answer->fap.funcindex_clientid == question->fap.funcindex_clientid); +} + +static inline bool hidpp_match_error(struct hidpp_report *question, + struct hidpp_report *answer) +{ + return (answer->fap.feature_index == HIDPP_ERROR) && + (answer->fap.funcindex_clientid == question->fap.feature_index) && + (answer->fap.params[0] == question->fap.funcindex_clientid); +} + +/* -------------------------------------------------------------------------- */ +/* 0x0000: Root */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_ROOT 0x0000 +#define HIDPP_PAGE_ROOT_IDX 0x00 + +#define CMD_ROOT_GET_FEATURE 0x01 +#define CMD_ROOT_GET_PROTOCOL_VERSION 0x11 + +static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16 feature, + u8 *feature_index, u8 *feature_type) +{ + struct hidpp_report response; + int ret; + u8 params[2] = { feature >> 8, feature & 0x00FF }; + + ret = hidpp_send_fap_command_sync(hidpp, + HIDPP_PAGE_ROOT_IDX, + CMD_ROOT_GET_FEATURE, + params, 2, &response); + if (ret) + return ret; + + *feature_index = response.fap.params[0]; + *feature_type = response.fap.params[1]; + + return ret; +} + +static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp) +{ + struct hidpp_report response; + int ret; + + ret = hidpp_send_fap_command_sync(hidpp, + HIDPP_PAGE_ROOT_IDX, + CMD_ROOT_GET_PROTOCOL_VERSION, + NULL, 0, &response); + + if (ret == 1) { + hidpp->protocol_major = 1; + hidpp->protocol_minor = 0; + return 0; + } + + if (ret) + return -ret; + + hidpp->protocol_major = response.fap.params[0]; + hidpp->protocol_minor = response.fap.params[1]; + + return ret; +} + +static bool hidpp_is_connected(struct hidpp_device *hidpp) +{ + int ret; + + ret = hidpp_root_get_protocol_version(hidpp); + if (!ret) + hid_dbg(hidpp->hid_dev, "HID++ %u.%u device connected.\n", + hidpp->protocol_major, hidpp->protocol_minor); + return ret == 0; +} + +/* -------------------------------------------------------------------------- */ +/* 0x0005: GetDeviceNameType */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_GET_DEVICE_NAME_TYPE 0x0005 + +#define CMD_GET_DEVICE_NAME_TYPE_GET_COUNT 0x01 +#define CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME 0x11 +#define CMD_GET_DEVICE_NAME_TYPE_GET_TYPE 0x21 + +static int hidpp_devicenametype_get_count(struct hidpp_device *hidpp, + u8 feature_index, u8 *nameLength) +{ + struct hidpp_report response; + int ret; + + ret = hidpp_send_fap_command_sync(hidpp, feature_index, + CMD_GET_DEVICE_NAME_TYPE_GET_COUNT, NULL, 0, &response); + + if (ret) + return -ret; + + *nameLength = response.fap.params[0]; + + return ret; +} + +static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp, + u8 feature_index, u8 char_index, char *device_name, int len_buf) +{ + struct hidpp_report response; + int ret, i; + int count; + + ret = hidpp_send_fap_command_sync(hidpp, feature_index, + CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME, &char_index, 1, + &response); + + if (ret) + return -ret; + + if (response.report_id == REPORT_ID_HIDPP_LONG) + count = HIDPP_REPORT_LONG_LENGTH - 4; + else + count = HIDPP_REPORT_SHORT_LENGTH - 4; + + if (len_buf < count) + count = len_buf; + + for (i = 0; i < count; i++) + device_name[i] = response.fap.params[i]; + + return count; +} + +static char *hidpp_get_device_name(struct hidpp_device *hidpp, u8 *name_length) +{ + u8 feature_type; + u8 feature_index; + u8 __name_length; + char *name; + unsigned index = 0; + int ret; + + ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_GET_DEVICE_NAME_TYPE, + &feature_index, &feature_type); + if (ret) + goto out_err; + + ret = hidpp_devicenametype_get_count(hidpp, feature_index, + &__name_length); + if (ret) + goto out_err; + + name = kzalloc(__name_length + 1, GFP_KERNEL); + if (!name) + goto out_err; + + *name_length = __name_length + 1; + while (index < __name_length) + index += hidpp_devicenametype_get_device_name(hidpp, + feature_index, index, name + index, + __name_length - index); + + return name; + +out_err: + *name_length = 0; + return NULL; +} + +/* -------------------------------------------------------------------------- */ +/* 0x6100: TouchPadRawXY */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_TOUCHPAD_RAW_XY 0x6100 + +#define CMD_TOUCHPAD_GET_RAW_INFO 0x01 + +#define TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT 0x01 +#define TOUCHPAD_RAW_XY_ORIGIN_UPPER_LEFT 0x03 + +struct hidpp_touchpad_raw_info { + u16 x_size; + u16 y_size; + u8 z_range; + u8 area_range; + u8 timestamp_unit; + u8 maxcontacts; + u8 origin; + u16 res; +}; + +struct hidpp_touchpad_raw_xy_finger { + u8 contact_type; + u8 contact_status; + u16 x; + u16 y; + u8 z; + u8 area; + u8 finger_id; +}; + +struct hidpp_touchpad_raw_xy { + u16 timestamp; + struct hidpp_touchpad_raw_xy_finger fingers[2]; + u8 spurious_flag; + u8 end_of_frame; + u8 finger_count; + u8 button; +}; + +static int hidpp_touchpad_get_raw_info(struct hidpp_device *hidpp, + u8 feature_index, struct hidpp_touchpad_raw_info *raw_info) +{ + struct hidpp_report response; + int ret; + u8 *params = (u8 *)response.fap.params; + + ret = hidpp_send_fap_command_sync(hidpp, feature_index, + CMD_TOUCHPAD_GET_RAW_INFO, NULL, 0, &response); + + if (ret) + return -ret; + + raw_info->x_size = get_unaligned_be16(¶ms[0]); + raw_info->y_size = get_unaligned_be16(¶ms[2]); + raw_info->z_range = params[4]; + raw_info->area_range = params[5]; + raw_info->maxcontacts = params[7]; + raw_info->origin = params[8]; + /* res is given in unit per inch */ + raw_info->res = get_unaligned_be16(¶ms[13]) * 2 / 51; + + return ret; +} + +/* ************************************************************************** */ +/* */ +/* Device Support */ +/* */ +/* ************************************************************************** */ + +/* -------------------------------------------------------------------------- */ +/* Touchpad HID++ devices */ +/* -------------------------------------------------------------------------- */ + +struct wtp_data { + struct input_dev *input; + u16 x_size, y_size; + u8 finger_count; + u8 mt_feature_index; + u8 button_feature_index; + u8 maxcontacts; + bool flip_y; + unsigned int resolution; +}; + +static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + return -1; +} + +static void wtp_input_configured(struct hid_device *hdev, + struct hid_input *hidinput) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + struct wtp_data *wd = hidpp->private_data; + struct input_dev *input_dev = hidinput->input; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __clear_bit(EV_REL, input_dev->evbit); + __clear_bit(EV_LED, input_dev->evbit); + + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, wd->x_size, 0, 0); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, wd->resolution); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, wd->y_size, 0, 0); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, wd->resolution); + + /* Max pressure is not given by the devices, pick one */ + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 50, 0, 0); + + input_set_capability(input_dev, EV_KEY, BTN_LEFT); + + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + input_mt_init_slots(input_dev, wd->maxcontacts, INPUT_MT_POINTER | + INPUT_MT_DROP_UNUSED); + + wd->input = input_dev; +} + +static void wtp_touch_event(struct wtp_data *wd, + struct hidpp_touchpad_raw_xy_finger *touch_report) +{ + int slot; + + if (!touch_report->finger_id || touch_report->contact_type) + /* no actual data */ + return; + + slot = input_mt_get_slot_by_key(wd->input, touch_report->finger_id); + + input_mt_slot(wd->input, slot); + input_mt_report_slot_state(wd->input, MT_TOOL_FINGER, + touch_report->contact_status); + if (touch_report->contact_status) { + input_event(wd->input, EV_ABS, ABS_MT_POSITION_X, + touch_report->x); + input_event(wd->input, EV_ABS, ABS_MT_POSITION_Y, + wd->flip_y ? wd->y_size - touch_report->y : + touch_report->y); + input_event(wd->input, EV_ABS, ABS_MT_PRESSURE, + touch_report->area); + } +} + +static void wtp_send_raw_xy_event(struct hidpp_device *hidpp, + struct hidpp_touchpad_raw_xy *raw) +{ + struct wtp_data *wd = hidpp->private_data; + int i; + + for (i = 0; i < 2; i++) + wtp_touch_event(wd, &(raw->fingers[i])); + + if (raw->end_of_frame) + input_event(wd->input, EV_KEY, BTN_LEFT, raw->button); + + if (raw->end_of_frame || raw->finger_count <= 2) { + input_mt_sync_frame(wd->input); + input_sync(wd->input); + } +} + +static int wtp_mouse_raw_xy_event(struct hidpp_device *hidpp, u8 *data) +{ + struct wtp_data *wd = hidpp->private_data; + u8 c1_area = ((data[7] & 0xf) * (data[7] & 0xf) + + (data[7] >> 4) * (data[7] >> 4)) / 2; + u8 c2_area = ((data[13] & 0xf) * (data[13] & 0xf) + + (data[13] >> 4) * (data[13] >> 4)) / 2; + struct hidpp_touchpad_raw_xy raw = { + .timestamp = data[1], + .fingers = { + { + .contact_type = 0, + .contact_status = !!data[7], + .x = get_unaligned_le16(&data[3]), + .y = get_unaligned_le16(&data[5]), + .z = c1_area, + .area = c1_area, + .finger_id = data[2], + }, { + .contact_type = 0, + .contact_status = !!data[13], + .x = get_unaligned_le16(&data[9]), + .y = get_unaligned_le16(&data[11]), + .z = c2_area, + .area = c2_area, + .finger_id = data[8], + } + }, + .finger_count = wd->maxcontacts, + .spurious_flag = 0, + .end_of_frame = (data[0] >> 7) == 0, + .button = data[0] & 0x01, + }; + + wtp_send_raw_xy_event(hidpp, &raw); + + return 1; +} + +static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + struct wtp_data *wd = hidpp->private_data; + + if (!wd || !wd->input || (data[0] != 0x02) || size < 21) + return 1; + + return wtp_mouse_raw_xy_event(hidpp, &data[7]); +} + +static int wtp_get_config(struct hidpp_device *hidpp) +{ + struct wtp_data *wd = hidpp->private_data; + struct hidpp_touchpad_raw_info raw_info = {0}; + u8 feature_type; + int ret; + + ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_TOUCHPAD_RAW_XY, + &wd->mt_feature_index, &feature_type); + if (ret) + /* means that the device is not powered up */ + return ret; + + ret = hidpp_touchpad_get_raw_info(hidpp, wd->mt_feature_index, + &raw_info); + if (ret) + return ret; + + wd->x_size = raw_info.x_size; + wd->y_size = raw_info.y_size; + wd->maxcontacts = raw_info.maxcontacts; + wd->flip_y = raw_info.origin == TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT; + wd->resolution = raw_info.res; + + return 0; +} + +static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + struct wtp_data *wd; + + wd = devm_kzalloc(&hdev->dev, sizeof(struct wtp_data), + GFP_KERNEL); + if (!wd) + return -ENOMEM; + + hidpp->private_data = wd; + + return 0; +}; + +/* -------------------------------------------------------------------------- */ +/* Generic HID++ devices */ +/* -------------------------------------------------------------------------- */ + +static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) + return wtp_input_mapping(hdev, hi, field, usage, bit, max); + + return 0; +} + +static void hidpp_input_configured(struct hid_device *hdev, + struct hid_input *hidinput) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) + wtp_input_configured(hdev, hidinput); +} + +static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, + int size) +{ + struct hidpp_report *question = hidpp->send_receive_buf; + struct hidpp_report *answer = hidpp->send_receive_buf; + struct hidpp_report *report = (struct hidpp_report *)data; + + /* + * If the mutex is locked then we have a pending answer from a + * previoulsly sent command + */ + if (unlikely(mutex_is_locked(&hidpp->send_mutex))) { + /* + * Check for a correct hidpp20 answer or the corresponding + * error + */ + if (hidpp_match_answer(question, report) || + hidpp_match_error(question, report)) { + *answer = *report; + hidpp->answer_available = true; + wake_up(&hidpp->wait); + /* + * This was an answer to a command that this driver sent + * We return 1 to hid-core to avoid forwarding the + * command upstream as it has been treated by the driver + */ + + return 1; + } + } + + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) + return wtp_raw_event(hidpp->hid_dev, data, size); + + return 0; +} + +static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + + switch (data[0]) { + case REPORT_ID_HIDPP_LONG: + if (size != HIDPP_REPORT_LONG_LENGTH) { + hid_err(hdev, "received hid++ report of bad size (%d)", + size); + return 1; + } + return hidpp_raw_hidpp_event(hidpp, data, size); + case REPORT_ID_HIDPP_SHORT: + if (size != HIDPP_REPORT_SHORT_LENGTH) { + hid_err(hdev, "received hid++ report of bad size (%d)", + size); + return 1; + } + return hidpp_raw_hidpp_event(hidpp, data, size); + } + + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) + return wtp_raw_event(hdev, data, size); + + return 0; +} + +static void hidpp_overwrite_name(struct hid_device *hdev) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + char *name; + u8 name_length; + + name = hidpp_get_device_name(hidpp, &name_length); + + if (!name) + hid_err(hdev, "unable to retrieve the name of the device"); + else + snprintf(hdev->name, sizeof(hdev->name), "%s", name); + + kfree(name); +} + +static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct hidpp_device *hidpp; + int ret; + bool connected; + + hidpp = devm_kzalloc(&hdev->dev, sizeof(struct hidpp_device), + GFP_KERNEL); + if (!hidpp) + return -ENOMEM; + + hidpp->hid_dev = hdev; + hid_set_drvdata(hdev, hidpp); + + hidpp->quirks = id->driver_data; + + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { + ret = wtp_allocate(hdev, id); + if (ret) + return ret; + } + + mutex_init(&hidpp->send_mutex); + init_waitqueue_head(&hidpp->wait); + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "%s:parse failed\n", __func__); + goto hid_parse_fail; + } + + /* Allow incoming packets */ + hid_device_io_start(hdev); + + connected = hidpp_is_connected(hidpp); + if (!connected) { + hid_err(hdev, "Device not connected"); + goto hid_parse_fail; + } + + /* the device is connected, we can ask for its name */ + hid_info(hdev, "HID++ %u.%u device connected.\n", + hidpp->protocol_major, hidpp->protocol_minor); + hidpp_overwrite_name(hdev); + + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { + ret = wtp_get_config(hidpp); + if (ret) + goto hid_parse_fail; + } + + /* Block incoming packets */ + hid_device_io_stop(hdev); + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "%s:hid_hw_start returned error\n", __func__); + goto hid_hw_start_fail; + } + + return ret; + +hid_hw_start_fail: +hid_parse_fail: + mutex_destroy(&hidpp->send_mutex); + hid_set_drvdata(hdev, NULL); + return ret; +} + +static void hidpp_remove(struct hid_device *hdev) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + + mutex_destroy(&hidpp->send_mutex); + hid_hw_stop(hdev); +} + +static const struct hid_device_id hidpp_devices[] = { + { /* wireless touchpad T651 */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_T651), + .driver_data = HIDPP_QUIRK_CLASS_WTP }, + {} +}; + +MODULE_DEVICE_TABLE(hid, hidpp_devices); + +static struct hid_driver hidpp_driver = { + .name = "logitech-hidpp-device", + .id_table = hidpp_devices, + .probe = hidpp_probe, + .remove = hidpp_remove, + .raw_event = hidpp_raw_event, + .input_configured = hidpp_input_configured, + .input_mapping = hidpp_input_mapping, +}; + +module_hid_driver(hidpp_driver); -- cgit v1.2.3 From ab94e562ed45c99914fe874b7feaf75b80ceea84 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:28 -0400 Subject: HID: logitech: move dj devices to the HID++ module Devices connected through the Logitech Wireless Receiver are HID++ devices. We can handle them here to benefit from this new module and activate enhaced support of the various wireless touchpad or mice with touch sensors on them. Signed-off-by: Benjamin Tissoires Tested-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 1 + drivers/hid/hid-logitech-dj.c | 42 +--------------------------------------- drivers/hid/hid-logitech-hidpp.c | 21 ++++++++++++-------- 3 files changed, 15 insertions(+), 49 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 6f299cd54e6d..dd49a9b14b9c 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -371,6 +371,7 @@ config HID_LOGITECH_DJ tristate "Logitech Unifying receivers full support" depends on HIDRAW depends on HID_LOGITECH + select HID_LOGITECH_HIDPP ---help--- Say Y if you want support for Logitech Unifying receivers and devices. Unifying receivers are capable of pairing up to 6 Logitech compliant diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 6aea16dca3ff..45a7eacdfe98 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -977,48 +977,8 @@ static struct hid_driver logi_djreceiver_driver = { #endif }; +module_hid_driver(logi_djreceiver_driver); -static const struct hid_device_id logi_dj_devices[] = { - { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, - USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, - {} -}; - -static struct hid_driver logi_djdevice_driver = { - .name = "logitech-djdevice", - .id_table = logi_dj_devices, -}; - - -static int __init logi_dj_init(void) -{ - int retval; - - dbg_hid("Logitech-DJ:%s\n", __func__); - - retval = hid_register_driver(&logi_djreceiver_driver); - if (retval) - return retval; - - retval = hid_register_driver(&logi_djdevice_driver); - if (retval) - hid_unregister_driver(&logi_djreceiver_driver); - - return retval; - -} - -static void __exit logi_dj_exit(void) -{ - dbg_hid("Logitech-DJ:%s\n", __func__); - - hid_unregister_driver(&logi_djdevice_driver); - hid_unregister_driver(&logi_djreceiver_driver); - -} - -module_init(logi_dj_init); -module_exit(logi_dj_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Logitech"); MODULE_AUTHOR("Nestor Lopez Casado"); diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 7dd9163f7e03..48dec394dd38 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -777,15 +777,17 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) hid_device_io_start(hdev); connected = hidpp_is_connected(hidpp); - if (!connected) { - hid_err(hdev, "Device not connected"); - goto hid_parse_fail; - } + if (id->group != HID_GROUP_LOGITECH_DJ_DEVICE) { + if (!connected) { + hid_err(hdev, "Device not connected"); + goto hid_parse_fail; + } - /* the device is connected, we can ask for its name */ - hid_info(hdev, "HID++ %u.%u device connected.\n", - hidpp->protocol_major, hidpp->protocol_minor); - hidpp_overwrite_name(hdev); + /* the device is connected, we can ask for its name */ + hid_info(hdev, "HID++ %u.%u device connected.\n", + hidpp->protocol_major, hidpp->protocol_minor); + hidpp_overwrite_name(hdev); + } if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { ret = wtp_get_config(hidpp); @@ -824,6 +826,9 @@ static const struct hid_device_id hidpp_devices[] = { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651), .driver_data = HIDPP_QUIRK_CLASS_WTP }, + + { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, {} }; -- cgit v1.2.3 From 925f0f3ed24f98b40c28627e74ff3e7f9d1e28bc Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:29 -0400 Subject: HID: logitech-dj: allow transfer of HID++ reports from/to the correct dj device HID++ is a Logitech-specific protocol for communicating with HID devices. DJ devices implement HID++, and so we can add the HID++ collection in the report descriptor and forward the incoming reports from the receiver to the appropriate DJ device. The same can be done in the other way, if someone calls a .raw_request(), we can forward it to the correct dj device by overriding the device_index in the HID++ report. Signed-off-by: Benjamin Tisssoires Tested-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 188 +++++++++++++++++++++++++++++++++++------- 1 file changed, 160 insertions(+), 28 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 45a7eacdfe98..feddacd87b8b 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -42,6 +42,15 @@ #define REPORT_ID_DJ_SHORT 0x20 #define REPORT_ID_DJ_LONG 0x21 +#define REPORT_ID_HIDPP_SHORT 0x10 +#define REPORT_ID_HIDPP_LONG 0x11 + +#define HIDPP_REPORT_SHORT_LENGTH 7 +#define HIDPP_REPORT_LONG_LENGTH 20 + +#define HIDPP_RECEIVER_INDEX 0xff + +#define REPORT_TYPE_RFREPORT_FIRST 0x01 #define REPORT_TYPE_RFREPORT_LAST 0x1F /* Command Switch to DJ mode */ @@ -242,6 +251,57 @@ static const char media_descriptor[] = { 0xc0, /* EndCollection */ }; /* */ +/* HIDPP descriptor */ +static const char hidpp_descriptor[] = { + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x09, 0x01, /* Usage (Vendor Usage 1) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x85, 0x10, /* Report ID (16) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x06, /* Report Count (6) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xff, 0x00, /* Logical Maximum (255) */ + 0x09, 0x01, /* Usage (Vendor Usage 1) */ + 0x81, 0x00, /* Input (Data,Arr,Abs) */ + 0x09, 0x01, /* Usage (Vendor Usage 1) */ + 0x91, 0x00, /* Output (Data,Arr,Abs) */ + 0xc0, /* End Collection */ + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x09, 0x02, /* Usage (Vendor Usage 2) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x85, 0x11, /* Report ID (17) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x13, /* Report Count (19) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xff, 0x00, /* Logical Maximum (255) */ + 0x09, 0x02, /* Usage (Vendor Usage 2) */ + 0x81, 0x00, /* Input (Data,Arr,Abs) */ + 0x09, 0x02, /* Usage (Vendor Usage 2) */ + 0x91, 0x00, /* Output (Data,Arr,Abs) */ + 0xc0, /* End Collection */ + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x09, 0x04, /* Usage (Vendor Usage 0x04) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x85, 0x20, /* Report ID (32) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x0e, /* Report Count (14) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xff, 0x00, /* Logical Maximum (255) */ + 0x09, 0x41, /* Usage (Vendor Usage 0x41) */ + 0x81, 0x00, /* Input (Data,Arr,Abs) */ + 0x09, 0x41, /* Usage (Vendor Usage 0x41) */ + 0x91, 0x00, /* Output (Data,Arr,Abs) */ + 0x85, 0x21, /* Report ID (33) */ + 0x95, 0x1f, /* Report Count (31) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xff, 0x00, /* Logical Maximum (255) */ + 0x09, 0x42, /* Usage (Vendor Usage 0x42) */ + 0x81, 0x00, /* Input (Data,Arr,Abs) */ + 0x09, 0x42, /* Usage (Vendor Usage 0x42) */ + 0x91, 0x00, /* Output (Data,Arr,Abs) */ + 0xc0, /* End Collection */ +}; + /* Maximum size of all defined hid reports in bytes (including report id) */ #define MAX_REPORT_SIZE 8 @@ -251,7 +311,8 @@ static const char media_descriptor[] = { sizeof(mse_descriptor) + \ sizeof(consumer_descriptor) + \ sizeof(syscontrol_descriptor) + \ - sizeof(media_descriptor)) + sizeof(media_descriptor) + \ + sizeof(hidpp_descriptor)) /* Number of possible hid report types that can be created by this driver. * @@ -512,6 +573,13 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev, } } +static void logi_dj_recv_forward_hidpp(struct dj_device *dj_dev, u8 *data, + int size) +{ + /* We are called from atomic context (tasklet && djrcv->lock held) */ + if (hid_input_report(dj_dev->hdev, HID_INPUT_REPORT, data, size, 1)) + dbg_hid("hid_input_report error\n"); +} static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev, struct dj_report *dj_report) @@ -609,6 +677,16 @@ static int logi_dj_ll_raw_request(struct hid_device *hid, u8 *out_buf; int ret; + if ((buf[0] == REPORT_ID_HIDPP_SHORT) || + (buf[0] == REPORT_ID_HIDPP_LONG)) { + if (count < 2) + return -EINVAL; + + buf[1] = djdev->device_index; + return hid_hw_raw_request(djrcv_dev->hdev, reportnum, buf, + count, report_type, reqtype); + } + if (buf[0] != REPORT_TYPE_LEDS) return -EINVAL; @@ -687,6 +765,8 @@ static int logi_dj_ll_parse(struct hid_device *hid) __func__, djdev->reports_supported); } + rdcat(rdesc, &rsize, hidpp_descriptor, sizeof(hidpp_descriptor)); + retval = hid_parse_report(hid, rdesc, rsize); kfree(rdesc); @@ -714,8 +794,7 @@ static struct hid_ll_driver logi_dj_ll_driver = { .raw_request = logi_dj_ll_raw_request, }; - -static int logi_dj_raw_event(struct hid_device *hdev, +static int logi_dj_dj_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { @@ -723,36 +802,24 @@ static int logi_dj_raw_event(struct hid_device *hdev, struct dj_report *dj_report = (struct dj_report *) data; unsigned long flags; - dbg_hid("%s, size:%d\n", __func__, size); - - /* Here we receive all data coming from iface 2, there are 4 cases: - * - * 1) Data should continue its normal processing i.e. data does not - * come from the DJ collection, in which case we do nothing and - * return 0, so hid-core can continue normal processing (will forward - * to associated hidraw device) + /* + * Here we receive all data coming from iface 2, there are 3 cases: * - * 2) Data is from DJ collection, and is intended for this driver i. e. - * data contains arrival, departure, etc notifications, in which case - * we queue them for delayed processing by the work queue. We return 1 - * to hid-core as no further processing is required from it. + * 1) Data is intended for this driver i. e. data contains arrival, + * departure, etc notifications, in which case we queue them for delayed + * processing by the work queue. We return 1 to hid-core as no further + * processing is required from it. * - * 3) Data is from DJ collection, and informs a connection change, - * if the change means rf link loss, then we must send a null report - * to the upper layer to discard potentially pressed keys that may be - * repeated forever by the input layer. Return 1 to hid-core as no - * further processing is required. + * 2) Data informs a connection change, if the change means rf link + * loss, then we must send a null report to the upper layer to discard + * potentially pressed keys that may be repeated forever by the input + * layer. Return 1 to hid-core as no further processing is required. * - * 4) Data is from DJ collection and is an actual input event from - * a paired DJ device in which case we forward it to the correct hid - * device (via hid_input_report() ) and return 1 so hid-core does not do - * anything else with it. + * 3) Data is an actual input event from a paired DJ device in which + * case we forward it to the correct hid device (via hid_input_report() + * ) and return 1 so hid-core does not anything else with it. */ - /* case 1) */ - if (data[0] != REPORT_ID_DJ_SHORT) - return false; - if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) || (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) { /* @@ -797,6 +864,71 @@ out: return true; } +static int logi_dj_hidpp_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, + int size) +{ + struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev); + struct dj_report *dj_report = (struct dj_report *) data; + unsigned long flags; + u8 device_index = dj_report->device_index; + + if (device_index == HIDPP_RECEIVER_INDEX) + return false; + + /* + * Data is from the HID++ collection, in this case, we forward the + * data to the corresponding child dj device and return 0 to hid-core + * so he data also goes to the hidraw device of the receiver. This + * allows a user space application to implement the full HID++ routing + * via the receiver. + */ + + if ((device_index < DJ_DEVICE_INDEX_MIN) || + (device_index > DJ_DEVICE_INDEX_MAX)) { + /* + * Device index is wrong, bail out. + * This driver can ignore safely the receiver notifications, + * so ignore those reports too. + */ + dev_err(&hdev->dev, "%s: invalid device index:%d\n", + __func__, dj_report->device_index); + return false; + } + + spin_lock_irqsave(&djrcv_dev->lock, flags); + + if (!djrcv_dev->paired_dj_devices[device_index]) + /* received an event for an unknown device, bail out */ + goto out; + + logi_dj_recv_forward_hidpp(djrcv_dev->paired_dj_devices[device_index], + data, size); + +out: + spin_unlock_irqrestore(&djrcv_dev->lock, flags); + + return false; +} + +static int logi_dj_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, + int size) +{ + dbg_hid("%s, size:%d\n", __func__, size); + + switch (data[0]) { + case REPORT_ID_DJ_SHORT: + return logi_dj_dj_event(hdev, report, data, size); + case REPORT_ID_HIDPP_SHORT: + /* intentional fallthrough */ + case REPORT_ID_HIDPP_LONG: + return logi_dj_hidpp_event(hdev, report, data, size); + } + + return false; +} + static int logi_dj_probe(struct hid_device *hdev, const struct hid_device_id *id) { -- cgit v1.2.3 From 33797820af98cde5c7cee00d00f0d8e255ea199f Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:30 -0400 Subject: HID: logitech: allow the DJ device to request the unifying name The names of the DJ devices are stored in the receiver. These names can be retrieved through a HID++ command. However, the protocol says that you have to ask the receiver for that, not the device iteself. Introduce a special case in the DJ handling where a device can request its unifying name, and when such a name is given, forward it also to the corresponding device. On the HID++ side, the receiver talks only HID++ 1.0, so we need to implement this part of the protocol in the module. Signed-off-by: Benjamin Tissoires Tested-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 24 ++++++++++-- drivers/hid/hid-logitech-hidpp.c | 80 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 7 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index feddacd87b8b..9bc39421627f 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -667,6 +667,9 @@ static void logi_dj_ll_close(struct hid_device *hid) dbg_hid("%s:%s\n", __func__, hid->phys); } +static u8 unifying_name_query[] = {0x10, 0xff, 0x83, 0xb5, 0x40, 0x00, 0x00}; +static u8 unifying_name_answer[] = {0x11, 0xff, 0x83, 0xb5}; + static int logi_dj_ll_raw_request(struct hid_device *hid, unsigned char reportnum, __u8 *buf, size_t count, unsigned char report_type, @@ -682,7 +685,13 @@ static int logi_dj_ll_raw_request(struct hid_device *hid, if (count < 2) return -EINVAL; - buf[1] = djdev->device_index; + /* special case where we should not overwrite + * the device_index */ + if (count == 7 && !memcmp(buf, unifying_name_query, + sizeof(unifying_name_query))) + buf[4] |= djdev->device_index - 1; + else + buf[1] = djdev->device_index; return hid_hw_raw_request(djrcv_dev->hdev, reportnum, buf, count, report_type, reqtype); } @@ -873,8 +882,17 @@ static int logi_dj_hidpp_event(struct hid_device *hdev, unsigned long flags; u8 device_index = dj_report->device_index; - if (device_index == HIDPP_RECEIVER_INDEX) - return false; + if (device_index == HIDPP_RECEIVER_INDEX) { + /* special case were the device wants to know its unifying + * name */ + if (size == HIDPP_REPORT_LONG_LENGTH && + !memcmp(data, unifying_name_answer, + sizeof(unifying_name_answer)) && + ((data[4] & 0xF0) == 0x40)) + device_index = (data[4] & 0x0F) + 1; + else + return false; + } /* * Data is from the HID++ collection, in this case, we forward the diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 48dec394dd38..e748e45b5b2f 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -205,6 +205,31 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp, return ret; } +static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev, + u8 report_id, u8 sub_id, u8 reg_address, u8 *params, int param_count, + struct hidpp_report *response) +{ + struct hidpp_report *message = kzalloc(sizeof(struct hidpp_report), + GFP_KERNEL); + int ret; + + if ((report_id != REPORT_ID_HIDPP_SHORT) && + (report_id != REPORT_ID_HIDPP_LONG)) + return -EINVAL; + + if (param_count > sizeof(message->rap.params)) + return -EINVAL; + + message->report_id = report_id; + message->rap.sub_id = sub_id; + message->rap.reg_address = reg_address; + memcpy(&message->rap.params, params, param_count); + + ret = hidpp_send_message_sync(hidpp_dev, message, response); + kfree(message); + return ret; +} + static inline bool hidpp_match_answer(struct hidpp_report *question, struct hidpp_report *answer) { @@ -220,6 +245,45 @@ static inline bool hidpp_match_error(struct hidpp_report *question, (answer->fap.params[0] == question->fap.funcindex_clientid); } +/* -------------------------------------------------------------------------- */ +/* HIDP++ 1.0 commands */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_SET_REGISTER 0x80 +#define HIDPP_GET_REGISTER 0x81 +#define HIDPP_SET_LONG_REGISTER 0x82 +#define HIDPP_GET_LONG_REGISTER 0x83 + +#define HIDPP_REG_PAIRING_INFORMATION 0xB5 +#define DEVICE_NAME 0x40 + +static char *hidpp_get_unifying_name(struct hidpp_device *hidpp_dev) +{ + struct hidpp_report response; + int ret; + /* hid-logitech-dj is in charge of setting the right device index */ + u8 params[1] = { DEVICE_NAME }; + char *name; + int len; + + ret = hidpp_send_rap_command_sync(hidpp_dev, + REPORT_ID_HIDPP_SHORT, + HIDPP_GET_LONG_REGISTER, + HIDPP_REG_PAIRING_INFORMATION, + params, 1, &response); + if (ret) + return NULL; + + len = response.rap.params[1]; + + name = kzalloc(len + 1, GFP_KERNEL); + if (!name) + return NULL; + + memcpy(name, &response.rap.params[2], len); + return name; +} + /* -------------------------------------------------------------------------- */ /* 0x0000: Root */ /* -------------------------------------------------------------------------- */ @@ -726,13 +790,21 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report, return 0; } -static void hidpp_overwrite_name(struct hid_device *hdev) +static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); char *name; u8 name_length; - name = hidpp_get_device_name(hidpp, &name_length); + if (use_unifying) + /* + * the device is connected through an Unifying receiver, and + * might not be already connected. + * Ask the receiver for its name. + */ + name = hidpp_get_unifying_name(hidpp); + else + name = hidpp_get_device_name(hidpp, &name_length); if (!name) hid_err(hdev, "unable to retrieve the name of the device"); @@ -783,12 +855,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) goto hid_parse_fail; } - /* the device is connected, we can ask for its name */ hid_info(hdev, "HID++ %u.%u device connected.\n", hidpp->protocol_major, hidpp->protocol_minor); - hidpp_overwrite_name(hdev); } + hidpp_overwrite_name(hdev, id->group == HID_GROUP_LOGITECH_DJ_DEVICE); + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { ret = wtp_get_config(hidpp); if (ret) -- cgit v1.2.3 From 6a9ddc8978835deae0b2d918df74fc83588a4104 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:31 -0400 Subject: HID: logitech-dj: enable notifications on connect/disconnect The receiver can send HID++ notifications to the DJ devices when the physical devices are connected/disconnected. Enable this feature by default. This command uses a HID++ command instead of a DJ one, so use a direct call to usbhid instead of using logi_dj_recv_send_report() Signed-off-by: Benjamin Tissoires Tested-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 9bc39421627f..c917ab61aafa 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -630,7 +630,9 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev) static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, unsigned timeout) { + struct hid_device *hdev = djrcv_dev->hdev; struct dj_report *dj_report; + u8 *buf; int retval; dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL); @@ -642,7 +644,6 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, dj_report->report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x3F; dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout; retval = logi_dj_recv_send_report(djrcv_dev, dj_report); - kfree(dj_report); /* * Ugly sleep to work around a USB 3.0 bug when the receiver is still @@ -651,6 +652,30 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, */ msleep(50); + /* + * Magical bits to set up hidpp notifications when the dj devices + * are connected/disconnected. + * + * We can reuse dj_report because HIDPP_REPORT_SHORT_LENGTH is smaller + * than DJREPORT_SHORT_LENGTH. + */ + buf = (u8 *)dj_report; + + memset(buf, 0, HIDPP_REPORT_SHORT_LENGTH); + + buf[0] = REPORT_ID_HIDPP_SHORT; + buf[1] = 0xFF; + buf[2] = 0x80; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x09; + buf[6] = 0x00; + + hid_hw_raw_request(hdev, REPORT_ID_HIDPP_SHORT, buf, + HIDPP_REPORT_SHORT_LENGTH, HID_OUTPUT_REPORT, + HID_REQ_SET_REPORT); + + kfree(dj_report); return retval; } -- cgit v1.2.3 From c39e3d5fc9dd3e16c6f59dd94d827540040de66d Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:32 -0400 Subject: HID: logitech-hidpp: late bind the input device on wireless connection Now that the receiver forwards the connect/disconnect events, we can know when the device is available to communicate with us. When it is ready, we can for instance retrieve its full name, which guarantee that we always have the same name for the DJ device (the DJ name is somewhat shorter than the HID++ name). This mechanism is mandatory for the touchpads line, which has the min/max information stored in the device. This information can only be retrieved when the device is connected. So we can not populate the input device until we are sure that the device is connected. This patch creates a new input device for such devices. However, this input is not bound to hid directly, so the various drivers which wants to use it are required to process completely the incoming reports in .raw_event(). Note that the patch in itself just adds the bits for the next ones, and this feature is disabled by default. Signed-off-by: Benjamin Tissoires Tested-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 155 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 147 insertions(+), 8 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index e748e45b5b2f..9561a1f9b42b 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -36,6 +36,9 @@ MODULE_AUTHOR("Nestor Lopez Casado "); #define HIDPP_QUIRK_CLASS_WTP BIT(0) +/* bits 1..20 are reserved for classes */ +#define HIDPP_QUIRK_DELAYED_INIT BIT(21) + /* * There are two hidpp protocols in use, the first version hidpp10 is known * as register access protocol or RAP, the second version hidpp20 is known as @@ -91,6 +94,11 @@ struct hidpp_device { void *private_data; + struct work_struct work; + struct kfifo delayed_work_fifo; + atomic_t connected; + struct input_dev *delayed_input; + unsigned long quirks; }; @@ -110,6 +118,8 @@ struct hidpp_device { #define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b #define HIDPP_ERROR_WRONG_PIN_CODE 0x0c +static void hidpp_connect_event(struct hidpp_device *hidpp_dev); + static int __hidpp_send_report(struct hid_device *hdev, struct hidpp_report *hidpp_report) { @@ -230,6 +240,13 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev, return ret; } +static void delayed_work_cb(struct work_struct *work) +{ + struct hidpp_device *hidpp = container_of(work, struct hidpp_device, + work); + hidpp_connect_event(hidpp); +} + static inline bool hidpp_match_answer(struct hidpp_report *question, struct hidpp_report *answer) { @@ -245,6 +262,12 @@ static inline bool hidpp_match_error(struct hidpp_report *question, (answer->fap.params[0] == question->fap.funcindex_clientid); } +static inline bool hidpp_report_is_connect_event(struct hidpp_report *report) +{ + return (report->report_id == REPORT_ID_HIDPP_SHORT) && + (report->rap.sub_id == 0x41); +} + /* -------------------------------------------------------------------------- */ /* HIDP++ 1.0 commands */ /* -------------------------------------------------------------------------- */ @@ -535,12 +558,10 @@ static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi, return -1; } -static void wtp_input_configured(struct hid_device *hdev, - struct hid_input *hidinput) +static void wtp_populate_input(struct hidpp_device *hidpp, + struct input_dev *input_dev, bool origin_is_hid_core) { - struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct wtp_data *wd = hidpp->private_data; - struct input_dev *input_dev = hidinput->input; __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); @@ -716,13 +737,20 @@ static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } +static void hidpp_populate_input(struct hidpp_device *hidpp, + struct input_dev *input, bool origin_is_hid_core) +{ + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) + wtp_populate_input(hidpp, input, origin_is_hid_core); +} + static void hidpp_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); + struct input_dev *input = hidinput->input; - if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) - wtp_input_configured(hdev, hidinput); + hidpp_populate_input(hidpp, input, true); } static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, @@ -756,6 +784,15 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, } } + if (unlikely(hidpp_report_is_connect_event(report))) { + atomic_set(&hidpp->connected, + !(report->rap.params[0] & (1 << 6))); + if ((hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) && + (schedule_work(&hidpp->work) == 0)) + dbg_hid("%s: connect event already queued\n", __func__); + return 1; + } + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) return wtp_raw_event(hidpp->hid_dev, data, size); @@ -814,11 +851,99 @@ static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying) kfree(name); } +static int hidpp_input_open(struct input_dev *dev) +{ + struct hid_device *hid = input_get_drvdata(dev); + + return hid_hw_open(hid); +} + +static void hidpp_input_close(struct input_dev *dev) +{ + struct hid_device *hid = input_get_drvdata(dev); + + hid_hw_close(hid); +} + +static struct input_dev *hidpp_allocate_input(struct hid_device *hdev) +{ + struct input_dev *input_dev = devm_input_allocate_device(&hdev->dev); + + if (!input_dev) + return NULL; + + input_set_drvdata(input_dev, hdev); + input_dev->open = hidpp_input_open; + input_dev->close = hidpp_input_close; + + input_dev->name = hdev->name; + input_dev->phys = hdev->phys; + input_dev->uniq = hdev->uniq; + input_dev->id.bustype = hdev->bus; + input_dev->id.vendor = hdev->vendor; + input_dev->id.product = hdev->product; + input_dev->id.version = hdev->version; + input_dev->dev.parent = &hdev->dev; + + return input_dev; +} + +static void hidpp_connect_event(struct hidpp_device *hidpp) +{ + struct hid_device *hdev = hidpp->hid_dev; + int ret = 0; + bool connected = atomic_read(&hidpp->connected); + struct input_dev *input; + char *name, *devm_name; + u8 name_length; + + if (!connected || hidpp->delayed_input) + return; + + if (!hidpp->protocol_major) { + ret = !hidpp_is_connected(hidpp); + if (ret) { + hid_err(hdev, "Can not get the protocol version.\n"); + return; + } + } + + /* the device is already connected, we can ask for its name and + * protocol */ + hid_info(hdev, "HID++ %u.%u device connected.\n", + hidpp->protocol_major, hidpp->protocol_minor); + + input = hidpp_allocate_input(hdev); + if (!input) { + hid_err(hdev, "cannot allocate new input device: %d\n", ret); + return; + } + + name = hidpp_get_device_name(hidpp, &name_length); + if (!name) { + hid_err(hdev, "unable to retrieve the name of the device"); + } else { + devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name); + if (devm_name) + input->name = devm_name; + kfree(name); + } + + hidpp_populate_input(hidpp, input, false); + + ret = input_register_device(input); + if (ret) + input_free_device(input); + + hidpp->delayed_input = input; +} + static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) { struct hidpp_device *hidpp; int ret; bool connected; + unsigned int connect_mask = HID_CONNECT_DEFAULT; hidpp = devm_kzalloc(&hdev->dev, sizeof(struct hidpp_device), GFP_KERNEL); @@ -836,6 +961,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } + INIT_WORK(&hidpp->work, delayed_work_cb); mutex_init(&hidpp->send_mutex); init_waitqueue_head(&hidpp->wait); @@ -860,8 +986,9 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) } hidpp_overwrite_name(hdev, id->group == HID_GROUP_LOGITECH_DJ_DEVICE); + atomic_set(&hidpp->connected, connected); - if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { + if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) { ret = wtp_get_config(hidpp); if (ret) goto hid_parse_fail; @@ -870,16 +997,27 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) /* Block incoming packets */ hid_device_io_stop(hdev); - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) + connect_mask &= ~HID_CONNECT_HIDINPUT; + + ret = hid_hw_start(hdev, connect_mask); if (ret) { hid_err(hdev, "%s:hid_hw_start returned error\n", __func__); goto hid_hw_start_fail; } + if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) { + /* Allow incoming packets */ + hid_device_io_start(hdev); + + hidpp_connect_event(hidpp); + } + return ret; hid_hw_start_fail: hid_parse_fail: + cancel_work_sync(&hidpp->work); mutex_destroy(&hidpp->send_mutex); hid_set_drvdata(hdev, NULL); return ret; @@ -889,6 +1027,7 @@ static void hidpp_remove(struct hid_device *hdev) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); + cancel_work_sync(&hidpp->work); mutex_destroy(&hidpp->send_mutex); hid_hw_stop(hdev); } -- cgit v1.2.3 From 586bdc4e4f15fec37dc37167d6f314ea94aa14d1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:33 -0400 Subject: HID: logitech-hidpp: Add Wireless Touchpad T650 support All the bits are now in place to add the support of the Touchpad T650. The creation/population of the input device is delayed until the device is ready. The T650 uses the special HID++ reporting protocol, so activate this on connect. Signed-off-by: Benjamin Tissoires Tested-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 105 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 9561a1f9b42b..f9a4ec0ff76d 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -471,6 +471,9 @@ out_err: #define HIDPP_PAGE_TOUCHPAD_RAW_XY 0x6100 #define CMD_TOUCHPAD_GET_RAW_INFO 0x01 +#define CMD_TOUCHPAD_SET_RAW_REPORT_STATE 0x21 + +#define EVENT_TOUCHPAD_RAW_XY 0x00 #define TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT 0x01 #define TOUCHPAD_RAW_XY_ORIGIN_UPPER_LEFT 0x03 @@ -530,6 +533,59 @@ static int hidpp_touchpad_get_raw_info(struct hidpp_device *hidpp, return ret; } +static int hidpp_touchpad_set_raw_report_state(struct hidpp_device *hidpp_dev, + u8 feature_index, bool send_raw_reports, + bool sensor_enhanced_settings) +{ + struct hidpp_report response; + + /* + * Params: + * bit 0 - enable raw + * bit 1 - 16bit Z, no area + * bit 2 - enhanced sensitivity + * bit 3 - width, height (4 bits each) instead of area + * bit 4 - send raw + gestures (degrades smoothness) + * remaining bits - reserved + */ + u8 params = send_raw_reports | (sensor_enhanced_settings << 2); + + return hidpp_send_fap_command_sync(hidpp_dev, feature_index, + CMD_TOUCHPAD_SET_RAW_REPORT_STATE, ¶ms, 1, &response); +} + +static void hidpp_touchpad_touch_event(u8 *data, + struct hidpp_touchpad_raw_xy_finger *finger) +{ + u8 x_m = data[0] << 2; + u8 y_m = data[2] << 2; + + finger->x = x_m << 6 | data[1]; + finger->y = y_m << 6 | data[3]; + + finger->contact_type = data[0] >> 6; + finger->contact_status = data[2] >> 6; + + finger->z = data[4]; + finger->area = data[5]; + finger->finger_id = data[6] >> 4; +} + +static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev, + u8 *data, struct hidpp_touchpad_raw_xy *raw_xy) +{ + memset(raw_xy, 0, sizeof(struct hidpp_touchpad_raw_xy)); + raw_xy->end_of_frame = data[8] & 0x01; + raw_xy->spurious_flag = (data[8] >> 1) & 0x01; + raw_xy->finger_count = data[15] & 0x0f; + raw_xy->button = (data[8] >> 2) & 0x01; + + if (raw_xy->finger_count) { + hidpp_touchpad_touch_event(&data[2], &raw_xy->fingers[0]); + hidpp_touchpad_touch_event(&data[9], &raw_xy->fingers[1]); + } +} + /* ************************************************************************** */ /* */ /* Device Support */ @@ -672,11 +728,28 @@ static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct wtp_data *wd = hidpp->private_data; + struct hidpp_report *report = (struct hidpp_report *)data; + struct hidpp_touchpad_raw_xy raw; - if (!wd || !wd->input || (data[0] != 0x02) || size < 21) + if (!wd || !wd->input) return 1; - return wtp_mouse_raw_xy_event(hidpp, &data[7]); + switch (data[0]) { + case 0x02: + if (size < 21) + return 1; + return wtp_mouse_raw_xy_event(hidpp, &data[7]); + case REPORT_ID_HIDPP_LONG: + if ((report->fap.feature_index != wd->mt_feature_index) || + (report->fap.funcindex_clientid != EVENT_TOUCHPAD_RAW_XY)) + return 1; + hidpp_touchpad_raw_xy_event(hidpp, data + 4, &raw); + + wtp_send_raw_xy_event(hidpp, &raw); + return 0; + } + + return 0; } static int wtp_get_config(struct hidpp_device *hidpp) @@ -721,6 +794,27 @@ static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id) return 0; }; +static void wtp_connect(struct hid_device *hdev, bool connected) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + struct wtp_data *wd = hidpp->private_data; + int ret; + + if (!connected) + return; + + if (!wd->x_size) { + ret = wtp_get_config(hidpp); + if (ret) { + hid_err(hdev, "Can not get wtp config: %d\n", ret); + return; + } + } + + hidpp_touchpad_set_raw_report_state(hidpp, wd->mt_feature_index, + true, true); +} + /* -------------------------------------------------------------------------- */ /* Generic HID++ devices */ /* -------------------------------------------------------------------------- */ @@ -897,6 +991,9 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) char *name, *devm_name; u8 name_length; + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) + wtp_connect(hdev, connected); + if (!connected || hidpp->delayed_input) return; @@ -1033,6 +1130,10 @@ static void hidpp_remove(struct hid_device *hdev) } static const struct hid_device_id hidpp_devices[] = { + { /* wireless touchpad T650 */ + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, 0x4101), + .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT }, { /* wireless touchpad T651 */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651), -- cgit v1.2.3 From 57ac86cf52e903d9e3e0f12b34c814cce6b65550 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:34 -0400 Subject: HID: logitech-hidpp: add support of the first Logitech Wireless Touchpad This touchpad differs from the T650 in several ways: - the resolution is not correctly returned by the device - it presents physical buttons, so the button flag in the raw touch report is not filled. Signed-off-by: Benjamin Tissoires Tested-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index f9a4ec0ff76d..17e27e98e442 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -38,6 +38,7 @@ MODULE_AUTHOR("Nestor Lopez Casado "); /* bits 1..20 are reserved for classes */ #define HIDPP_QUIRK_DELAYED_INIT BIT(21) +#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22) /* * There are two hidpp protocols in use, the first version hidpp10 is known @@ -596,6 +597,8 @@ static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev, /* Touchpad HID++ devices */ /* -------------------------------------------------------------------------- */ +#define WTP_MANUAL_RESOLUTION 39 + struct wtp_data { struct input_dev *input; u16 x_size, y_size; @@ -634,7 +637,10 @@ static void wtp_populate_input(struct hidpp_device *hidpp, input_set_capability(input_dev, EV_KEY, BTN_LEFT); - __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS) + input_set_capability(input_dev, EV_KEY, BTN_RIGHT); + else + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); input_mt_init_slots(input_dev, wd->maxcontacts, INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED); @@ -676,7 +682,8 @@ static void wtp_send_raw_xy_event(struct hidpp_device *hidpp, for (i = 0; i < 2; i++) wtp_touch_event(wd, &(raw->fingers[i])); - if (raw->end_of_frame) + if (raw->end_of_frame && + !(hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS)) input_event(wd->input, EV_KEY, BTN_LEFT, raw->button); if (raw->end_of_frame || raw->finger_count <= 2) { @@ -736,9 +743,17 @@ static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size) switch (data[0]) { case 0x02: - if (size < 21) - return 1; - return wtp_mouse_raw_xy_event(hidpp, &data[7]); + if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS) { + input_event(wd->input, EV_KEY, BTN_LEFT, + !!(data[1] & 0x01)); + input_event(wd->input, EV_KEY, BTN_RIGHT, + !!(data[1] & 0x02)); + input_sync(wd->input); + } else { + if (size < 21) + return 1; + return wtp_mouse_raw_xy_event(hidpp, &data[7]); + } case REPORT_ID_HIDPP_LONG: if ((report->fap.feature_index != wd->mt_feature_index) || (report->fap.funcindex_clientid != EVENT_TOUCHPAD_RAW_XY)) @@ -775,6 +790,8 @@ static int wtp_get_config(struct hidpp_device *hidpp) wd->maxcontacts = raw_info.maxcontacts; wd->flip_y = raw_info.origin == TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT; wd->resolution = raw_info.res; + if (!wd->resolution) + wd->resolution = WTP_MANUAL_RESOLUTION; return 0; } @@ -1130,6 +1147,11 @@ static void hidpp_remove(struct hid_device *hdev) } static const struct hid_device_id hidpp_devices[] = { + { /* wireless touchpad */ + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, 0x4011), + .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT | + HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS }, { /* wireless touchpad T650 */ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, USB_VENDOR_ID_LOGITECH, 0x4101), -- cgit v1.2.3 From 3a61e97563d78a2ca10752902449570d8433ce76 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 13:18:35 -0400 Subject: HID: logitech-hidpp: support combo keyboard touchpad TK820 The TK820 presents both a keyboard and a touchpad on the same physical (and logical device). Use the generic hid-input processing for the keyboard part. The keyboard input device is created when the receiver is plugged in, so no events are missed on connect. When the device actaully connects, we can set it to use the raw multitouch reporting to have a consistent user experience accross all Logitech touchpads. Signed-off-by: Benjamin Tissoires Tested-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 17e27e98e442..361e97da1169 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -39,6 +39,7 @@ MODULE_AUTHOR("Nestor Lopez Casado "); /* bits 1..20 are reserved for classes */ #define HIDPP_QUIRK_DELAYED_INIT BIT(21) #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22) +#define HIDPP_QUIRK_MULTI_INPUT BIT(23) /* * There are two hidpp protocols in use, the first version hidpp10 is known @@ -614,6 +615,12 @@ static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + + if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) && + (field->application == HID_GD_KEYBOARD)) + return 0; + return -1; } @@ -622,6 +629,10 @@ static void wtp_populate_input(struct hidpp_device *hidpp, { struct wtp_data *wd = hidpp->private_data; + if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) && origin_is_hid_core) + /* this is the generic hid-input call */ + return; + __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); __clear_bit(EV_REL, input_dev->evbit); @@ -1114,6 +1125,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) connect_mask &= ~HID_CONNECT_HIDINPUT; + /* Re-enable hidinput for multi-input devices */ + if (hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) + connect_mask |= HID_CONNECT_HIDINPUT; + ret = hid_hw_start(hdev, connect_mask); if (ret) { hid_err(hdev, "%s:hid_hw_start returned error\n", __func__); @@ -1160,6 +1175,11 @@ static const struct hid_device_id hidpp_devices[] = { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651), .driver_data = HIDPP_QUIRK_CLASS_WTP }, + { /* Keyboard TK820 */ + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, 0x4102), + .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_MULTI_INPUT | + HIDPP_QUIRK_CLASS_WTP }, { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, -- cgit v1.2.3 From b905811a49bcd6e6726ce5bbb591f57aaddfd3be Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 30 Sep 2014 14:28:22 -0400 Subject: HID: usbhid: prevent unwanted events to be sent when re-opening the device When events occurs while no one is listening to the node (hid->open == 0 and usb_kill_urb() called) some events are still stacked somewhere in the USB (kernel or device?) stack. When the node gets reopened, these events are drained, and this results in spurious touch down/up, or mouse button clicks. The problem was spotted with touchscreens in fdo bug #81781 [1], but it actually occurs with any mouse using hid-generic or touchscreen. A way to reproduce it is to call: $ xinput disable 9 ; sleep 5 ; xinput enable 9 With 9 being the device ID for the touchscreen/mouse. During the "sleep", produce some touch events or click events. When "xinput enable" is called, at least one click is generated. This patch tries to fix this by draining the queue for 50 msec and during this time frame, not forwarding these old events to the hid layer. Hans completed the explanation: """ Devices like mice (basically any hid device) will have a fifo on the device side, when we stop submitting urbs to get hid reports from it, that fifo will fill up, and when we resume we will get whatever is there in that fifo. """ [1] https://bugs.freedesktop.org/show_bug.cgi?id=81781 Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 36 ++++++++++++++++++++++++------------ drivers/hid/usbhid/usbhid.h | 1 + 2 files changed, 25 insertions(+), 12 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index ca6849a0121e..04e34b917045 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -278,18 +278,20 @@ static void hid_irq_in(struct urb *urb) usbhid->retry_delay = 0; if ((hid->quirks & HID_QUIRK_ALWAYS_POLL) && !hid->open) break; - hid_input_report(urb->context, HID_INPUT_REPORT, - urb->transfer_buffer, - urb->actual_length, 1); - /* - * autosuspend refused while keys are pressed - * because most keyboards don't wake up when - * a key is released - */ - if (hid_check_keys_pressed(hid)) - set_bit(HID_KEYS_PRESSED, &usbhid->iofl); - else - clear_bit(HID_KEYS_PRESSED, &usbhid->iofl); + if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) { + hid_input_report(urb->context, HID_INPUT_REPORT, + urb->transfer_buffer, + urb->actual_length, 1); + /* + * autosuspend refused while keys are pressed + * because most keyboards don't wake up when + * a key is released + */ + if (hid_check_keys_pressed(hid)) + set_bit(HID_KEYS_PRESSED, &usbhid->iofl); + else + clear_bit(HID_KEYS_PRESSED, &usbhid->iofl); + } break; case -EPIPE: /* stall */ usbhid_mark_busy(usbhid); @@ -688,6 +690,7 @@ int usbhid_open(struct hid_device *hid) goto done; } usbhid->intf->needs_remote_wakeup = 1; + set_bit(HID_RESUME_RUNNING, &usbhid->iofl); res = hid_start_in(hid); if (res) { if (res != -ENOSPC) { @@ -701,6 +704,15 @@ int usbhid_open(struct hid_device *hid) } } usb_autopm_put_interface(usbhid->intf); + + /* + * In case events are generated while nobody was listening, + * some are released when the device is re-opened. + * Wait 50 msec for the queue to empty before allowing events + * to go through hid. + */ + msleep(50); + clear_bit(HID_RESUME_RUNNING, &usbhid->iofl); } done: mutex_unlock(&hid_open_mut); diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index f633c24ce28b..807922b49aa4 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -52,6 +52,7 @@ struct usb_interface *usbhid_find_interface(int minor); #define HID_STARTED 8 #define HID_KEYS_PRESSED 10 #define HID_NO_BANDWIDTH 11 +#define HID_RESUME_RUNNING 12 /* * USB-specific HID struct, to be pointed to -- cgit v1.2.3 From 6354b7e25b7617f71a2b544be82c399013b13917 Mon Sep 17 00:00:00 2001 From: Hans Duedal Date: Sat, 11 Oct 2014 17:01:49 +0200 Subject: HID: roccat: Fix code style issues Missing whitespace, semi-colon after macro, and a duplicated out of memory message. Signed-off-by: Hans Duedal Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat-kone.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 6101816a7ddd..c29265055ac1 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -46,6 +46,7 @@ static void kone_profile_activated(struct kone_device *kone, uint new_profile) static void kone_profile_report(struct kone_device *kone, uint new_profile) { struct kone_roccat_report roccat_report; + roccat_report.event = kone_mouse_event_switch_profile; roccat_report.value = new_profile; roccat_report.key = 0; @@ -163,6 +164,7 @@ static int kone_set_settings(struct usb_device *usb_dev, struct kone_settings const *settings) { int retval; + retval = kone_send(usb_dev, kone_command_settings, settings, sizeof(struct kone_settings)); if (retval) @@ -387,7 +389,7 @@ static struct bin_attribute bin_attr_profile##number = { \ .read = kone_sysfs_read_profilex, \ .write = kone_sysfs_write_profilex, \ .private = &profile_numbers[number-1], \ -}; +} PROFILE_ATTR(1); PROFILE_ATTR(2); PROFILE_ATTR(3); @@ -456,6 +458,7 @@ static ssize_t kone_sysfs_show_tcu(struct device *dev, static int kone_tcu_command(struct usb_device *usb_dev, int number) { unsigned char value; + value = number; return kone_send(usb_dev, kone_command_calibrate, &value, 1); } @@ -697,10 +700,8 @@ static int kone_init_specials(struct hid_device *hdev) == USB_INTERFACE_PROTOCOL_MOUSE) { kone = kzalloc(sizeof(*kone), GFP_KERNEL); - if (!kone) { - hid_err(hdev, "can't alloc device descriptor\n"); + if (!kone) return -ENOMEM; - } hid_set_drvdata(hdev, kone); retval = kone_init_kone_device_struct(usb_dev, kone); -- cgit v1.2.3 From 1a3f83f6493f9d78aa0fe31401fd530b0fe296da Mon Sep 17 00:00:00 2001 From: JD Cole Date: Fri, 31 Oct 2014 17:34:42 -0700 Subject: HID: plantronics: fix errant mouse events This version of the driver prevents Telephony pages which are not mapped as Consumer Control applications AND are not on the Consumer Page from being registered by the hid-input driver. Signed-off-by: JD Cole Reviewed-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 ++++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 2 ++ drivers/hid/hid-plantronics.c | 78 +++++++++++++++++++++++++++++++++++++++++++ include/linux/hid.h | 3 ++ 6 files changed, 92 insertions(+) create mode 100644 drivers/hid/hid-plantronics.c (limited to 'drivers/hid') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index f42df4dd58d2..bf1c74e19c75 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -613,6 +613,13 @@ config HID_PICOLCD_CIR ---help--- Provide access to PicoLCD's CIR interface via remote control (LIRC). +config HID_PLANTRONICS + tristate "Plantronics USB HID Driver" + default !EXPERT + depends on HID + ---help--- + Provides HID support for Plantronics telephony devices. + config HID_PRIMAX tristate "Primax non-fully HID-compliant devices" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index e2850d8af9ca..5e7ac59447d8 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -94,6 +94,7 @@ ifdef CONFIG_DEBUG_FS hid-picolcd-y += hid-picolcd_debugfs.o endif +obj-$(CONFIG_HID_PLANTRONICS) += hid-plantronics.o obj-$(CONFIG_HID_PRIMAX) += hid-primax.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 73bd9e2e42bc..d50313ca64a6 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1886,6 +1886,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_6000) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) }, { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, #if IS_ENABLED(CONFIG_HID_ROCCAT) { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e23ab8b30626..f9f476db671c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -715,6 +715,8 @@ #define USB_DEVICE_ID_ORTEK_PKB1700 0x1700 #define USB_DEVICE_ID_ORTEK_WKB2000 0x2000 +#define USB_VENDOR_ID_PLANTRONICS 0x047f + #define USB_VENDOR_ID_PANASONIC 0x04da #define USB_DEVICE_ID_PANABOARD_UBT780 0x1044 #define USB_DEVICE_ID_PANABOARD_UBT880 0x104d diff --git a/drivers/hid/hid-plantronics.c b/drivers/hid/hid-plantronics.c new file mode 100644 index 000000000000..215cdbd10dea --- /dev/null +++ b/drivers/hid/hid-plantronics.c @@ -0,0 +1,78 @@ +/* + * Plantronics USB HID Driver + * + * Copyright (c) 2014 JD Cole + * Copyright (c) 2014 Terry Junge + */ + +/* + * 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 "hid-ids.h" + +#include +#include + +static int plantronics_input_mapping(struct hid_device *hdev, + struct hid_input *hi, + struct hid_field *field, + struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if (field->application == HID_CP_CONSUMERCONTROL + && (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) { + hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n", + usage->hid, field->application); + return 0; + } + + hid_dbg(hdev, "usage: %08x (appl: %08x) - ignored\n", + usage->hid, field->application); + + return -1; +} + +static int plantronics_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; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + goto err; + } + + return 0; + err: + return ret; +} + +static const struct hid_device_id plantronics_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) }, + { } +}; +MODULE_DEVICE_TABLE(hid, plantronics_devices); + +static struct hid_driver plantronics_driver = { + .name = "plantronics", + .id_table = plantronics_devices, + .input_mapping = plantronics_input_mapping, + .probe = plantronics_probe, +}; +module_hid_driver(plantronics_driver); + +MODULE_AUTHOR("JD Cole "); +MODULE_AUTHOR("Terry Junge "); +MODULE_DESCRIPTION("Plantronics USB HID Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/hid.h b/include/linux/hid.h index 78ea9bf941cd..a63f2aaed646 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -234,6 +234,9 @@ struct hid_item { #define HID_DG_BARRELSWITCH 0x000d0044 #define HID_DG_ERASER 0x000d0045 #define HID_DG_TABLETPICK 0x000d0046 + +#define HID_CP_CONSUMERCONTROL 0x000c0001 + #define HID_DG_CONFIDENCE 0x000d0047 #define HID_DG_WIDTH 0x000d0048 #define HID_DG_HEIGHT 0x000d0049 -- cgit v1.2.3 From 1e48d542e751f766d0ff03aa97bef22d90a98041 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 3 Nov 2014 14:19:14 +0100 Subject: HID: plantronics: remove superfluous .probe() Remove redundanct probe() callback, as it isn't doing anything extra that HID core wouldn't do by default. Reported-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-plantronics.c | 23 ----------------------- 1 file changed, 23 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-plantronics.c b/drivers/hid/hid-plantronics.c index 215cdbd10dea..2180e0789b76 100644 --- a/drivers/hid/hid-plantronics.c +++ b/drivers/hid/hid-plantronics.c @@ -36,28 +36,6 @@ static int plantronics_input_mapping(struct hid_device *hdev, return -1; } -static int plantronics_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; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) { - hid_err(hdev, "hw start failed\n"); - goto err; - } - - return 0; - err: - return ret; -} - static const struct hid_device_id plantronics_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) }, { } @@ -68,7 +46,6 @@ static struct hid_driver plantronics_driver = { .name = "plantronics", .id_table = plantronics_devices, .input_mapping = plantronics_input_mapping, - .probe = plantronics_probe, }; module_hid_driver(plantronics_driver); -- cgit v1.2.3 From 3e7830ceb94cd06c05832a0d53cf324db3792418 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 31 Oct 2014 12:14:39 +0300 Subject: HID: logitech-hidpp: leaks and NULL dereferences Shift the allocation down a few lines to avoid a memory leak and also add a check for allocation failure. Fixes: 2f31c5252910 ('HID: Introduce hidpp, a module to handle Logitech hid++ devices') Signed-off-by: Dan Carpenter Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 361e97da1169..8d2d54b527b0 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -200,13 +200,15 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp, u8 feat_index, u8 funcindex_clientid, u8 *params, int param_count, struct hidpp_report *response) { - struct hidpp_report *message = kzalloc(sizeof(struct hidpp_report), - GFP_KERNEL); + struct hidpp_report *message; int ret; if (param_count > sizeof(message->fap.params)) return -EINVAL; + message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL); + if (!message) + return -ENOMEM; message->report_id = REPORT_ID_HIDPP_LONG; message->fap.feature_index = feat_index; message->fap.funcindex_clientid = funcindex_clientid; @@ -221,8 +223,7 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev, u8 report_id, u8 sub_id, u8 reg_address, u8 *params, int param_count, struct hidpp_report *response) { - struct hidpp_report *message = kzalloc(sizeof(struct hidpp_report), - GFP_KERNEL); + struct hidpp_report *message; int ret; if ((report_id != REPORT_ID_HIDPP_SHORT) && @@ -232,6 +233,9 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev, if (param_count > sizeof(message->rap.params)) return -EINVAL; + message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL); + if (!message) + return -ENOMEM; message->report_id = report_id; message->rap.sub_id = sub_id; message->rap.reg_address = reg_address; -- cgit v1.2.3 From 8c9952b26b2be25311706082598e5e176eb92748 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 3 Nov 2014 16:09:58 -0500 Subject: HID: logitech-hidpp: fix negated returns Reported by Dan Carpenter: drivers/hid/hid-logitech-hidpp.c:359 hidpp_root_get_protocol_version() warn: should this return really be negated? drivers/hid/hid-logitech-hidpp.c:398 hidpp_devicenametype_get_count() warn: should this return really be negated? drivers/hid/hid-logitech-hidpp.c:417 hidpp_devicenametype_get_device_name() warn: should this return really be negated? drivers/hid/hid-logitech-hidpp.c:524 hidpp_touchpad_get_raw_info() warn: should this return really be negated? The problem lies in hidpp_send_message_sync() which can return 2 types of errors depending of their sign. Adding a comment there to clarify what is happening. To solve that, print an error in case of a protocol problem, and raise -EPROTO instead. Reported-by: Dan Carpenter Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 8d2d54b527b0..61f9e75c45d2 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -151,6 +151,14 @@ static int __hidpp_send_report(struct hid_device *hdev, return ret == fields_count ? 0 : -1; } +/** + * hidpp_send_message_sync() returns 0 in case of success, and something else + * in case of a failure. + * - If ' something else' is positive, that means that an error has been raised + * by the protocol itself. + * - If ' something else' is negative, that means that we had a classic error + * (-ENOMEM, -EPIPE, etc...) + */ static int hidpp_send_message_sync(struct hidpp_device *hidpp, struct hidpp_report *message, struct hidpp_report *response) @@ -359,8 +367,13 @@ static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp) return 0; } + if (ret > 0) { + hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", + __func__, ret); + return -EPROTO; + } if (ret) - return -ret; + return ret; hidpp->protocol_major = response.fap.params[0]; hidpp->protocol_minor = response.fap.params[1]; @@ -398,8 +411,13 @@ static int hidpp_devicenametype_get_count(struct hidpp_device *hidpp, ret = hidpp_send_fap_command_sync(hidpp, feature_index, CMD_GET_DEVICE_NAME_TYPE_GET_COUNT, NULL, 0, &response); + if (ret > 0) { + hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", + __func__, ret); + return -EPROTO; + } if (ret) - return -ret; + return ret; *nameLength = response.fap.params[0]; @@ -417,8 +435,13 @@ static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp, CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME, &char_index, 1, &response); + if (ret > 0) { + hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", + __func__, ret); + return -EPROTO; + } if (ret) - return -ret; + return ret; if (response.report_id == REPORT_ID_HIDPP_LONG) count = HIDPP_REPORT_LONG_LENGTH - 4; @@ -524,8 +547,13 @@ static int hidpp_touchpad_get_raw_info(struct hidpp_device *hidpp, ret = hidpp_send_fap_command_sync(hidpp, feature_index, CMD_TOUCHPAD_GET_RAW_INFO, NULL, 0, &response); + if (ret > 0) { + hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", + __func__, ret); + return -EPROTO; + } if (ret) - return -ret; + return ret; raw_info->x_size = get_unaligned_be16(¶ms[0]); raw_info->y_size = get_unaligned_be16(¶ms[2]); -- cgit v1.2.3 From 552f12eb68440a0328dc9929066f6d0321a75874 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 3 Nov 2014 16:09:59 -0500 Subject: HID: logitech-hidpp: 2 fixes in hidpp_root_get_protocol_version() - remove the constant '1' - when the device is not connected, the protocol error HIDPP_ERROR_RESOURCE_ERROR is raised. We should not warn the user about it because it is somewhat expected as an answer when we check if the device is connected. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 61f9e75c45d2..1a6395d0c2bd 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -361,12 +361,16 @@ static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp) CMD_ROOT_GET_PROTOCOL_VERSION, NULL, 0, &response); - if (ret == 1) { + if (ret == HIDPP_ERROR_INVALID_SUBID) { hidpp->protocol_major = 1; hidpp->protocol_minor = 0; return 0; } + /* the device might not be connected */ + if (ret == HIDPP_ERROR_RESOURCE_ERROR) + return -EIO; + if (ret > 0) { hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", __func__, ret); -- cgit v1.2.3 From 7bb9d643651180f081cfcc1ed43ff685eda8a358 Mon Sep 17 00:00:00 2001 From: Ville Aakko Date: Wed, 5 Nov 2014 15:13:51 +0100 Subject: HID: saitek: quirk for Saitek R.A.T.7 works with R.A.T.9 too I have tested HID quirk for Saitek R.A.T.7 with my R.A.T. 9. It works fine for me. Attached patch makes the necessary changes to use the quirk on the R.A.T.9 too, and necessary Kconfig changes too. I have stylized the Kconfig option name to include Mad Catz in the option name, as (at least some of) the devices are marketed under Mad Catz brand. Signed-off-by: Ville Aakko Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 3 ++- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-saitek.c | 4 +++- 4 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index f42df4dd58d2..3a3f29c0cc36 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -629,7 +629,7 @@ config HID_ROCCAT support for its special functionalities. config HID_SAITEK - tristate "Saitek non-fully HID-compliant devices" + tristate "Saitek (Mad Catz) non-fully HID-compliant devices" depends on HID ---help--- Support for Saitek devices that are not fully compliant with the @@ -637,6 +637,7 @@ config HID_SAITEK Supported devices: - PS1000 Dual Analog Pad + - R.A.T.9 Gaming Mouse - R.A.T.7 Gaming Mouse - M.M.O.7 Gaming Mouse diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 946540e1764d..1af93ca42226 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1906,6 +1906,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9) }, #endif { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index cd9c9e96cf0e..08cf4e75c3bb 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -617,6 +617,7 @@ #define USB_VENDOR_ID_MADCATZ 0x0738 #define USB_DEVICE_ID_MADCATZ_BEATPAD 0x4540 +#define USB_DEVICE_ID_MADCATZ_RAT9 0x1709 #define USB_VENDOR_ID_MCC 0x09db #define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c index 69cca1476a0c..5632c54eadf0 100644 --- a/drivers/hid/hid-saitek.c +++ b/drivers/hid/hid-saitek.c @@ -7,7 +7,7 @@ * (This module is based on "hid-ortek".) * Copyright (c) 2012 Andreas Hübner * - * R.A.T.7, M.M.O.7 (USB gaming mice): + * R.A.T.7, R.A.T.9, M.M.O.7 (USB gaming mice): * Fixes the mode button which cycles through three constantly pressed * buttons. All three press events are mapped to one button and the * missing release event is generated immediately. @@ -179,6 +179,8 @@ static const struct hid_device_id saitek_devices[] = { .driver_data = SAITEK_FIX_PS1000 }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7), .driver_data = SAITEK_RELEASE_MODE_RAT7 }, + { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9), + .driver_data = SAITEK_RELEASE_MODE_RAT7 }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7), .driver_data = SAITEK_RELEASE_MODE_MMO7 }, { } -- cgit v1.2.3 From 44b9683853c7b47d039366d7d2fd0d5380aba276 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Thu, 6 Nov 2014 17:30:51 -0800 Subject: HID: wacom - make sure touch_input is valid before using it touch_input is stored in wacom_shared for pen data to report touch switch status. It is possible, although we didn't see it happen on Linux yet, that pen data is procesed before touch interface is fully probed. As a by-product of this patch, it fixes the FreeBSD issue reported by Denis Akiyakov http://www.spinics.net/lists/linux-input/msg33971.html Reviewed-by: Hans Petter Selasky Tested-by: Denis Akiyakov Signed-off-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 586b2405b0d4..d343a6ee5bf7 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1681,7 +1681,9 @@ static int wacom_bpt_pen(struct wacom_wac *wacom) return 0; if (data[0] == WACOM_REPORT_USB) { - if (features->type == INTUOSHT && features->touch_max) { + if (features->type == INTUOSHT && + wacom->shared->touch_input && + features->touch_max) { input_report_switch(wacom->shared->touch_input, SW_MUTE_DEVICE, data[8] & 0x40); input_sync(wacom->shared->touch_input); @@ -1774,7 +1776,8 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) int pid, battery, ps_connected; if ((wacom->shared->type == INTUOSHT) && - wacom->shared->touch_max) { + wacom->shared->touch_input && + wacom->shared->touch_max) { input_report_switch(wacom->shared->touch_input, SW_MUTE_DEVICE, data[5] & 0x40); input_sync(wacom->shared->touch_input); -- cgit v1.2.3 From be3b16341d5cd8cf2a64fcc7a604a8efe6599ff0 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Mon, 3 Nov 2014 18:26:12 -0800 Subject: HID: add support for MS Surface Pro 3 Type Cover Surface Pro 3 Type Cover that works with Ubuntu (and possibly Arch) from this thread. Both trackpad and keyboard work after compiling my own kernel. http://ubuntuforums.org/showthread.php?t=2231207&page=2&s=44910e0c56047e4f93dfd9fea58121ef Also includes Jarrad Whitaker's message which sources http://winaero.com/blog/how-to-install-linux-on-surface-pro-3/ which he says is sourced from a Russian site Signed-off-by: Alan Wu Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 6 ++++++ drivers/hid/hid-ids.h | 1 + drivers/hid/hid-microsoft.c | 2 ++ drivers/hid/usbhid/hid-quirks.c | 1 + 4 files changed, 10 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 73bd9e2e42bc..e94afcc19eda 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -702,6 +702,11 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) if (((parser->global.usage_page << 16) == HID_UP_SENSOR) && type == HID_COLLECTION_PHYSICAL) hid->group = HID_GROUP_SENSOR_HUB; + + if (hid->vendor == USB_VENDOR_ID_MICROSOFT && + hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 && + hid->group == HID_GROUP_MULTITOUCH) + hid->group = HID_GROUP_GENERIC; } static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) @@ -1861,6 +1866,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index cd9c9e96cf0e..0d38df761e29 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -646,6 +646,7 @@ #define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 #define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 #define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 +#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07dc #define USB_VENDOR_ID_MOJO 0x8282 #define USB_DEVICE_ID_RETRO_ADAPTER 0x3201 diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 8ba17a946f2a..cacda43f6a6f 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -274,6 +274,8 @@ static const struct hid_device_id ms_devices[] = { .driver_data = MS_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500), .driver_data = MS_DUPLICATE_USAGES }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), + .driver_data = MS_HIDINPUT }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), .driver_data = MS_PRESENTER }, diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index f3cb5b0a4345..be6763524fe8 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -75,6 +75,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS }, -- cgit v1.2.3 From 5556eb14b4cbf5dce840194ab591027322cbdb1f Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Sun, 9 Nov 2014 07:54:29 +0000 Subject: HID: lenovo: Move USB KEY_FILE to 0x00f9 to prevent scancode clash The bluetooth keyboard also generates 0x00fa when the middle button is pressed. Move the made-up report out of the way so we don't trigger KEY_FILE when middle button is pressed Signed-off-by: Jamie Lentin Signed-off-by: Jiri Kosina --- drivers/hid/hid-lenovo.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index bf227f7679af..4716f1e59eca 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -85,13 +85,13 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev, 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 */ + case 0x00f9: /* 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; + case 0x00fa: /* Fn-Esc: Fn-lock toggle */ + map_key_clear(KEY_FN_ESC); + return 1; } } @@ -207,8 +207,8 @@ static int lenovo_raw_event(struct hid_device *hdev, && data[0] == 0x15 && data[1] == 0x94 && data[2] == 0x01)) { - data[1] = 0x0; - data[2] = 0x4; + data[1] = 0x00; + data[2] = 0x01; } return 0; -- cgit v1.2.3 From c87d92b30208826a656d3cd63e6c96c56cf516a4 Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Sun, 9 Nov 2014 07:54:30 +0000 Subject: HID: lenovo: Don't set EV_REP to avoid repeating mice buttons On the USB keyboard, the VENDOR hotkeys share the same device as the mouse. Setting EV_REP also affects mouse buttons, so leave it off. The bluetooth keyboard still has autorepeating mouse buttons, as it only has one device and is set by the KEYBOARD pages. Signed-off-by: Jamie Lentin Signed-off-by: Jiri Kosina --- drivers/hid/hid-lenovo.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index 4716f1e59eca..4c55f4d95798 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -62,7 +62,6 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev, /* 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); -- cgit v1.2.3 From fefb391f8c15900477432a29d9c3527dd66a8415 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Tue, 11 Nov 2014 12:52:08 -0800 Subject: HID: wacom: Add support for Intuos Pen Medium Signed-off-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index d343a6ee5bf7..104829524bcd 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2881,6 +2881,10 @@ static const struct wacom_features wacom_features_0x30C = { "Wacom ISDv5 30C", .type = WACOM_24HDT, /* Touch */ .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30A, .touch_max = 10, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x323 = + { "Wacom Intuos P M", 21600, 13500, 1023, 31, + INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_HID_ANY_ID = { "Wacom HID", .type = HID_GENERIC }; @@ -3025,6 +3029,7 @@ const struct hid_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x314) }, { USB_DEVICE_WACOM(0x315) }, { USB_DEVICE_WACOM(0x317) }, + { USB_DEVICE_WACOM(0x323) }, { USB_DEVICE_WACOM(0x4001) }, { USB_DEVICE_WACOM(0x4004) }, { USB_DEVICE_WACOM(0x5000) }, -- cgit v1.2.3 From 08bb7beae7fe102939fe5931222fc09e10d27cda Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Sun, 16 Nov 2014 22:45:43 +0800 Subject: HID: i2c-hid: print the correct data in dbg msg Report is received in "buffer"; fix the following i2c_hid_dbg() to dump data from the correct pointer. Signed-off-by: Jean-Baptiste Maneyrol [Antonio Borneo: cleanup and rebase to v3.17] Signed-off-by: Antonio Borneo Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 2 +- 1 file changed, 1 insertion(+), 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 747d54421e73..933bf1052ec0 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -437,7 +437,7 @@ static void i2c_hid_init_report(struct hid_report *report, u8 *buffer, report->id, buffer, size)) return; - i2c_hid_dbg(ihid, "report (len=%d): %*ph\n", size, size, ihid->inbuf); + i2c_hid_dbg(ihid, "report (len=%d): %*ph\n", size, size, buffer); ret_size = buffer[0] | (buffer[1] << 8); -- cgit v1.2.3 From 9b2b5c9a790743ccb2516f1a86671d5df9c6c5cd Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 12 Nov 2014 14:10:09 -0500 Subject: HID: sony: Use kernel allocated buffers for HID reports Replace stack buffers with kernel allocated buffers for sending and receiving HID reports to prevent issues with DMA transfers on certain hardware. Output report buffers are allocated at initialization time to avoid excessive calls to kmalloc and kfree. Link: https://bugzilla.kernel.org/show_bug.cgi?id=87991 Signed-off-by: Frank Praznik Reviewed-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 147 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 113 insertions(+), 34 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index bc4269e559f1..b6e610289e9d 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -798,6 +798,12 @@ union sixaxis_output_report_01 { __u8 buf[36]; }; +#define DS4_REPORT_0x02_SIZE 37 +#define DS4_REPORT_0x05_SIZE 32 +#define DS4_REPORT_0x11_SIZE 78 +#define DS4_REPORT_0x81_SIZE 7 +#define SIXAXIS_REPORT_0xF2_SIZE 18 + static spinlock_t sony_dev_list_lock; static LIST_HEAD(sony_device_list); static DEFINE_IDA(sony_device_id_allocator); @@ -811,6 +817,7 @@ struct sony_sc { struct work_struct state_worker; struct power_supply battery; int device_id; + __u8 *output_report_dmabuf; #ifdef CONFIG_SONY_FF __u8 left; @@ -1142,9 +1149,20 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) static int sixaxis_set_operational_bt(struct hid_device *hdev) { - unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 }; - return hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf), + static const __u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 }; + __u8 *buf; + int ret; + + buf = kmemdup(report, sizeof(report), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(report), HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + + kfree(buf); + + return ret; } /* @@ -1153,10 +1171,19 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev) */ static int dualshock4_set_operational_bt(struct hid_device *hdev) { - __u8 buf[37] = { 0 }; + __u8 *buf; + int ret; - return hid_hw_raw_request(hdev, 0x02, buf, sizeof(buf), + buf = kmalloc(DS4_REPORT_0x02_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hid_hw_raw_request(hdev, 0x02, buf, DS4_REPORT_0x02_SIZE, HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + + kfree(buf); + + return ret; } static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS]) @@ -1471,9 +1498,7 @@ error_leds: static void sixaxis_state_worker(struct work_struct *work) { - struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); - int n; - union sixaxis_output_report_01 report = { + static const union sixaxis_output_report_01 default_report = { .buf = { 0x01, 0x00, 0xff, 0x00, 0xff, 0x00, @@ -1485,20 +1510,27 @@ static void sixaxis_state_worker(struct work_struct *work) 0x00, 0x00, 0x00, 0x00, 0x00 } }; + struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); + struct sixaxis_output_report *report = + (struct sixaxis_output_report *)sc->output_report_dmabuf; + int n; + + /* Initialize the report with default values */ + memcpy(report, &default_report, sizeof(struct sixaxis_output_report)); #ifdef CONFIG_SONY_FF - report.data.rumble.right_motor_on = sc->right ? 1 : 0; - report.data.rumble.left_motor_force = sc->left; + report->rumble.right_motor_on = sc->right ? 1 : 0; + report->rumble.left_motor_force = sc->left; #endif - report.data.leds_bitmap |= sc->led_state[0] << 1; - report.data.leds_bitmap |= sc->led_state[1] << 2; - report.data.leds_bitmap |= sc->led_state[2] << 3; - report.data.leds_bitmap |= sc->led_state[3] << 4; + report->leds_bitmap |= sc->led_state[0] << 1; + report->leds_bitmap |= sc->led_state[1] << 2; + report->leds_bitmap |= sc->led_state[2] << 3; + report->leds_bitmap |= sc->led_state[3] << 4; /* Set flag for all leds off, required for 3rd party INTEC controller */ - if ((report.data.leds_bitmap & 0x1E) == 0) - report.data.leds_bitmap |= 0x20; + if ((report->leds_bitmap & 0x1E) == 0) + report->leds_bitmap |= 0x20; /* * The LEDs in the report are indexed in reverse order to their @@ -1511,28 +1543,30 @@ static void sixaxis_state_worker(struct work_struct *work) */ for (n = 0; n < 4; n++) { if (sc->led_delay_on[n] || sc->led_delay_off[n]) { - report.data.led[3 - n].duty_off = sc->led_delay_off[n]; - report.data.led[3 - n].duty_on = sc->led_delay_on[n]; + report->led[3 - n].duty_off = sc->led_delay_off[n]; + report->led[3 - n].duty_on = sc->led_delay_on[n]; } } - hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf, - sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); + hid_hw_raw_request(sc->hdev, report->report_id, (__u8 *)report, + sizeof(struct sixaxis_output_report), + HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); } static void dualshock4_state_worker(struct work_struct *work) { struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); struct hid_device *hdev = sc->hdev; + __u8 *buf = sc->output_report_dmabuf; int offset; - __u8 buf[78] = { 0 }; - if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { + memset(buf, 0, DS4_REPORT_0x05_SIZE); buf[0] = 0x05; buf[1] = 0xFF; offset = 4; } else { + memset(buf, 0, DS4_REPORT_0x11_SIZE); buf[0] = 0x11; buf[1] = 0xB0; buf[3] = 0x0F; @@ -1560,12 +1594,33 @@ static void dualshock4_state_worker(struct work_struct *work) buf[offset++] = sc->led_delay_off[3]; if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) - hid_hw_output_report(hdev, buf, 32); + hid_hw_output_report(hdev, buf, DS4_REPORT_0x05_SIZE); else - hid_hw_raw_request(hdev, 0x11, buf, 78, + hid_hw_raw_request(hdev, 0x11, buf, DS4_REPORT_0x11_SIZE, HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); } +static int sony_allocate_output_report(struct sony_sc *sc) +{ + if (sc->quirks & SIXAXIS_CONTROLLER) + sc->output_report_dmabuf = + kmalloc(sizeof(union sixaxis_output_report_01), + GFP_KERNEL); + else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) + sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x11_SIZE, + GFP_KERNEL); + else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) + sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x05_SIZE, + GFP_KERNEL); + else + return 0; + + if (!sc->output_report_dmabuf) + return -ENOMEM; + + return 0; +} + #ifdef CONFIG_SONY_FF static int sony_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) @@ -1754,6 +1809,7 @@ static int sony_get_bt_devaddr(struct sony_sc *sc) static int sony_check_add(struct sony_sc *sc) { + __u8 *buf = NULL; int n, ret; if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) || @@ -1769,36 +1825,44 @@ static int sony_check_add(struct sony_sc *sc) return 0; } } else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { - __u8 buf[7]; + buf = kmalloc(DS4_REPORT_0x81_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; /* * The MAC address of a DS4 controller connected via USB can be * retrieved with feature report 0x81. The address begins at * offset 1. */ - ret = hid_hw_raw_request(sc->hdev, 0x81, buf, sizeof(buf), - HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + ret = hid_hw_raw_request(sc->hdev, 0x81, buf, + DS4_REPORT_0x81_SIZE, HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); - if (ret != 7) { + if (ret != DS4_REPORT_0x81_SIZE) { hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n"); - return ret < 0 ? ret : -EINVAL; + ret = ret < 0 ? ret : -EINVAL; + goto out_free; } memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address)); } else if (sc->quirks & SIXAXIS_CONTROLLER_USB) { - __u8 buf[18]; + buf = kmalloc(SIXAXIS_REPORT_0xF2_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; /* * The MAC address of a Sixaxis controller connected via USB can * be retrieved with feature report 0xf2. The address begins at * offset 4. */ - ret = hid_hw_raw_request(sc->hdev, 0xf2, buf, sizeof(buf), - HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + ret = hid_hw_raw_request(sc->hdev, 0xf2, buf, + SIXAXIS_REPORT_0xF2_SIZE, HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); - if (ret != 18) { + if (ret != SIXAXIS_REPORT_0xF2_SIZE) { hid_err(sc->hdev, "failed to retrieve feature report 0xf2 with the Sixaxis MAC address\n"); - return ret < 0 ? ret : -EINVAL; + ret = ret < 0 ? ret : -EINVAL; + goto out_free; } /* @@ -1811,7 +1875,13 @@ static int sony_check_add(struct sony_sc *sc) return 0; } - return sony_check_add_dev_list(sc); + ret = sony_check_add_dev_list(sc); + +out_free: + + kfree(buf); + + return ret; } static int sony_set_device_id(struct sony_sc *sc) @@ -1895,6 +1965,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } + ret = sony_allocate_output_report(sc); + if (ret < 0) { + hid_err(hdev, "failed to allocate the output report buffer\n"); + goto err_stop; + } + ret = sony_set_device_id(sc); if (ret < 0) { hid_err(hdev, "failed to allocate the device id\n"); @@ -1984,6 +2060,7 @@ err_stop: if (sc->quirks & SONY_BATTERY_SUPPORT) sony_battery_remove(sc); sony_cancel_work_sync(sc); + kfree(sc->output_report_dmabuf); sony_remove_dev_list(sc); sony_release_device_id(sc); hid_hw_stop(hdev); @@ -2004,6 +2081,8 @@ static void sony_remove(struct hid_device *hdev) sony_cancel_work_sync(sc); + kfree(sc->output_report_dmabuf); + sony_remove_dev_list(sc); sony_release_device_id(sc); -- cgit v1.2.3 From a32c99e7ab8410bae7c276a7e94ca84d108de034 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 17 Nov 2014 17:11:42 +0100 Subject: HID: yet another buggy ELAN touchscreen The touchscreen needs the same quirk as the other models. Signed-off-by: Oliver Neukum Reported-by: Bryan Poling CC: stable@vger.kernel.org Acked-by: Greg Kroah-Hartman Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/usbhid/hid-quirks.c | 1 + drivers/usb/core/quirks.c | 3 +++ 3 files changed, 5 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 7c863738e419..a7c1c1d9706d 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -300,6 +300,7 @@ #define USB_DEVICE_ID_ELAN_TOUCHSCREEN 0x0089 #define USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B 0x009b #define USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103 0x0103 +#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_010c 0x010c #define USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F 0x016f #define USB_VENDOR_ID_ELECOM 0x056e diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 552671ee7c5d..3e5f169d35f3 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -73,6 +73,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103, HID_QUIRK_ALWAYS_POLL }, + { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_010c, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 39b4081b632d..8e8bc4f9d2f0 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -100,6 +100,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x04f3, 0x009b), .driver_info = USB_QUIRK_DEVICE_QUALIFIER }, + { USB_DEVICE(0x04f3, 0x010c), .driver_info = + USB_QUIRK_DEVICE_QUALIFIER }, + { USB_DEVICE(0x04f3, 0x016f), .driver_info = USB_QUIRK_DEVICE_QUALIFIER }, -- cgit v1.2.3 From 68a49e51a4a9c500d950f893f1758c5ac13b7c8b Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 12 Nov 2014 14:52:28 -0500 Subject: HID: sony: Add support for the third-party SMK PS3 Bluetooth Remote Add vid/pid for the SMK branded third-party PS3 Bluetooth remote and enable support in the hid-sony driver. Signed-off-by: Frank Praznik Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 +++ drivers/hid/hid-sony.c | 3 +++ 3 files changed, 7 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 3402033fa52a..0ab6d6af54c8 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1914,6 +1914,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 7c863738e419..27e0f5188d08 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -813,6 +813,9 @@ #define USB_VENDOR_ID_SKYCABLE 0x1223 #define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07 +#define USB_VENDOR_ID_SMK 0x0609 +#define USB_DEVICE_ID_SMK_PS3_BDREMOTE 0x0306 + #define USB_VENDOR_ID_SONY 0x054c #define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b #define USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE 0x0374 diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index b6e610289e9d..31e9d2561106 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -2113,6 +2113,9 @@ static const struct hid_device_id sony_devices[] = { /* Logitech Harmony Adapter for PS3 */ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3), .driver_data = PS3REMOTE }, + /* SMK-Link PS3 BD Remote Control */ + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE), + .driver_data = PS3REMOTE }, /* Sony Dualshock 4 controllers for PS4 */ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), .driver_data = DUALSHOCK4_CONTROLLER_USB }, -- cgit v1.2.3 From b3c8e93f99a7f9453be54549ae362b603c42188c Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Tue, 18 Nov 2014 13:27:48 -0800 Subject: HID: wacom - return ENODEV for failed wacom_setup_pad_input_capabilities ENODEV indicates no device is added. Hence, the associated pad input device is simply freed. Signed-off-by: Ping Cheng Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 104829524bcd..63821c0e8207 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2405,7 +2405,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, case INTUOSPS: /* touch interface does not have the pad device */ if (features->device_type != BTN_TOOL_PEN) - return 1; + return -ENODEV; for (i = 0; i < 7; i++) __set_bit(BTN_0 + i, input_dev->keybit); @@ -2450,7 +2450,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, case BAMBOO_PT: /* pad device is on the touch interface */ if (features->device_type != BTN_TOOL_FINGER) - return 1; + return -ENODEV; __clear_bit(ABS_MISC, input_dev->absbit); @@ -2463,7 +2463,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, default: /* no pad supported */ - return 1; + return -ENODEV; } return 0; } -- cgit v1.2.3 From 97edcad81324ede21e421b79eaeab26f0b1ad2fc Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Tue, 18 Nov 2014 13:28:37 -0800 Subject: HID: wacom - Cleanup input_capabilities for Graphire 4 and Bamboo Fun Graphire 4 only has two tablet buttons; Bamboo Fun touch ring is only for PAD. Signed-off-by: Ping Cheng Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 63821c0e8207..51c7353e01a5 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2050,9 +2050,6 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, switch (features->type) { case WACOM_MO: - input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); - /* fall through */ - case WACOM_G4: /* fall through */ @@ -2306,9 +2303,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, case WACOM_G4: __set_bit(BTN_BACK, input_dev->keybit); - __set_bit(BTN_LEFT, input_dev->keybit); __set_bit(BTN_FORWARD, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); input_set_capability(input_dev, EV_REL, REL_WHEEL); break; -- cgit v1.2.3 From 30ebc1aea8ce02da5b3789eba775dd2d79837813 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Tue, 18 Nov 2014 13:29:16 -0800 Subject: HID: wacom - Bamboo pen-only tablet does not support PAD Bamboo models do not support HID_DG_CONTACTMAX. Plus, Bamboo pen-only has touch descriptor. This leads to some complications in the code. This patch also fixes duplicated PAD interfeaces for Intuos Pen models. Signed-off-by: Ping Cheng Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom.h | 2 +- drivers/hid/wacom_sys.c | 25 +++++++++++++++---------- drivers/hid/wacom_wac.c | 9 +++++++-- 3 files changed, 23 insertions(+), 13 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h index 0cc53440543a..7db432809e9e 100644 --- a/drivers/hid/wacom.h +++ b/drivers/hid/wacom.h @@ -140,7 +140,7 @@ extern const struct hid_device_id wacom_ids[]; void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); void wacom_setup_device_quirks(struct wacom_features *features); -int wacom_setup_input_capabilities(struct input_dev *input_dev, +int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac); int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac); diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 8593047bb726..68b6cd66f9c3 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -192,9 +192,15 @@ static void wacom_usage_mapping(struct hid_device *hdev, if (!pen && !finger) return; - if (finger && !features->touch_max) - /* touch device at least supports one touch point */ - features->touch_max = 1; + /* + * Bamboo models do not support HID_DG_CONTACTMAX. + * And, Bamboo Pen only descriptor contains touch. + */ + if (features->type != BAMBOO_PT) { + /* ISDv4 touch devices at least supports one touch point */ + if (finger && !features->touch_max) + features->touch_max = 1; + } switch (usage->hid) { case HID_GD_X: @@ -1151,13 +1157,12 @@ static int wacom_register_inputs(struct wacom *wacom) if (!input_dev || !pad_input_dev) return -EINVAL; - error = wacom_setup_input_capabilities(input_dev, wacom_wac); - if (error) - return error; - - error = input_register_device(input_dev); - if (error) - return error; + error = wacom_setup_pentouch_input_capabilities(input_dev, wacom_wac); + if (!error) { + error = input_register_device(input_dev); + if (error) + return error; + } error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac); if (error) { diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 51c7353e01a5..caf035f662b9 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2032,7 +2032,7 @@ static void wacom_abs_set_axis(struct input_dev *input_dev, } } -int wacom_setup_input_capabilities(struct input_dev *input_dev, +int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { struct wacom_features *features = &wacom_wac->features; @@ -2246,6 +2246,9 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, __clear_bit(ABS_X, input_dev->absbit); __clear_bit(ABS_Y, input_dev->absbit); __clear_bit(BTN_TOUCH, input_dev->keybit); + + /* PAD is setup by wacom_setup_pad_input_capabilities later */ + return 1; } } else if (features->device_type == BTN_TOOL_PEN) { __set_bit(INPUT_PROP_POINTER, input_dev->propbit); @@ -2444,7 +2447,9 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, case INTUOSHT: case BAMBOO_PT: /* pad device is on the touch interface */ - if (features->device_type != BTN_TOOL_FINGER) + if ((features->device_type != BTN_TOOL_FINGER) || + /* Bamboo Pen only tablet does not have pad */ + ((features->type == BAMBOO_PT) && !features->touch_max)) return -ENODEV; __clear_bit(ABS_MISC, input_dev->absbit); -- cgit v1.2.3 From ec5fc1c1bdf0c445d2669ca9e1dbb69c9725856a Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 18 Nov 2014 16:50:08 -0800 Subject: HID: wacom: Report ABS_TILT_{X,Y} as signed values Centers the ABS_TILT_{X,Y} axes so that a value of zero is reported when the pen is vertical. Combined with resolution information in the next patch, this makes it possible for userspace to calculate the pen angle without needing hardware-specific knowledge. The xf86-input-wacom driver was updated to support signed tilt values in late-2012 (2f2acec). Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index caf035f662b9..74c9fbde5695 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -600,8 +600,8 @@ static void wacom_intuos_general(struct wacom_wac *wacom) } input_report_abs(input, ABS_PRESSURE, t); input_report_abs(input, ABS_TILT_X, - ((data[7] << 1) & 0x7e) | (data[8] >> 7)); - input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); + (((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64); + input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64); input_report_key(input, BTN_STYLUS, data[1] & 2); input_report_key(input, BTN_STYLUS2, data[1] & 4); input_report_key(input, BTN_TOUCH, t > 10); @@ -612,8 +612,8 @@ static void wacom_intuos_general(struct wacom_wac *wacom) input_report_abs(input, ABS_WHEEL, (data[6] << 2) | ((data[7] >> 6) & 3)); input_report_abs(input, ABS_TILT_X, - ((data[7] << 1) & 0x7e) | (data[8] >> 7)); - input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); + (((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64); + input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64); } } @@ -915,8 +915,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) input_report_key(input, BTN_EXTRA, data[6] & 0x10); input_report_abs(input, ABS_TILT_X, - ((data[7] << 1) & 0x7e) | (data[8] >> 7)); - input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); + (((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64); + input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64); } else { /* 2D mouse packet */ input_report_key(input, BTN_LEFT, data[8] & 0x04); @@ -1929,8 +1929,8 @@ static void wacom_setup_cintiq(struct wacom_wac *wacom_wac) input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features.distance_max, 0, 0); input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); - input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0); - input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_X, -64, 63, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_Y, -64, 63, 0, 0); } static void wacom_setup_intuos(struct wacom_wac *wacom_wac) -- cgit v1.2.3 From 26fe41245f43c86f65cae272a2851dd792e443f7 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 18 Nov 2014 16:50:09 -0800 Subject: HID: wacom: Add angular resolution data to some ABS axes Provide the resolution of several angular axes (tilt, pen rotation, puck rotation) to userspace. Because these values are natively degree-based, we need to convert them to into units/radian as required by the input_absinfo struct. To ensure wraparound behaves properly for the rotation axes, the converted value was rounded up rather than rounded nearest. Notably, the touchring axes (ABS_WHEEL and ABS_THROTTLE) are left without a a declared resolution because the their low resolution cannot be accurately represented (the worst-case rounding-induced error would be ~16 degrees). Pre-scaling the values and range by at least 10x would reduce the error in the resolution to acceptable levels, but the xf86-input-wacom driver is not able to use pre-scaled values for these axes at this time. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 74c9fbde5695..9565d31262ec 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1930,7 +1930,9 @@ static void wacom_setup_cintiq(struct wacom_wac *wacom_wac) 0, wacom_wac->features.distance_max, 0, 0); input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); input_set_abs_params(input_dev, ABS_TILT_X, -64, 63, 0, 0); + input_abs_set_res(input_dev, ABS_TILT_X, 57); input_set_abs_params(input_dev, ABS_TILT_Y, -64, 63, 0, 0); + input_abs_set_res(input_dev, ABS_TILT_Y, 57); } static void wacom_setup_intuos(struct wacom_wac *wacom_wac) @@ -1950,6 +1952,7 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac) __set_bit(BTN_TOOL_LENS, input_dev->keybit); input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0); + input_abs_set_res(input_dev, ABS_RZ, 287); input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0); } @@ -2092,6 +2095,7 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, case WACOM_24HD: input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + input_abs_set_res(input_dev, ABS_Z, 287); input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0); /* fall through */ @@ -2106,6 +2110,7 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, case WACOM_BEE: case CINTIQ: input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + input_abs_set_res(input_dev, ABS_Z, 287); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); @@ -2114,6 +2119,7 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, case WACOM_13HD: input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + input_abs_set_res(input_dev, ABS_Z, 287); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); wacom_setup_cintiq(wacom_wac); break; @@ -2122,6 +2128,7 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, case INTUOS3L: case INTUOS3S: input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + input_abs_set_res(input_dev, ABS_Z, 287); /* fall through */ case INTUOS: @@ -2144,6 +2151,7 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, 0, 0); input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + input_abs_set_res(input_dev, ABS_Z, 287); wacom_setup_intuos(wacom_wac); } else if (features->device_type == BTN_TOOL_FINGER) { @@ -2162,6 +2170,7 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, case INTUOS4L: case INTUOS4S: input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + input_abs_set_res(input_dev, ABS_Z, 287); wacom_setup_intuos(wacom_wac); __set_bit(INPUT_PROP_POINTER, input_dev->propbit); @@ -2264,6 +2273,7 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, case CINTIQ_HYBRID: input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + input_abs_set_res(input_dev, ABS_Z, 287); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); wacom_setup_cintiq(wacom_wac); -- cgit v1.2.3 From 6296f4a8eb86f9abcc370fb7a1a116b8441c17fd Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 20 Nov 2014 00:46:37 +0800 Subject: HID: i2c-hid: fix race condition reading reports Current driver uses a common buffer for reading reports either synchronously in i2c_hid_get_raw_report() and asynchronously in the interrupt handler. There is race condition if an interrupt arrives immediately after the report is received in i2c_hid_get_raw_report(); the common buffer is modified by the interrupt handler with the new report and then i2c_hid_get_raw_report() proceed using wrong data. Fix it by using a separate buffers for synchronous reports. Signed-off-by: Jean-Baptiste Maneyrol [Antonio Borneo: cleanup, rebase to v3.17, submit mainline] Signed-off-by: Antonio Borneo Reviewed-by: Benjamin Tissoires Cc: stable@vger.kernel.org Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 933bf1052ec0..c66e6acf7152 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -137,6 +137,7 @@ struct i2c_hid { * descriptor. */ unsigned int bufsize; /* i2c buffer size */ char *inbuf; /* Input buffer */ + char *rawbuf; /* Raw Input buffer */ char *cmdbuf; /* Command buffer */ char *argsbuf; /* Command arguments buffer */ @@ -504,9 +505,11 @@ static void i2c_hid_find_max_report(struct hid_device *hid, unsigned int type, static void i2c_hid_free_buffers(struct i2c_hid *ihid) { kfree(ihid->inbuf); + kfree(ihid->rawbuf); kfree(ihid->argsbuf); kfree(ihid->cmdbuf); ihid->inbuf = NULL; + ihid->rawbuf = NULL; ihid->cmdbuf = NULL; ihid->argsbuf = NULL; ihid->bufsize = 0; @@ -522,10 +525,11 @@ static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size) report_size; /* report */ ihid->inbuf = kzalloc(report_size, GFP_KERNEL); + ihid->rawbuf = kzalloc(report_size, GFP_KERNEL); ihid->argsbuf = kzalloc(args_len, GFP_KERNEL); ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL); - if (!ihid->inbuf || !ihid->argsbuf || !ihid->cmdbuf) { + if (!ihid->inbuf || !ihid->rawbuf || !ihid->argsbuf || !ihid->cmdbuf) { i2c_hid_free_buffers(ihid); return -ENOMEM; } @@ -552,12 +556,12 @@ static int i2c_hid_get_raw_report(struct hid_device *hid, ret = i2c_hid_get_report(client, report_type == HID_FEATURE_REPORT ? 0x03 : 0x01, - report_number, ihid->inbuf, ask_count); + report_number, ihid->rawbuf, ask_count); if (ret < 0) return ret; - ret_count = ihid->inbuf[0] | (ihid->inbuf[1] << 8); + ret_count = ihid->rawbuf[0] | (ihid->rawbuf[1] << 8); if (ret_count <= 2) return 0; @@ -566,7 +570,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid, /* The query buffer contains the size, dropping it in the reply */ count = min(count, ret_count - 2); - memcpy(buf, ihid->inbuf + 2, count); + memcpy(buf, ihid->rawbuf + 2, count); return count; } -- cgit v1.2.3 From da10bc252476a8d1d1bdf7b94502ad2cbd7f30b1 Mon Sep 17 00:00:00 2001 From: Mathieu Magnaudet Date: Sat, 22 Nov 2014 12:02:07 +0100 Subject: HID: multitouch: Add quirk for VTL touch panels VTL panels do not switch to the multitouch mode until the input mode feature is read by the host. This should normally be done by usbhid, but it looks like an other bug prevents usbhid to properly retrieve the feature state. As a workaround, we force the reading of the feature in mt_set_input_mode for such devices. Reviewed-by: Benjamin Tissoires Signed-off-by: Mathieu Magnaudet Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 3 +++ drivers/hid/hid-multitouch.c | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 7c863738e419..d6cc6a9cf10f 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -931,6 +931,9 @@ #define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 #define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006 +#define USB_VENDOR_ID_VTL 0x0306 +#define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F 0xff3f + #define USB_VENDOR_ID_WACOM 0x056a #define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81 #define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH 0x00BD diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 51e25b9407f2..683cda6c60ce 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -67,6 +67,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_IGNORE_DUPLICATES (1 << 10) #define MT_QUIRK_HOVERING (1 << 11) #define MT_QUIRK_CONTACT_CNT_ACCURATE (1 << 12) +#define MT_QUIRK_FORCE_GET_FEATURE (1 << 13) #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 @@ -150,6 +151,7 @@ static void mt_post_parse(struct mt_device *td); #define MT_CLS_FLATFROG 0x0107 #define MT_CLS_GENERALTOUCH_TWOFINGERS 0x0108 #define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109 +#define MT_CLS_VTL 0x0110 #define MT_DEFAULT_MAXCONTACT 10 #define MT_MAX_MAXCONTACT 250 @@ -255,6 +257,11 @@ static struct mt_class mt_classes[] = { .sn_move = 2048, .maxcontacts = 40, }, + { .name = MT_CLS_VTL, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_FORCE_GET_FEATURE, + }, { } }; @@ -809,6 +816,9 @@ static void mt_set_input_mode(struct hid_device *hdev) struct mt_device *td = hid_get_drvdata(hdev); struct hid_report *r; struct hid_report_enum *re; + struct mt_class *cls = &td->mtclass; + char *buf; + int report_len; if (td->inputmode < 0) return; @@ -816,6 +826,18 @@ static void mt_set_input_mode(struct hid_device *hdev) re = &(hdev->report_enum[HID_FEATURE_REPORT]); r = re->report_id_hash[td->inputmode]; if (r) { + if (cls->quirks & MT_QUIRK_FORCE_GET_FEATURE) { + report_len = ((r->size - 1) >> 3) + 1 + (r->id > 0); + buf = hid_alloc_report_buf(r, GFP_KERNEL); + if (!buf) { + hid_err(hdev, "failed to allocate buffer for report\n"); + return; + } + hid_hw_raw_request(hdev, r->id, buf, report_len, + HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); + kfree(buf); + } r->field[0]->value[td->inputmode_index] = td->inputmode_value; hid_hw_request(hdev, r, HID_REQ_SET_REPORT); } @@ -1281,6 +1303,11 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) }, + /* VTL panels */ + { .driver_data = MT_CLS_VTL, + MT_USB_DEVICE(USB_VENDOR_ID_VTL, + USB_DEVICE_ID_VTL_MULTITOUCH_FF3F) }, + /* Wistron panels */ { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_WISTRON, -- cgit v1.2.3 From 954df6ad00ea2ba732e2e7ae6a68b3111eee4e09 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Thu, 20 Nov 2014 16:31:12 -0800 Subject: HID: wacom: PAD is independent with pen/touch PAD can be on pen interface (Intuos Pro and Cintiq series) or touch interface (Bamboo PT and Intuos PT series) or its own interface (Bamboo pen-only and Intuos Pen M/S). We need to mark it independently. Signed-off-by: Ping Cheng Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 8 +++++--- drivers/hid/wacom_wac.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 68b6cd66f9c3..eb5531657e41 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -1135,7 +1135,7 @@ static void wacom_clean_inputs(struct wacom *wacom) input_free_device(wacom->wacom_wac.input); } if (wacom->wacom_wac.pad_input) { - if (wacom->wacom_wac.input_registered) + if (wacom->wacom_wac.pad_registered) input_unregister_device(wacom->wacom_wac.pad_input); else input_free_device(wacom->wacom_wac.pad_input); @@ -1162,6 +1162,7 @@ static int wacom_register_inputs(struct wacom *wacom) error = input_register_device(input_dev); if (error) return error; + wacom_wac->input_registered = true; } error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac); @@ -1174,22 +1175,23 @@ static int wacom_register_inputs(struct wacom *wacom) error = input_register_device(pad_input_dev); if (error) goto fail_register_pad_input; + wacom_wac->pad_registered = true; error = wacom_initialize_leds(wacom); if (error) goto fail_leds; } - wacom_wac->input_registered = true; - return 0; fail_leds: input_unregister_device(pad_input_dev); pad_input_dev = NULL; + wacom_wac->pad_registered = false; fail_register_pad_input: input_unregister_device(input_dev); wacom_wac->input = NULL; + wacom_wac->input_registered = false; return error; } diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 0f0b85ec1322..128cbb337ff6 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -183,6 +183,7 @@ struct wacom_wac { struct input_dev *input; struct input_dev *pad_input; bool input_registered; + bool pad_registered; int pid; int battery_capacity; int num_contacts_left; -- cgit v1.2.3 From c376e7167e0ff390e07d60b4320398c3baae72a6 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Mon, 24 Nov 2014 15:32:12 -0800 Subject: HID: wacom: Consult the application usage when determining field type It is not necessarily sufficient to look only at the physical and logical usages when determining if a field is for the pen or touch. Some fields are not contained in a sub-collection and thus only have an application usage. Not checking the application usage in such cases causes us to ignore the field entirely, which may lead to incorrect behavior. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 9565d31262ec..1468f008bfbd 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1484,9 +1484,11 @@ static void wacom_wac_finger_report(struct hid_device *hdev, } #define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \ - ((f)->physical == HID_DG_STYLUS)) + ((f)->physical == HID_DG_STYLUS) || \ + ((f)->application == HID_DG_PEN)) #define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \ - ((f)->physical == HID_DG_FINGER)) + ((f)->physical == HID_DG_FINGER) || \ + ((f)->application == HID_DG_TOUCHSCREEN)) void wacom_wac_usage_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) -- cgit v1.2.3 From dabb05c6670e6e4415d7b56cd5864a5dbd90207f Mon Sep 17 00:00:00 2001 From: Mathieu Magnaudet Date: Thu, 27 Nov 2014 16:02:36 +0100 Subject: HID: make hid_report_len as a static inline function in hid.h In several hid drivers it is necessary to calculate the length of an hid_report. This patch exports the existing static function hid_report_len of hid-core.c as an inline function in hid.h Signed-off-by: Mathieu Magnaudet Reviewed-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 6 ------ drivers/hid/hid-input.c | 2 +- drivers/hid/hid-multitouch.c | 2 +- drivers/hid/usbhid/hid-core.c | 3 +-- drivers/hid/wacom_sys.c | 8 +------- include/linux/hid.h | 11 +++++++++++ 6 files changed, 15 insertions(+), 17 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 3402033fa52a..ee6ba7625e77 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1280,12 +1280,6 @@ void hid_output_report(struct hid_report *report, __u8 *data) } EXPORT_SYMBOL_GPL(hid_output_report); -static int hid_report_len(struct hid_report *report) -{ - /* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */ - return ((report->size - 1) >> 3) + 1 + (report->id > 0); -} - /* * Allocator for buffer that is going to be passed to hid_output_report() */ diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 725f22ca47fc..1e95f4df4146 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1215,7 +1215,7 @@ static void hidinput_led_worker(struct work_struct *work) return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT); /* fall back to generic raw-output-report */ - len = ((report->size - 1) >> 3) + 1 + (report->id > 0); + len = hid_report_len(report); buf = hid_alloc_report_buf(report, GFP_KERNEL); if (!buf) return; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 683cda6c60ce..f65e78b46999 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -827,7 +827,7 @@ static void mt_set_input_mode(struct hid_device *hdev) r = re->report_id_hash[td->inputmode]; if (r) { if (cls->quirks & MT_QUIRK_FORCE_GET_FEATURE) { - report_len = ((r->size - 1) >> 3) + 1 + (r->id > 0); + report_len = hid_report_len(r); buf = hid_alloc_report_buf(r, GFP_KERNEL); if (!buf) { hid_err(hdev, "failed to allocate buffer for report\n"); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index ca6849a0121e..b6cb7a5e4b27 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -338,8 +338,7 @@ static int hid_submit_out(struct hid_device *hid) report = usbhid->out[usbhid->outtail].report; raw_report = usbhid->out[usbhid->outtail].raw_report; - usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + - 1 + (report->id > 0); + usbhid->urbout->transfer_buffer_length = hid_report_len(report); usbhid->urbout->dev = hid_to_usb_dev(hid); if (raw_report) { memcpy(usbhid->outbuf, raw_report, diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 8593047bb726..8e02a4a6fde0 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -1321,12 +1321,6 @@ static void wacom_calculate_res(struct wacom_features *features) features->unitExpo); } -static int wacom_hid_report_len(struct hid_report *report) -{ - /* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */ - return ((report->size - 1) >> 3) + 1 + (report->id > 0); -} - static size_t wacom_compute_pktlen(struct hid_device *hdev) { struct hid_report_enum *report_enum; @@ -1336,7 +1330,7 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev) report_enum = hdev->report_enum + HID_INPUT_REPORT; list_for_each_entry(report, &report_enum->report_list, list) { - size_t report_size = wacom_hid_report_len(report); + size_t report_size = hid_report_len(report); if (report_size > size) size = report_size; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 78ea9bf941cd..2366fda010c8 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -1063,6 +1063,17 @@ static inline void hid_hw_wait(struct hid_device *hdev) hdev->ll_driver->wait(hdev); } +/** + * hid_report_len - calculate the report length + * + * @report: the report we want to know the length + */ +static inline int hid_report_len(struct hid_report *report) +{ + /* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */ + return ((report->size - 1) >> 3) + 1 + (report->id > 0); +} + int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int interrupt); -- cgit v1.2.3 From fa770340286bffddf65f2322a2ca10d65c3e38e4 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Mon, 1 Dec 2014 13:44:28 -0800 Subject: HID: wacom: add defines for new Cintiq and DTU outbound tracking There are screen to tablet offsets for newer Cintiq and DTU models. These offsets serve as outbound tracking for those display tablets. Use defines instead of hardcoded numbers for the offsets. Signed-off-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 1468f008bfbd..a8a19a5ccee5 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -25,6 +25,10 @@ #define WACOM_INTUOS_RES 100 #define WACOM_INTUOS3_RES 200 +/* Newer Cintiq and DTU have an offset between tablet and screen areas */ +#define WACOM_DTU_OFFSET 200 +#define WACOM_CINTIQ_OFFSET 400 + /* * Scale factor relating reported contact size to logical contact area. * 2^14/pi is a good approximation on Intuos5 and 3rd-gen Bamboo @@ -2680,10 +2684,12 @@ static const struct wacom_features wacom_features_0x317 = .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0xF4 = { "Wacom Cintiq 24HD", 104280, 65400, 2047, 63, - WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; + WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0xF8 = { "Wacom Cintiq 24HD touch", 104280, 65400, 2047, 63, /* Pen */ - WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 }; static const struct wacom_features wacom_features_0xF6 = { "Wacom Cintiq 24HD touch", .type = WACOM_24HDT, /* Touch */ @@ -2700,7 +2706,8 @@ static const struct wacom_features wacom_features_0xC6 = WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0x304 = { "Wacom Cintiq 13HD", 59352, 33648, 1023, 63, - WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; + WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0xC7 = { "Wacom DTU1931", 37832, 30305, 511, 0, PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -2713,13 +2720,16 @@ static const struct wacom_features wacom_features_0xF0 = DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xFB = { "Wacom DTU1031", 22096, 13960, 511, 0, - DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, + WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; static const struct wacom_features wacom_features_0x57 = { "Wacom DTK2241", 95640, 54060, 2047, 63, - DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; + DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0x59 = /* Pen */ { "Wacom DTH2242", 95640, 54060, 2047, 63, - DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D }; static const struct wacom_features wacom_features_0x5D = /* Touch */ { "Wacom DTH2242", .type = WACOM_24HDT, @@ -2727,13 +2737,16 @@ static const struct wacom_features wacom_features_0x5D = /* Touch */ .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0xCC = { "Wacom Cintiq 21UX2", 87000, 65400, 2047, 63, - WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; + WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0xFA = { "Wacom Cintiq 22HD", 95640, 54060, 2047, 63, - WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; + WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0x5B = { "Wacom Cintiq 22HDT", 95640, 54060, 2047, 63, - WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e }; static const struct wacom_features wacom_features_0x5E = { "Wacom Cintiq 22HDT", .type = WACOM_24HDT, @@ -2879,7 +2892,8 @@ static const struct wacom_features wacom_features_0x6004 = TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x307 = { "Wacom ISDv5 307", 59352, 33648, 2047, 63, - CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 }; static const struct wacom_features wacom_features_0x309 = { "Wacom ISDv5 309", .type = WACOM_24HDT, /* Touch */ @@ -2887,7 +2901,8 @@ static const struct wacom_features wacom_features_0x309 = .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x30A = { "Wacom ISDv5 30A", 59352, 33648, 2047, 63, - CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30C }; static const struct wacom_features wacom_features_0x30C = { "Wacom ISDv5 30C", .type = WACOM_24HDT, /* Touch */ -- cgit v1.2.3 From fff00bf8cc8d98aa3b84d9c7daea0af418b6aa69 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Thu, 4 Dec 2014 18:23:04 -0800 Subject: HID: wacom: Add support for DTU-1031X Signed-off-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 7 +++++++ drivers/hid/wacom_wac.h | 1 + 2 files changed, 8 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index a8a19a5ccee5..b65e0d7cd7e5 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1847,6 +1847,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) break; case DTUS: + case DTUSX: sync = wacom_dtus_irq(wacom_wac); break; @@ -2211,6 +2212,7 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, /* fall through */ case DTUS: + case DTUSX: case PL: case DTU: __set_bit(BTN_TOOL_PEN, input_dev->keybit); @@ -2722,6 +2724,10 @@ static const struct wacom_features wacom_features_0xFB = { "Wacom DTU1031", 22096, 13960, 511, 0, DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; +static const struct wacom_features wacom_features_0x32F = + { "Wacom DTU1031X", 22472, 12728, 511, 0, + DTUSX, WACOM_INTUOS_RES, WACOM_INTUOS_RES, + WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; static const struct wacom_features wacom_features_0x57 = { "Wacom DTK2241", 95640, 54060, 2047, 63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, @@ -3057,6 +3063,7 @@ const struct hid_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x315) }, { USB_DEVICE_WACOM(0x317) }, { USB_DEVICE_WACOM(0x323) }, + { USB_DEVICE_WACOM(0x32F) }, { USB_DEVICE_WACOM(0x4001) }, { USB_DEVICE_WACOM(0x4004) }, { USB_DEVICE_WACOM(0x5000) }, diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 128cbb337ff6..5384043778fc 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -80,6 +80,7 @@ enum { PL, DTU, DTUS, + DTUSX, INTUOS, INTUOS3S, INTUOS3, -- cgit v1.2.3 From ecd618dc25582923add5d7abfbb3b9aeaf51e2ac Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Thu, 4 Dec 2014 18:25:07 -0800 Subject: HID: wacom: Update maximum X/Y accounding to outbound offset Defined outbound offset for DTU and Cintiq. But didn't update the relevant maximum values. Oops... Signed-off-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index b65e0d7cd7e5..7b7a61a9194d 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2685,11 +2685,11 @@ static const struct wacom_features wacom_features_0x317 = INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0xF4 = - { "Wacom Cintiq 24HD", 104280, 65400, 2047, 63, + { "Wacom Cintiq 24HD", 104080, 65200, 2047, 63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0xF8 = - { "Wacom Cintiq 24HD touch", 104280, 65400, 2047, 63, /* Pen */ + { "Wacom Cintiq 24HD touch", 104080, 65200, 2047, 63, /* Pen */ WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 }; @@ -2707,7 +2707,7 @@ static const struct wacom_features wacom_features_0xC6 = { "Wacom Cintiq 12WX", 53020, 33440, 1023, 63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0x304 = - { "Wacom Cintiq 13HD", 59352, 33648, 1023, 63, + { "Wacom Cintiq 13HD", 59152, 33448, 1023, 63, WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0xC7 = @@ -2721,7 +2721,7 @@ static const struct wacom_features wacom_features_0xF0 = { "Wacom DTU1631", 34623, 19553, 511, 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xFB = - { "Wacom DTU1031", 22096, 13960, 511, 0, + { "Wacom DTU1031", 21896, 13760, 511, 0, DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; static const struct wacom_features wacom_features_0x32F = @@ -2742,15 +2742,15 @@ static const struct wacom_features wacom_features_0x5D = /* Touch */ .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0xCC = - { "Wacom Cintiq 21UX2", 87000, 65400, 2047, 63, + { "Wacom Cintiq 21UX2", 86800, 65200, 2047, 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0xFA = - { "Wacom Cintiq 22HD", 95640, 54060, 2047, 63, + { "Wacom Cintiq 22HD", 95440, 53860, 2047, 63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0x5B = - { "Wacom Cintiq 22HDT", 95640, 54060, 2047, 63, + { "Wacom Cintiq 22HDT", 95440, 53860, 2047, 63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e }; @@ -2897,7 +2897,7 @@ static const struct wacom_features wacom_features_0x6004 = { "ISD-V4", 12800, 8000, 255, 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x307 = - { "Wacom ISDv5 307", 59352, 33648, 2047, 63, + { "Wacom ISDv5 307", 59152, 33448, 2047, 63, CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 }; @@ -2906,7 +2906,7 @@ static const struct wacom_features wacom_features_0x309 = .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x30A = - { "Wacom ISDv5 30A", 59352, 33648, 2047, 63, + { "Wacom ISDv5 30A", 59152, 33448, 2047, 63, CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30C }; -- cgit v1.2.3 From a966fdd5830e83c790032bfa45c6a6079cda6374 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Mon, 8 Dec 2014 15:01:59 -0800 Subject: HID: rmi: Check for additional ACM registers appended to F11 data report If a touchpad reports the F11 data40 register then this indicates that the touchpad reports additional ACM (Accidental Contact Mitigation) data after the F11 data in the HID attention report. These additional bytes shift the position of the F30 button data causing the driver to incorrectly report button state when this functionality is present. This patch accounts for the additional data in the report. Fixes: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1398533 Signed-off-by: Andrew Duggan Signed-off-by: Jiri Kosina --- drivers/hid/hid-rmi.c | 61 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 9 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 3cccff73b9b9..a994477bd25a 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -584,11 +584,15 @@ static int rmi_populate_f11(struct hid_device *hdev) bool has_query10 = false; bool has_query11; bool has_query12; + bool has_query27; + bool has_query28; + bool has_query36 = false; bool has_physical_props; bool has_gestures; bool has_rel; + bool has_data40 = false; unsigned x_size, y_size; - u16 query12_offset; + u16 query_offset; if (!data->f11.query_base_addr) { hid_err(hdev, "No 2D sensor found, giving up.\n"); @@ -604,6 +608,8 @@ static int rmi_populate_f11(struct hid_device *hdev) has_query9 = !!(buf[0] & BIT(3)); has_query11 = !!(buf[0] & BIT(4)); has_query12 = !!(buf[0] & BIT(5)); + has_query27 = !!(buf[0] & BIT(6)); + has_query28 = !!(buf[0] & BIT(7)); /* query 1 to get the max number of fingers */ ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf); @@ -642,27 +648,27 @@ static int rmi_populate_f11(struct hid_device *hdev) * +1 for query 5 which is present since absolute events are * reported and +1 for query 12. */ - query12_offset = 6; + query_offset = 6; if (has_rel) - ++query12_offset; /* query 6 is present */ + ++query_offset; /* query 6 is present */ if (has_gestures) - query12_offset += 2; /* query 7 and 8 are present */ + query_offset += 2; /* query 7 and 8 are present */ if (has_query9) - ++query12_offset; + ++query_offset; if (has_query10) - ++query12_offset; + ++query_offset; if (has_query11) - ++query12_offset; + ++query_offset; /* query 12 to know if the physical properties are reported */ if (has_query12) { ret = rmi_read(hdev, data->f11.query_base_addr - + query12_offset, buf); + + query_offset, buf); if (ret) { hid_err(hdev, "can not get query 12: %d.\n", ret); return ret; @@ -670,9 +676,10 @@ static int rmi_populate_f11(struct hid_device *hdev) has_physical_props = !!(buf[0] & BIT(5)); if (has_physical_props) { + query_offset += 1; ret = rmi_read_block(hdev, data->f11.query_base_addr - + query12_offset + 1, buf, 4); + + query_offset, buf, 4); if (ret) { hid_err(hdev, "can not read query 15-18: %d.\n", ret); @@ -687,9 +694,45 @@ static int rmi_populate_f11(struct hid_device *hdev) hid_info(hdev, "%s: size in mm: %d x %d\n", __func__, data->x_size_mm, data->y_size_mm); + + /* + * query 15 - 18 contain the size of the sensor + * and query 19 - 26 contain bezel dimensions + */ + query_offset += 12; + } + } + + if (has_query27) + ++query_offset; + + if (has_query28) { + ret = rmi_read(hdev, data->f11.query_base_addr + + query_offset, buf); + if (ret) { + hid_err(hdev, "can not get query 28: %d.\n", ret); + return ret; + } + + has_query36 = !!(buf[0] & BIT(6)); + } + + if (has_query36) { + query_offset += 2; + ret = rmi_read(hdev, data->f11.query_base_addr + + query_offset, buf); + if (ret) { + hid_err(hdev, "can not get query 36: %d.\n", ret); + return ret; } + + has_data40 = !!(buf[0] & BIT(5)); } + + if (has_data40) + data->f11.report_size += data->max_fingers * 2; + /* * retrieve the ctrl registers * the ctrl register has a size of 20 but a fw bug split it into 16 + 4, -- cgit v1.2.3 From df9e11ffae0f71b7667e17c137bc8e6ea4491236 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Mon, 8 Dec 2014 15:02:00 -0800 Subject: HID: rmi: The address of query8 must be calculated based on which query registers are present If a touchpad does not report relative data then query 6 will not be present and the address of query 8 will be one less. This patches calculates the location of query 8 instead of hardcoding the offset. Signed-off-by: Andrew Duggan Signed-off-by: Jiri Kosina --- drivers/hid/hid-rmi.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index a994477bd25a..b51200fe2f33 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -632,17 +632,6 @@ static int rmi_populate_f11(struct hid_device *hdev) 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)); - } - /* * At least 4 queries are guaranteed to be present in F11 * +1 for query 5 which is present since absolute events are @@ -653,8 +642,19 @@ static int rmi_populate_f11(struct hid_device *hdev) if (has_rel) ++query_offset; /* query 6 is present */ - if (has_gestures) + if (has_gestures) { + /* query 8 to find out if query 10 exists */ + ret = rmi_read(hdev, + data->f11.query_base_addr + query_offset + 1, buf); + if (ret) { + hid_err(hdev, "can not read gesture information: %d.\n", + ret); + return ret; + } + has_query10 = !!(buf[0] & BIT(2)); + query_offset += 2; /* query 7 and 8 are present */ + } if (has_query9) ++query_offset; -- cgit v1.2.3 From b58ba1ba1af9cfbad6f3af4c4fc3575d9aeae542 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 5 Dec 2014 13:37:32 -0800 Subject: HID: wacom: Initialize MT slots for generic devices at post_parse_hid If a HID descriptor places HID_DG_CONTACTID before HID_DG_X and HID_DG_Y then the ABS_X and ABS_Y will not be automatically initialized by the call to input_mt_init_slots. To ensure that this is not a problem, we relocate that call to occur after HID parsing has been completed and we've initalized all the multitouch axes. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 18 ++++++++++++++++++ drivers/hid/wacom_wac.c | 3 --- 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index eb5531657e41..872aa0be2e70 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -13,6 +13,7 @@ #include "wacom_wac.h" #include "wacom.h" +#include #define WAC_MSG_RETRIES 5 @@ -236,6 +237,21 @@ static void wacom_usage_mapping(struct hid_device *hdev, wacom_wac_usage_mapping(hdev, field, usage); } +static void wacom_post_parse_hid(struct hid_device *hdev, + struct wacom_features *features) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + + if (features->type == HID_GENERIC) { + /* Any last-minute generic device setup */ + if (features->touch_max > 1) { + input_mt_init_slots(wacom_wac->input, wacom_wac->features.touch_max, + INPUT_MT_DIRECT); + } + } +} + static void wacom_parse_hid(struct hid_device *hdev, struct wacom_features *features) { @@ -270,6 +286,8 @@ static void wacom_parse_hid(struct hid_device *hdev, wacom_usage_mapping(hdev, hreport->field[i], hreport->field[i]->usage + j); } + + wacom_post_parse_hid(hdev, features); } static int wacom_hid_set_device_mode(struct hid_device *hdev) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 7b7a61a9194d..064fd6cf36c6 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1381,7 +1381,6 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; - struct input_dev *input = wacom_wac->input; unsigned touch_max = wacom_wac->features.touch_max; switch (usage->hid) { @@ -1400,8 +1399,6 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, ABS_MT_POSITION_Y, 4); break; case HID_DG_CONTACTID: - input_mt_init_slots(input, wacom_wac->features.touch_max, - INPUT_MT_DIRECT); break; case HID_DG_INRANGE: break; -- cgit v1.2.3