summaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-11-07 12:49:27 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2015-11-07 12:49:27 -0800
commit6f1da317ac1df15f442b5fd37be7740c7cb55057 (patch)
tree6e0dc524079a833e84cb657eab6f95e5af015bd6 /drivers/hid
parent99aaa9c64be5fe728975109c96ef297f5650883d (diff)
parentd64e19db030dadb1c9d2747ace919fd952be359c (diff)
downloadlinux-6f1da317ac1df15f442b5fd37be7740c7cb55057.tar.bz2
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID updates from Jiri Kosina: "Highlights: - Intel Skylake Win8 precision touchpads support fixes/improvements from Mika Westerberg - Lenovo Yoga 2 quirk from Ritesh Raj Sarraf - potential uninitialized buffer access fix in HID core from Richard Purdie - Wacom Intuos and Wacom Cintiq 2 support improvements from Jason Gerecke and Ping Cheng - initiation of sysfs deprecation process for most of the roccat drivers, from the roccat support maintiner Stefan Achatz - quite a few device ID / quirk additions and small fixes" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (30 commits) HID: logitech: Add support for G29 HID: logitech: Simplify wheel detection scheme HID: wacom: Call 'wacom_query_tablet_data' only after 'hid_hw_start' HID: wacom: Fix ABS_MISC reporting for Cintiq Companion 2 HID: wacom: Remove useless conditions from 'wacom_query_tablet_data' HID: wacom: fix Intuos wireless report id issue HID: fix some indenting issues HID: wacom: Expect 'touch_max' touches if HID_DG_CONTACTCOUNT not present HID: wacom: Tie cached HID_DG_CONTACTCOUNT indices to report ID HID: roccat: Fixed resubmit: Deprecating most Roccat sysfs attributes HID: wacom: Report full pressure range for Intuos, Cintiq 13HD Touch HID: wacom: Add support for Cintiq Companion 2 HID: multitouch: Fetch feature reports on demand for Win8 devices HID: sensor-hub: Add quirk for Lenovo Yoga 2 with ITE Chips HID: usbhid: Fix for the WiiU adapter from Mayflash HID: corsair: boolify struct k90_led.removed HID: corsair: Add Corsair Vengeance K90 driver HID: hid-input: allow input_configured callback return errors HID: multitouch: Add suffix for HID_DG_TOUCHPAD HID: i2c-hid: Fill in physical device providing HID functionality ...
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/Kconfig15
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/hid-aureal.c3
-rw-r--r--drivers/hid/hid-core.c10
-rw-r--r--drivers/hid/hid-corsair.c673
-rw-r--r--drivers/hid/hid-dr.c58
-rw-r--r--drivers/hid/hid-elecom.c2
-rw-r--r--drivers/hid/hid-ids.h7
-rw-r--r--drivers/hid/hid-lg.c9
-rw-r--r--drivers/hid/hid-lg4ff.c127
-rw-r--r--drivers/hid/hid-logitech-hidpp.c159
-rw-r--r--drivers/hid/hid-microsoft.c2
-rw-r--r--drivers/hid/hid-multitouch.c48
-rw-r--r--drivers/hid/hid-prodikeys.c4
-rw-r--r--drivers/hid/hid-saitek.c2
-rw-r--r--drivers/hid/hid-sensor-hub.c25
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.c1
-rw-r--r--drivers/hid/usbhid/hid-quirks.c2
-rw-r--r--drivers/hid/wacom_sys.c58
-rw-r--r--drivers/hid/wacom_wac.c191
-rw-r--r--drivers/hid/wacom_wac.h10
21 files changed, 1279 insertions, 128 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7245b7f8767f..513a16cc6e18 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -171,6 +171,16 @@ config HID_CHICONY
---help---
Support for Chicony Tactical pad.
+config HID_CORSAIR
+ tristate "Corsair devices"
+ depends on HID && USB && LEDS_CLASS
+ ---help---
+ Support for Corsair devices that are not fully compliant with the
+ HID standard.
+
+ Supported devices:
+ - Vengeance K90
+
config HID_PRODIKEYS
tristate "Prodikeys PC-MIDI Keyboard support"
depends on HID && SND
@@ -678,9 +688,8 @@ 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
+ - Saitek R.A.T.7, R.A.T.9, M.M.O.7 Gaming Mice
+ - Mad Catz R.A.T.5, R.A.T.9 Gaming Mice
config HID_SAMSUNG
tristate "Samsung InfraRed remote control or keyboards"
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 571d176d22df..00011fee08b9 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
+obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o
obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o
diff --git a/drivers/hid/hid-aureal.c b/drivers/hid/hid-aureal.c
index 340ba9d394a0..3280aff28e90 100644
--- a/drivers/hid/hid-aureal.c
+++ b/drivers/hid/hid-aureal.c
@@ -23,7 +23,8 @@ static __u8 *aureal_report_fixup(struct hid_device *hdev, __u8 *rdesc,
if (*rsize >= 54 && rdesc[52] == 0x25 && rdesc[53] == 0x01) {
dev_info(&hdev->dev, "fixing Aureal Cy se W-01RN USB_V3.1 report descriptor.\n");
rdesc[53] = 0x65;
- } return rdesc;
+ }
+ return rdesc;
}
static const struct hid_device_id aureal_devices[] = {
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 70a11ac38119..c6f7a694f67a 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -725,6 +725,7 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
if (hid->vendor == USB_VENDOR_ID_MICROSOFT &&
(hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 ||
+ hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 ||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP ||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 ||
hid->product == USB_DEVICE_ID_MS_POWER_COVER) &&
@@ -1611,7 +1612,7 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
"Multi-Axis Controller"
};
const char *type, *bus;
- char buf[64];
+ char buf[64] = "";
unsigned int i;
int len;
int ret;
@@ -1678,6 +1679,9 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
case BUS_BLUETOOTH:
bus = "BLUETOOTH";
break;
+ case BUS_I2C:
+ bus = "I2C";
+ break;
default:
bus = "<UNKNOWN>";
}
@@ -1828,6 +1832,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
@@ -1896,6 +1901,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
@@ -1928,6 +1934,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ 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_PRO_3) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER) },
@@ -1981,6 +1988,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD) },
{ 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_RAT5) },
{ 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) },
diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c
new file mode 100644
index 000000000000..bcefb9ebb026
--- /dev/null
+++ b/drivers/hid/hid-corsair.c
@@ -0,0 +1,673 @@
+/*
+ * HID driver for Corsair devices
+ *
+ * Supported devices:
+ * - Vengeance K90 Keyboard
+ *
+ * Copyright (c) 2015 Clement Vuchener
+ */
+
+/*
+ * 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 <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/leds.h>
+
+#include "hid-ids.h"
+
+#define CORSAIR_USE_K90_MACRO (1<<0)
+#define CORSAIR_USE_K90_BACKLIGHT (1<<1)
+
+struct k90_led {
+ struct led_classdev cdev;
+ int brightness;
+ struct work_struct work;
+ bool removed;
+};
+
+struct k90_drvdata {
+ struct k90_led record_led;
+};
+
+struct corsair_drvdata {
+ unsigned long quirks;
+ struct k90_drvdata *k90;
+ struct k90_led *backlight;
+};
+
+#define K90_GKEY_COUNT 18
+
+static int corsair_usage_to_gkey(unsigned int usage)
+{
+ /* G1 (0xd0) to G16 (0xdf) */
+ if (usage >= 0xd0 && usage <= 0xdf)
+ return usage - 0xd0 + 1;
+ /* G17 (0xe8) to G18 (0xe9) */
+ if (usage >= 0xe8 && usage <= 0xe9)
+ return usage - 0xe8 + 17;
+ return 0;
+}
+
+static unsigned short corsair_gkey_map[K90_GKEY_COUNT] = {
+ BTN_TRIGGER_HAPPY1,
+ BTN_TRIGGER_HAPPY2,
+ BTN_TRIGGER_HAPPY3,
+ BTN_TRIGGER_HAPPY4,
+ BTN_TRIGGER_HAPPY5,
+ BTN_TRIGGER_HAPPY6,
+ BTN_TRIGGER_HAPPY7,
+ BTN_TRIGGER_HAPPY8,
+ BTN_TRIGGER_HAPPY9,
+ BTN_TRIGGER_HAPPY10,
+ BTN_TRIGGER_HAPPY11,
+ BTN_TRIGGER_HAPPY12,
+ BTN_TRIGGER_HAPPY13,
+ BTN_TRIGGER_HAPPY14,
+ BTN_TRIGGER_HAPPY15,
+ BTN_TRIGGER_HAPPY16,
+ BTN_TRIGGER_HAPPY17,
+ BTN_TRIGGER_HAPPY18,
+};
+
+module_param_array_named(gkey_codes, corsair_gkey_map, ushort, NULL, S_IRUGO);
+MODULE_PARM_DESC(gkey_codes, "Key codes for the G-keys");
+
+static unsigned short corsair_record_keycodes[2] = {
+ BTN_TRIGGER_HAPPY19,
+ BTN_TRIGGER_HAPPY20
+};
+
+module_param_array_named(recordkey_codes, corsair_record_keycodes, ushort,
+ NULL, S_IRUGO);
+MODULE_PARM_DESC(recordkey_codes, "Key codes for the MR (start and stop record) button");
+
+static unsigned short corsair_profile_keycodes[3] = {
+ BTN_TRIGGER_HAPPY21,
+ BTN_TRIGGER_HAPPY22,
+ BTN_TRIGGER_HAPPY23
+};
+
+module_param_array_named(profilekey_codes, corsair_profile_keycodes, ushort,
+ NULL, S_IRUGO);
+MODULE_PARM_DESC(profilekey_codes, "Key codes for the profile buttons");
+
+#define CORSAIR_USAGE_SPECIAL_MIN 0xf0
+#define CORSAIR_USAGE_SPECIAL_MAX 0xff
+
+#define CORSAIR_USAGE_MACRO_RECORD_START 0xf6
+#define CORSAIR_USAGE_MACRO_RECORD_STOP 0xf7
+
+#define CORSAIR_USAGE_PROFILE 0xf1
+#define CORSAIR_USAGE_M1 0xf1
+#define CORSAIR_USAGE_M2 0xf2
+#define CORSAIR_USAGE_M3 0xf3
+#define CORSAIR_USAGE_PROFILE_MAX 0xf3
+
+#define CORSAIR_USAGE_META_OFF 0xf4
+#define CORSAIR_USAGE_META_ON 0xf5
+
+#define CORSAIR_USAGE_LIGHT 0xfa
+#define CORSAIR_USAGE_LIGHT_OFF 0xfa
+#define CORSAIR_USAGE_LIGHT_DIM 0xfb
+#define CORSAIR_USAGE_LIGHT_MEDIUM 0xfc
+#define CORSAIR_USAGE_LIGHT_BRIGHT 0xfd
+#define CORSAIR_USAGE_LIGHT_MAX 0xfd
+
+/* USB control protocol */
+
+#define K90_REQUEST_BRIGHTNESS 49
+#define K90_REQUEST_MACRO_MODE 2
+#define K90_REQUEST_STATUS 4
+#define K90_REQUEST_GET_MODE 5
+#define K90_REQUEST_PROFILE 20
+
+#define K90_MACRO_MODE_SW 0x0030
+#define K90_MACRO_MODE_HW 0x0001
+
+#define K90_MACRO_LED_ON 0x0020
+#define K90_MACRO_LED_OFF 0x0040
+
+/*
+ * LED class devices
+ */
+
+#define K90_BACKLIGHT_LED_SUFFIX "::backlight"
+#define K90_RECORD_LED_SUFFIX "::record"
+
+static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev)
+{
+ int ret;
+ struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);
+ struct device *dev = led->cdev.dev->parent;
+ struct usb_interface *usbif = to_usb_interface(dev->parent);
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+ int brightness;
+ char data[8];
+
+ ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
+ K90_REQUEST_STATUS,
+ USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, 0, 0, data, 8,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret < 0) {
+ dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
+ ret);
+ return -EIO;
+ }
+ brightness = data[4];
+ if (brightness < 0 || brightness > 3) {
+ dev_warn(dev,
+ "Read invalid backlight brightness: %02hhx.\n",
+ data[4]);
+ return -EIO;
+ }
+ return brightness;
+}
+
+static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev)
+{
+ struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);
+
+ return led->brightness;
+}
+
+static void k90_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);
+
+ led->brightness = brightness;
+ schedule_work(&led->work);
+}
+
+static void k90_backlight_work(struct work_struct *work)
+{
+ int ret;
+ struct k90_led *led = container_of(work, struct k90_led, work);
+ struct device *dev;
+ struct usb_interface *usbif;
+ struct usb_device *usbdev;
+
+ if (led->removed)
+ return;
+
+ dev = led->cdev.dev->parent;
+ usbif = to_usb_interface(dev->parent);
+ usbdev = interface_to_usbdev(usbif);
+
+ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+ K90_REQUEST_BRIGHTNESS,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, led->brightness, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (ret != 0)
+ dev_warn(dev, "Failed to set backlight brightness (error: %d).\n",
+ ret);
+}
+
+static void k90_record_led_work(struct work_struct *work)
+{
+ int ret;
+ struct k90_led *led = container_of(work, struct k90_led, work);
+ struct device *dev;
+ struct usb_interface *usbif;
+ struct usb_device *usbdev;
+ int value;
+
+ if (led->removed)
+ return;
+
+ dev = led->cdev.dev->parent;
+ usbif = to_usb_interface(dev->parent);
+ usbdev = interface_to_usbdev(usbif);
+
+ if (led->brightness > 0)
+ value = K90_MACRO_LED_ON;
+ else
+ value = K90_MACRO_LED_OFF;
+
+ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+ K90_REQUEST_MACRO_MODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, value, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret != 0)
+ dev_warn(dev, "Failed to set record LED state (error: %d).\n",
+ ret);
+}
+
+/*
+ * Keyboard attributes
+ */
+
+static ssize_t k90_show_macro_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct usb_interface *usbif = to_usb_interface(dev->parent);
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+ const char *macro_mode;
+ char data[8];
+
+ ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
+ K90_REQUEST_GET_MODE,
+ USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, 0, 0, data, 2,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret < 0) {
+ dev_warn(dev, "Failed to get K90 initial mode (error %d).\n",
+ ret);
+ return -EIO;
+ }
+
+ switch (data[0]) {
+ case K90_MACRO_MODE_HW:
+ macro_mode = "HW";
+ break;
+
+ case K90_MACRO_MODE_SW:
+ macro_mode = "SW";
+ break;
+ default:
+ dev_warn(dev, "K90 in unknown mode: %02hhx.\n",
+ data[0]);
+ return -EIO;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
+}
+
+static ssize_t k90_store_macro_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct usb_interface *usbif = to_usb_interface(dev->parent);
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+ __u16 value;
+
+ if (strncmp(buf, "SW", 2) == 0)
+ value = K90_MACRO_MODE_SW;
+ else if (strncmp(buf, "HW", 2) == 0)
+ value = K90_MACRO_MODE_HW;
+ else
+ return -EINVAL;
+
+ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+ K90_REQUEST_MACRO_MODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, value, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret != 0) {
+ dev_warn(dev, "Failed to set macro mode.\n");
+ return ret;
+ }
+
+ return count;
+}
+
+static ssize_t k90_show_current_profile(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct usb_interface *usbif = to_usb_interface(dev->parent);
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+ int current_profile;
+ char data[8];
+
+ ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
+ K90_REQUEST_STATUS,
+ USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, 0, 0, data, 8,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret < 0) {
+ dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
+ ret);
+ return -EIO;
+ }
+ current_profile = data[7];
+ if (current_profile < 1 || current_profile > 3) {
+ dev_warn(dev, "Read invalid current profile: %02hhx.\n",
+ data[7]);
+ return -EIO;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
+}
+
+static ssize_t k90_store_current_profile(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct usb_interface *usbif = to_usb_interface(dev->parent);
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+ int profile;
+
+ if (kstrtoint(buf, 10, &profile))
+ return -EINVAL;
+ if (profile < 1 || profile > 3)
+ return -EINVAL;
+
+ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+ K90_REQUEST_PROFILE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, profile, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret != 0) {
+ dev_warn(dev, "Failed to change current profile (error %d).\n",
+ ret);
+ return ret;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(macro_mode, 0644, k90_show_macro_mode, k90_store_macro_mode);
+static DEVICE_ATTR(current_profile, 0644, k90_show_current_profile,
+ k90_store_current_profile);
+
+static struct attribute *k90_attrs[] = {
+ &dev_attr_macro_mode.attr,
+ &dev_attr_current_profile.attr,
+ NULL
+};
+
+static const struct attribute_group k90_attr_group = {
+ .attrs = k90_attrs,
+};
+
+/*
+ * Driver functions
+ */
+
+static int k90_init_backlight(struct hid_device *dev)
+{
+ int ret;
+ struct corsair_drvdata *drvdata = hid_get_drvdata(dev);
+ size_t name_sz;
+ char *name;
+
+ drvdata->backlight = kzalloc(sizeof(struct k90_led), GFP_KERNEL);
+ if (!drvdata->backlight) {
+ ret = -ENOMEM;
+ goto fail_backlight_alloc;
+ }
+
+ name_sz =
+ strlen(dev_name(&dev->dev)) + sizeof(K90_BACKLIGHT_LED_SUFFIX);
+ name = kzalloc(name_sz, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto fail_name_alloc;
+ }
+ snprintf(name, name_sz, "%s" K90_BACKLIGHT_LED_SUFFIX,
+ dev_name(&dev->dev));
+ drvdata->backlight->removed = false;
+ drvdata->backlight->cdev.name = name;
+ drvdata->backlight->cdev.max_brightness = 3;
+ drvdata->backlight->cdev.brightness_set = k90_brightness_set;
+ drvdata->backlight->cdev.brightness_get = k90_backlight_get;
+ INIT_WORK(&drvdata->backlight->work, k90_backlight_work);
+ ret = led_classdev_register(&dev->dev, &drvdata->backlight->cdev);
+ if (ret != 0)
+ goto fail_register_cdev;
+
+ return 0;
+
+fail_register_cdev:
+ kfree(drvdata->backlight->cdev.name);
+fail_name_alloc:
+ kfree(drvdata->backlight);
+ drvdata->backlight = NULL;
+fail_backlight_alloc:
+ return ret;
+}
+
+static int k90_init_macro_functions(struct hid_device *dev)
+{
+ int ret;
+ struct corsair_drvdata *drvdata = hid_get_drvdata(dev);
+ struct k90_drvdata *k90;
+ size_t name_sz;
+ char *name;
+
+ k90 = kzalloc(sizeof(struct k90_drvdata), GFP_KERNEL);
+ if (!k90) {
+ ret = -ENOMEM;
+ goto fail_drvdata;
+ }
+ drvdata->k90 = k90;
+
+ /* Init LED device for record LED */
+ name_sz = strlen(dev_name(&dev->dev)) + sizeof(K90_RECORD_LED_SUFFIX);
+ name = kzalloc(name_sz, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto fail_record_led_alloc;
+ }
+ snprintf(name, name_sz, "%s" K90_RECORD_LED_SUFFIX,
+ dev_name(&dev->dev));
+ k90->record_led.removed = false;
+ k90->record_led.cdev.name = name;
+ k90->record_led.cdev.max_brightness = 1;
+ k90->record_led.cdev.brightness_set = k90_brightness_set;
+ k90->record_led.cdev.brightness_get = k90_record_led_get;
+ INIT_WORK(&k90->record_led.work, k90_record_led_work);
+ k90->record_led.brightness = 0;
+ ret = led_classdev_register(&dev->dev, &k90->record_led.cdev);
+ if (ret != 0)
+ goto fail_record_led;
+
+ /* Init attributes */
+ ret = sysfs_create_group(&dev->dev.kobj, &k90_attr_group);
+ if (ret != 0)
+ goto fail_sysfs;
+
+ return 0;
+
+fail_sysfs:
+ k90->record_led.removed = true;
+ led_classdev_unregister(&k90->record_led.cdev);
+ cancel_work_sync(&k90->record_led.work);
+fail_record_led:
+ kfree(k90->record_led.cdev.name);
+fail_record_led_alloc:
+ kfree(k90);
+fail_drvdata:
+ drvdata->k90 = NULL;
+ return ret;
+}
+
+static void k90_cleanup_backlight(struct hid_device *dev)
+{
+ struct corsair_drvdata *drvdata = hid_get_drvdata(dev);
+
+ if (drvdata->backlight) {
+ drvdata->backlight->removed = true;
+ led_classdev_unregister(&drvdata->backlight->cdev);
+ cancel_work_sync(&drvdata->backlight->work);
+ kfree(drvdata->backlight->cdev.name);
+ kfree(drvdata->backlight);
+ }
+}
+
+static void k90_cleanup_macro_functions(struct hid_device *dev)
+{
+ struct corsair_drvdata *drvdata = hid_get_drvdata(dev);
+ struct k90_drvdata *k90 = drvdata->k90;
+
+ if (k90) {
+ sysfs_remove_group(&dev->dev.kobj, &k90_attr_group);
+
+ k90->record_led.removed = true;
+ led_classdev_unregister(&k90->record_led.cdev);
+ cancel_work_sync(&k90->record_led.work);
+ kfree(k90->record_led.cdev.name);
+
+ kfree(k90);
+ }
+}
+
+static int corsair_probe(struct hid_device *dev, const struct hid_device_id *id)
+{
+ int ret;
+ unsigned long quirks = id->driver_data;
+ struct corsair_drvdata *drvdata;
+ struct usb_interface *usbif = to_usb_interface(dev->dev.parent);
+
+ drvdata = devm_kzalloc(&dev->dev, sizeof(struct corsair_drvdata),
+ GFP_KERNEL);
+ if (drvdata == NULL)
+ return -ENOMEM;
+ drvdata->quirks = quirks;
+ hid_set_drvdata(dev, drvdata);
+
+ ret = hid_parse(dev);
+ if (ret != 0) {
+ hid_err(dev, "parse failed\n");
+ return ret;
+ }
+ ret = hid_hw_start(dev, HID_CONNECT_DEFAULT);
+ if (ret != 0) {
+ hid_err(dev, "hw start failed\n");
+ return ret;
+ }
+
+ if (usbif->cur_altsetting->desc.bInterfaceNumber == 0) {
+ if (quirks & CORSAIR_USE_K90_MACRO) {
+ ret = k90_init_macro_functions(dev);
+ if (ret != 0)
+ hid_warn(dev, "Failed to initialize K90 macro functions.\n");
+ }
+ if (quirks & CORSAIR_USE_K90_BACKLIGHT) {
+ ret = k90_init_backlight(dev);
+ if (ret != 0)
+ hid_warn(dev, "Failed to initialize K90 backlight.\n");
+ }
+ }
+
+ return 0;
+}
+
+static void corsair_remove(struct hid_device *dev)
+{
+ k90_cleanup_macro_functions(dev);
+ k90_cleanup_backlight(dev);
+
+ hid_hw_stop(dev);
+}
+
+static int corsair_event(struct hid_device *dev, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ struct corsair_drvdata *drvdata = hid_get_drvdata(dev);
+
+ if (!drvdata->k90)
+ return 0;
+
+ switch (usage->hid & HID_USAGE) {
+ case CORSAIR_USAGE_MACRO_RECORD_START:
+ drvdata->k90->record_led.brightness = 1;
+ break;
+ case CORSAIR_USAGE_MACRO_RECORD_STOP:
+ drvdata->k90->record_led.brightness = 0;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int corsair_input_mapping(struct hid_device *dev,
+ struct hid_input *input,
+ struct hid_field *field,
+ struct hid_usage *usage, unsigned long **bit,
+ int *max)
+{
+ int gkey;
+
+ gkey = corsair_usage_to_gkey(usage->hid & HID_USAGE);
+ if (gkey != 0) {
+ hid_map_usage_clear(input, usage, bit, max, EV_KEY,
+ corsair_gkey_map[gkey - 1]);
+ return 1;
+ }
+ if ((usage->hid & HID_USAGE) >= CORSAIR_USAGE_SPECIAL_MIN &&
+ (usage->hid & HID_USAGE) <= CORSAIR_USAGE_SPECIAL_MAX) {
+ switch (usage->hid & HID_USAGE) {
+ case CORSAIR_USAGE_MACRO_RECORD_START:
+ hid_map_usage_clear(input, usage, bit, max, EV_KEY,
+ corsair_record_keycodes[0]);
+ return 1;
+
+ case CORSAIR_USAGE_MACRO_RECORD_STOP:
+ hid_map_usage_clear(input, usage, bit, max, EV_KEY,
+ corsair_record_keycodes[1]);
+ return 1;
+
+ case CORSAIR_USAGE_M1:
+ hid_map_usage_clear(input, usage, bit, max, EV_KEY,
+ corsair_profile_keycodes[0]);
+ return 1;
+
+ case CORSAIR_USAGE_M2:
+ hid_map_usage_clear(input, usage, bit, max, EV_KEY,
+ corsair_profile_keycodes[1]);
+ return 1;
+
+ case CORSAIR_USAGE_M3:
+ hid_map_usage_clear(input, usage, bit, max, EV_KEY,
+ corsair_profile_keycodes[2]);
+ return 1;
+
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static const struct hid_device_id corsair_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90),
+ .driver_data = CORSAIR_USE_K90_MACRO |
+ CORSAIR_USE_K90_BACKLIGHT },
+ {}
+};
+
+MODULE_DEVICE_TABLE(hid, corsair_devices);
+
+static struct hid_driver corsair_driver = {
+ .name = "corsair",
+ .id_table = corsair_devices,
+ .probe = corsair_probe,
+ .event = corsair_event,
+ .remove = corsair_remove,
+ .input_mapping = corsair_input_mapping,
+};
+
+static int __init corsair_init(void)
+{
+ return hid_register_driver(&corsair_driver);
+}
+
+static void corsair_exit(void)
+{
+ hid_unregister_driver(&corsair_driver);
+}
+
+module_init(corsair_init);
+module_exit(corsair_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Clement Vuchener");
+MODULE_DESCRIPTION("HID driver for Corsair devices");
diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c
index ce0644424f58..1d78ba3b799e 100644
--- a/drivers/hid/hid-dr.c
+++ b/drivers/hid/hid-dr.c
@@ -234,6 +234,58 @@ static __u8 pid0011_rdesc_fixed[] = {
0xC0 /* End Collection */
};
+static __u8 pid0006_rdesc_fixed[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x04, /* Usage (Joystick) */
+ 0xA1, 0x01, /* Collection (Application) */
+ 0xA1, 0x02, /* Collection (Logical) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x05, /* Report Count (5) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0xFF, 0x00, /* Physical Maximum (255) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x33, /* Usage (Ry) */
+ 0x09, 0x32, /* Usage (Z) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x09, 0x34, /* Usage (Ry) */
+ 0x81, 0x02, /* Input (Variable) */
+ 0x75, 0x04, /* Report Size (4) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x25, 0x07, /* Logical Maximum (7) */
+ 0x46, 0x3B, 0x01, /* Physical Maximum (315) */
+ 0x65, 0x14, /* Unit (Centimeter) */
+ 0x09, 0x39, /* Usage (Hat switch) */
+ 0x81, 0x42, /* Input (Variable) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x0C, /* Report Count (12) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x45, 0x01, /* Physical Maximum (1) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (0x01) */
+ 0x29, 0x0C, /* Usage Maximum (0x0C) */
+ 0x81, 0x02, /* Input (Variable) */
+ 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x08, /* Report Count (8) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x45, 0x01, /* Physical Maximum (1) */
+ 0x09, 0x01, /* Usage (0x01) */
+ 0x81, 0x02, /* Input (Variable) */
+ 0xC0, /* End Collection */
+ 0xA1, 0x02, /* Collection (Logical) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x07, /* Report Count (7) */
+ 0x46, 0xFF, 0x00, /* Physical Maximum (255) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x09, 0x02, /* Usage (0x02) */
+ 0x91, 0x02, /* Output (Variable) */
+ 0xC0, /* End Collection */
+ 0xC0 /* End Collection */
+};
+
static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
@@ -244,6 +296,12 @@ static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
*rsize = sizeof(pid0011_rdesc_fixed);
}
break;
+ case 0x0006:
+ if (*rsize == sizeof(pid0006_rdesc_fixed)) {
+ rdesc = pid0006_rdesc_fixed;
+ *rsize = sizeof(pid0006_rdesc_fixed);
+ }
+ break;
}
return rdesc;
}
diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c
index d0bd13b62dc2..6e3848a8d8dd 100644
--- a/drivers/hid/hid-elecom.c
+++ b/drivers/hid/hid-elecom.c
@@ -27,7 +27,7 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
hid_info(hdev, "Fixing up Elecom BM084 report descriptor\n");
rdesc[47] = 0x00;
}
- return rdesc;
+ return rdesc;
}
static const struct hid_device_id elecom_devices[] = {
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index f769208276ae..ac1feea51be3 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -251,6 +251,9 @@
#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500
#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff
+#define USB_VENDOR_ID_CORSAIR 0x1b1c
+#define USB_DEVICE_ID_CORSAIR_K90 0x1b02
+
#define USB_VENDOR_ID_CREATIVELABS 0x041e
#define USB_DEVICE_ID_PRODIKEYS_PCMIDI 0x2801
@@ -288,6 +291,7 @@
#define USB_DEVICE_ID_DMI_ENC 0x5fab
#define USB_VENDOR_ID_DRAGONRISE 0x0079
+#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800
#define USB_VENDOR_ID_DWAV 0x0eef
#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
@@ -510,6 +514,7 @@
#define USB_VENDOR_ID_ITE 0x048d
#define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386
+#define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350
#define USB_VENDOR_ID_JABRA 0x0b0e
#define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412
@@ -646,6 +651,7 @@
#define USB_VENDOR_ID_MADCATZ 0x0738
#define USB_DEVICE_ID_MADCATZ_BEATPAD 0x4540
+#define USB_DEVICE_ID_MADCATZ_RAT5 0x1705
#define USB_DEVICE_ID_MADCATZ_RAT9 0x1709
#define USB_VENDOR_ID_MCC 0x09db
@@ -679,6 +685,7 @@
#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_PRO_3 0x07dc
+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2
#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd
#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de
#define USB_DEVICE_ID_MS_POWER_COVER 0x07da
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index 5332fb7d072a..c20ac76c0a8c 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -620,6 +620,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
usage->code == ABS_Y || usage->code == ABS_Z ||
usage->code == ABS_RZ)) {
switch (hdev->product) {
+ case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
case USB_DEVICE_ID_LOGITECH_WHEEL:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
@@ -658,10 +659,18 @@ static int lg_event(struct hid_device *hdev, struct hid_field *field,
static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
+ struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
+ __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
unsigned int connect_mask = HID_CONNECT_DEFAULT;
struct lg_drv_data *drv_data;
int ret;
+ /* Only work with the 1st interface (G29 presents multiple) */
+ if (iface_num != 0) {
+ dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
+ return -ENODEV;
+ }
+
drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
if (!drv_data) {
hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index 02cec83caac3..fbddcb37ae98 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -45,7 +45,8 @@
#define LG4FF_MODE_G25_IDX 3
#define LG4FF_MODE_DFGT_IDX 4
#define LG4FF_MODE_G27_IDX 5
-#define LG4FF_MODE_MAX_IDX 6
+#define LG4FF_MODE_G29_IDX 6
+#define LG4FF_MODE_MAX_IDX 7
#define LG4FF_MODE_NATIVE BIT(LG4FF_MODE_NATIVE_IDX)
#define LG4FF_MODE_DFEX BIT(LG4FF_MODE_DFEX_IDX)
@@ -53,6 +54,7 @@
#define LG4FF_MODE_G25 BIT(LG4FF_MODE_G25_IDX)
#define LG4FF_MODE_DFGT BIT(LG4FF_MODE_DFGT_IDX)
#define LG4FF_MODE_G27 BIT(LG4FF_MODE_G27_IDX)
+#define LG4FF_MODE_G29 BIT(LG4FF_MODE_G29_IDX)
#define LG4FF_DFEX_TAG "DF-EX"
#define LG4FF_DFEX_NAME "Driving Force / Formula EX"
@@ -62,6 +64,8 @@
#define LG4FF_G25_NAME "G25 Racing Wheel"
#define LG4FF_G27_TAG "G27"
#define LG4FF_G27_NAME "G27 Racing Wheel"
+#define LG4FF_G29_TAG "G29"
+#define LG4FF_G29_NAME "G29 Racing Wheel"
#define LG4FF_DFGT_TAG "DFGT"
#define LG4FF_DFGT_NAME "Driving Force GT"
@@ -114,16 +118,12 @@ struct lg4ff_compat_mode_switch {
};
struct lg4ff_wheel_ident_info {
+ const u32 modes;
const u16 mask;
const u16 result;
const u16 real_product_id;
};
-struct lg4ff_wheel_ident_checklist {
- const u32 count;
- const struct lg4ff_wheel_ident_info *models[];
-};
-
struct lg4ff_multimode_wheel {
const u16 product_id;
const u32 alternate_modes;
@@ -144,6 +144,7 @@ static const struct lg4ff_wheel lg4ff_devices[] = {
{USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
{USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
{USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
+ {USB_DEVICE_ID_LOGITECH_G29_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
{USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL},
{USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}
};
@@ -161,6 +162,9 @@ static const struct lg4ff_multimode_wheel lg4ff_multimode_wheels[] = {
{USB_DEVICE_ID_LOGITECH_G27_WHEEL,
LG4FF_MODE_NATIVE | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
LG4FF_G27_TAG, LG4FF_G27_NAME},
+ {USB_DEVICE_ID_LOGITECH_G29_WHEEL,
+ LG4FF_MODE_NATIVE | LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
+ LG4FF_G29_TAG, LG4FF_G29_NAME},
};
static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = {
@@ -169,41 +173,61 @@ static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = {
[LG4FF_MODE_DFP_IDX] = {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, LG4FF_DFP_TAG, LG4FF_DFP_NAME},
[LG4FF_MODE_G25_IDX] = {USB_DEVICE_ID_LOGITECH_G25_WHEEL, LG4FF_G25_TAG, LG4FF_G25_NAME},
[LG4FF_MODE_DFGT_IDX] = {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, LG4FF_DFGT_TAG, LG4FF_DFGT_NAME},
- [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME}
+ [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME},
+ [LG4FF_MODE_G29_IDX] = {USB_DEVICE_ID_LOGITECH_G29_WHEEL, LG4FF_G29_TAG, LG4FF_G29_NAME},
};
/* Multimode wheel identificators */
static const struct lg4ff_wheel_ident_info lg4ff_dfp_ident_info = {
+ LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
0xf000,
0x1000,
USB_DEVICE_ID_LOGITECH_DFP_WHEEL
};
static const struct lg4ff_wheel_ident_info lg4ff_g25_ident_info = {
+ LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
0xff00,
0x1200,
USB_DEVICE_ID_LOGITECH_G25_WHEEL
};
static const struct lg4ff_wheel_ident_info lg4ff_g27_ident_info = {
+ LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
0xfff0,
0x1230,
USB_DEVICE_ID_LOGITECH_G27_WHEEL
};
static const struct lg4ff_wheel_ident_info lg4ff_dfgt_ident_info = {
+ LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
0xff00,
0x1300,
USB_DEVICE_ID_LOGITECH_DFGT_WHEEL
};
+static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info = {
+ LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
+ 0xfff8,
+ 0x1350,
+ USB_DEVICE_ID_LOGITECH_G29_WHEEL
+};
+
+static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info2 = {
+ LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
+ 0xff00,
+ 0x8900,
+ USB_DEVICE_ID_LOGITECH_G29_WHEEL
+};
+
/* Multimode wheel identification checklists */
-static const struct lg4ff_wheel_ident_checklist lg4ff_main_checklist = {
- 4,
- {&lg4ff_dfgt_ident_info,
- &lg4ff_g27_ident_info,
- &lg4ff_g25_ident_info,
- &lg4ff_dfp_ident_info}
+static const struct lg4ff_wheel_ident_info *lg4ff_main_checklist[] = {
+ &lg4ff_g29_ident_info,
+ &lg4ff_g29_ident_info2,
+ &lg4ff_dfgt_ident_info,
+ &lg4ff_g27_ident_info,
+ &lg4ff_g25_ident_info,
+ &lg4ff_dfp_ident_info
};
/* Compatibility mode switching commands */
@@ -238,6 +262,12 @@ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g27 = {
0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G27 with detach */
};
+static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g29 = {
+ 2,
+ {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
+ 0xf8, 0x09, 0x05, 0x01, 0x01, 0x00, 0x00} /* Switch mode to G29 with detach */
+};
+
/* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */
static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext01_dfp = {
1,
@@ -651,6 +681,23 @@ static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(cons
return NULL;
}
break;
+ case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
+ switch (target_product_id) {
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
+ return &lg4ff_mode_switch_ext09_dfp;
+ case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
+ return &lg4ff_mode_switch_ext09_dfgt;
+ case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
+ return &lg4ff_mode_switch_ext09_g25;
+ case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
+ return &lg4ff_mode_switch_ext09_g27;
+ case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
+ return &lg4ff_mode_switch_ext09_g29;
+ /* G29 can only be switched to DF-EX, DFP, DFGT, G25, G27 or its native mode */
+ default:
+ return NULL;
+ }
+ break;
case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
switch (target_product_id) {
case USB_DEVICE_ID_LOGITECH_WHEEL:
@@ -1037,41 +1084,28 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
static u16 lg4ff_identify_multimode_wheel(struct hid_device *hid, const u16 reported_product_id, const u16 bcdDevice)
{
- const struct lg4ff_wheel_ident_checklist *checklist;
- int i, from_idx, to_idx;
+ u32 current_mode;
+ int i;
- switch (reported_product_id) {
- case USB_DEVICE_ID_LOGITECH_WHEEL:
- case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
- checklist = &lg4ff_main_checklist;
- from_idx = 0;
- to_idx = checklist->count - 1;
- break;
- case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
- checklist = &lg4ff_main_checklist;
- from_idx = 0;
- to_idx = checklist->count - 2; /* End identity check at G25 */
- break;
- case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
- checklist = &lg4ff_main_checklist;
- from_idx = 1; /* Start identity check at G27 */
- to_idx = checklist->count - 3; /* End identity check at G27 */
- break;
- case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
- checklist = &lg4ff_main_checklist;
- from_idx = 0;
- to_idx = checklist->count - 4; /* End identity check at DFGT */
- break;
- default:
- return 0;
+ /* identify current mode from USB PID */
+ for (i = 1; i < ARRAY_SIZE(lg4ff_alternate_modes); i++) {
+ dbg_hid("Testing whether PID is %X\n", lg4ff_alternate_modes[i].product_id);
+ if (reported_product_id == lg4ff_alternate_modes[i].product_id)
+ break;
}
- for (i = from_idx; i <= to_idx; i++) {
- const u16 mask = checklist->models[i]->mask;
- const u16 result = checklist->models[i]->result;
- const u16 real_product_id = checklist->models[i]->real_product_id;
+ if (i == ARRAY_SIZE(lg4ff_alternate_modes))
+ return 0;
+
+ current_mode = BIT(i);
+
+ for (i = 0; i < ARRAY_SIZE(lg4ff_main_checklist); i++) {
+ const u16 mask = lg4ff_main_checklist[i]->mask;
+ const u16 result = lg4ff_main_checklist[i]->result;
+ const u16 real_product_id = lg4ff_main_checklist[i]->real_product_id;
- if ((bcdDevice & mask) == result) {
+ if ((current_mode & lg4ff_main_checklist[i]->modes) && \
+ (bcdDevice & mask) == result) {
dbg_hid("Found wheel with real PID %X whose reported PID is %X\n", real_product_id, reported_product_id);
return real_product_id;
}
@@ -1246,12 +1280,13 @@ int lg4ff_init(struct hid_device *hid)
entry->wdata.set_range(hid, entry->wdata.range);
#ifdef CONFIG_LEDS_CLASS
- /* register led subsystem - G27 only */
+ /* register led subsystem - G27/G29 only */
entry->wdata.led_state = 0;
for (j = 0; j < 5; j++)
entry->wdata.led[j] = NULL;
- if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL) {
+ if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL ||
+ lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G29_WHEEL) {
struct led_classdev *led;
size_t name_sz;
char *name;
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index a25f562f2d7b..5fd97860aec4 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -33,6 +33,11 @@ module_param(disable_raw_mode, bool, 0644);
MODULE_PARM_DESC(disable_raw_mode,
"Disable Raw mode reporting for touchpads and keep firmware gestures.");
+static bool disable_tap_to_click;
+module_param(disable_tap_to_click, bool, 0644);
+MODULE_PARM_DESC(disable_tap_to_click,
+ "Disable Tap-To-Click mode reporting for touchpads (only on the K400 currently).");
+
#define REPORT_ID_HIDPP_SHORT 0x10
#define REPORT_ID_HIDPP_LONG 0x11
@@ -41,10 +46,15 @@ MODULE_PARM_DESC(disable_raw_mode,
#define HIDPP_QUIRK_CLASS_WTP BIT(0)
#define HIDPP_QUIRK_CLASS_M560 BIT(1)
+#define HIDPP_QUIRK_CLASS_K400 BIT(2)
/* bits 2..20 are reserved for classes */
-#define HIDPP_QUIRK_DELAYED_INIT BIT(21)
+#define HIDPP_QUIRK_CONNECT_EVENTS BIT(21)
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
+#define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
+
+#define HIDPP_QUIRK_DELAYED_INIT (HIDPP_QUIRK_NO_HIDINPUT | \
+ HIDPP_QUIRK_CONNECT_EVENTS)
/*
* There are two hidpp protocols in use, the first version hidpp10 is known
@@ -553,6 +563,52 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp)
}
/* -------------------------------------------------------------------------- */
+/* 0x6010: Touchpad FW items */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_TOUCHPAD_FW_ITEMS 0x6010
+
+#define CMD_TOUCHPAD_FW_ITEMS_SET 0x10
+
+struct hidpp_touchpad_fw_items {
+ uint8_t presence;
+ uint8_t desired_state;
+ uint8_t state;
+ uint8_t persistent;
+};
+
+/**
+ * send a set state command to the device by reading the current items->state
+ * field. items is then filled with the current state.
+ */
+static int hidpp_touchpad_fw_items_set(struct hidpp_device *hidpp,
+ u8 feature_index,
+ struct hidpp_touchpad_fw_items *items)
+{
+ struct hidpp_report response;
+ int ret;
+ u8 *params = (u8 *)response.fap.params;
+
+ ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ CMD_TOUCHPAD_FW_ITEMS_SET, &items->state, 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;
+
+ items->presence = params[0];
+ items->desired_state = params[1];
+ items->state = params[2];
+ items->persistent = params[3];
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
/* 0x6100: TouchPadRawXY */
/* -------------------------------------------------------------------------- */
@@ -1132,6 +1188,75 @@ static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return -1;
}
+/* ------------------------------------------------------------------------- */
+/* Logitech K400 devices */
+/* ------------------------------------------------------------------------- */
+
+/*
+ * The Logitech K400 keyboard has an embedded touchpad which is seen
+ * as a mouse from the OS point of view. There is a hardware shortcut to disable
+ * tap-to-click but the setting is not remembered accross reset, annoying some
+ * users.
+ *
+ * We can toggle this feature from the host by using the feature 0x6010:
+ * Touchpad FW items
+ */
+
+struct k400_private_data {
+ u8 feature_index;
+};
+
+static int k400_disable_tap_to_click(struct hidpp_device *hidpp)
+{
+ struct k400_private_data *k400 = hidpp->private_data;
+ struct hidpp_touchpad_fw_items items = {};
+ int ret;
+ u8 feature_type;
+
+ if (!k400->feature_index) {
+ ret = hidpp_root_get_feature(hidpp,
+ HIDPP_PAGE_TOUCHPAD_FW_ITEMS,
+ &k400->feature_index, &feature_type);
+ if (ret)
+ /* means that the device is not powered up */
+ return ret;
+ }
+
+ ret = hidpp_touchpad_fw_items_set(hidpp, k400->feature_index, &items);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int k400_allocate(struct hid_device *hdev)
+{
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ struct k400_private_data *k400;
+
+ k400 = devm_kzalloc(&hdev->dev, sizeof(struct k400_private_data),
+ GFP_KERNEL);
+ if (!k400)
+ return -ENOMEM;
+
+ hidpp->private_data = k400;
+
+ return 0;
+};
+
+static int k400_connect(struct hid_device *hdev, bool connected)
+{
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+ if (!connected)
+ return 0;
+
+ if (!disable_tap_to_click)
+ return 0;
+
+ return k400_disable_tap_to_click(hidpp);
+}
+
/* -------------------------------------------------------------------------- */
/* Generic HID++ devices */
/* -------------------------------------------------------------------------- */
@@ -1205,7 +1330,7 @@ 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) &&
+ if ((hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) &&
(schedule_work(&hidpp->work) == 0))
dbg_hid("%s: connect event already queued\n", __func__);
return 1;
@@ -1330,23 +1455,30 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
ret = m560_send_config_command(hdev, connected);
if (ret)
return;
+ } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) {
+ ret = k400_connect(hdev, connected);
+ if (ret)
+ return;
}
if (!connected || hidpp->delayed_input)
return;
+ /* the device is already connected, we can ask for its name and
+ * protocol */
if (!hidpp->protocol_major) {
ret = !hidpp_is_connected(hidpp);
if (ret) {
hid_err(hdev, "Can not get the protocol version.\n");
return;
}
+ hid_info(hdev, "HID++ %u.%u device connected.\n",
+ hidpp->protocol_major, hidpp->protocol_minor);
}
- /* 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);
+ if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT))
+ /* if HID created the input nodes for us, we can stop now */
+ return;
if (!hidpp->name || hidpp->name == hdev->name) {
name = hidpp_get_device_name(hidpp);
@@ -1399,7 +1531,8 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (disable_raw_mode) {
hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP;
- hidpp->quirks &= ~HIDPP_QUIRK_DELAYED_INIT;
+ hidpp->quirks &= ~HIDPP_QUIRK_CONNECT_EVENTS;
+ hidpp->quirks &= ~HIDPP_QUIRK_NO_HIDINPUT;
}
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
@@ -1410,6 +1543,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
ret = m560_allocate(hdev);
if (ret)
goto allocate_fail;
+ } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) {
+ ret = k400_allocate(hdev);
+ if (ret)
+ goto allocate_fail;
}
INIT_WORK(&hidpp->work, delayed_work_cb);
@@ -1450,7 +1587,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
/* Block incoming packets */
hid_device_io_stop(hdev);
- if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
+ if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)
connect_mask &= ~HID_CONNECT_HIDINPUT;
ret = hid_hw_start(hdev, connect_mask);
@@ -1459,7 +1596,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto hid_hw_start_fail;
}
- if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) {
+ if (hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) {
/* Allow incoming packets */
hid_device_io_start(hdev);
@@ -1504,6 +1641,10 @@ static const struct hid_device_id hidpp_devices[] = {
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, 0x402d),
.driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
+ { /* Keyboard logitech K400 */
+ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+ USB_VENDOR_ID_LOGITECH, 0x4024),
+ .driver_data = HIDPP_QUIRK_CONNECT_EVENTS | HIDPP_QUIRK_CLASS_K400 },
{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 9aa3515090a7..77a2cf3e4afe 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -278,6 +278,8 @@ static const struct hid_device_id ms_devices[] = {
.driver_data = MS_DUPLICATE_USAGES },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3),
.driver_data = MS_HIDINPUT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2),
+ .driver_data = MS_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP),
.driver_data = MS_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3),
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 2ed42d8f805b..3d664d01305e 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -309,6 +309,41 @@ static struct attribute_group mt_attribute_group = {
.attrs = sysfs_attrs
};
+static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
+{
+ struct mt_device *td = hid_get_drvdata(hdev);
+ int ret, size = hid_report_len(report);
+ u8 *buf;
+
+ /*
+ * Only fetch the feature report if initial reports are not already
+ * been retrieved. Currently this is only done for Windows 8 touch
+ * devices.
+ */
+ if (!(hdev->quirks & HID_QUIRK_NO_INIT_REPORTS))
+ return;
+ if (td->mtclass.name != MT_CLS_WIN_8)
+ return;
+
+ buf = hid_alloc_report_buf(report, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ ret = hid_hw_raw_request(hdev, report->id, buf, size,
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+ if (ret < 0) {
+ dev_warn(&hdev->dev, "failed to fetch feature %d\n",
+ report->id);
+ } else {
+ ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
+ size, 0);
+ if (ret)
+ dev_warn(&hdev->dev, "failed to report feature\n");
+ }
+
+ kfree(buf);
+}
+
static void mt_feature_mapping(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage)
{
@@ -327,6 +362,8 @@ static void mt_feature_mapping(struct hid_device *hdev,
break;
case HID_DG_CONTACTMAX:
+ mt_get_feature(hdev, field->report);
+
td->maxcontact_report_id = field->report->id;
td->maxcontacts = field->value[0];
if (!td->maxcontacts &&
@@ -343,6 +380,7 @@ static void mt_feature_mapping(struct hid_device *hdev,
break;
}
+ mt_get_feature(hdev, field->report);
if (field->value[usage->usage_index] == MT_BUTTONTYPE_CLICKPAD)
td->is_buttonpad = true;
@@ -976,6 +1014,9 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
case HID_DG_TOUCHSCREEN:
/* we do not set suffix = "Touchscreen" */
break;
+ case HID_DG_TOUCHPAD:
+ suffix = "Touchpad";
+ break;
case HID_GD_SYSTEM_CONTROL:
suffix = "System Control";
break;
@@ -1036,8 +1077,13 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
* reports. Fortunately, the Win8 spec says that all touches
* should be sent during each report, making the initialization
* of input reports unnecessary.
+ *
+ * In addition some touchpads do not behave well if we read
+ * all feature reports from them. Instead we prevent
+ * initial report fetching and then selectively fetch each
+ * report we are interested in.
*/
- hdev->quirks |= HID_QUIRK_NO_INIT_INPUT_REPORTS;
+ hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL);
if (!td) {
diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c
index e3e98ccf137b..3a207c0ac0e3 100644
--- a/drivers/hid/hid-prodikeys.c
+++ b/drivers/hid/hid-prodikeys.c
@@ -427,7 +427,7 @@ static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data)
pm->midi_octave = 2;
dbg_hid("pcmidi mode: %d octave: %d\n",
pm->midi_mode, pm->midi_octave);
- continue;
+ continue;
} else
key = KEY_MESSENGER;
break;
@@ -695,7 +695,7 @@ static int pcmidi_snd_initialise(struct pcmidi_snd *pm)
if (err < 0) {
pk_error("failed to register pc-midi sound card: error %d\n",
err);
- goto fail_register;
+ goto fail_register;
}
dbg_hid("pcmidi_snd_initialise finished ok\n");
diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c
index a014f21275d8..2f84b26f1167 100644
--- a/drivers/hid/hid-saitek.c
+++ b/drivers/hid/hid-saitek.c
@@ -177,6 +177,8 @@ static int saitek_event(struct hid_device *hdev, struct hid_field *field,
static const struct hid_device_id saitek_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000),
.driver_data = SAITEK_FIX_PS1000 },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5),
+ .driver_data = SAITEK_RELEASE_MODE_RAT7 },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD),
.driver_data = SAITEK_RELEASE_MODE_RAT7 },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7),
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index a76eb2a0a987..92870cdb52d9 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -593,6 +593,20 @@ static __u8 *sensor_hub_report_fixup(struct hid_device *hdev, __u8 *rdesc,
}
}
+ /* Checks if the report descriptor of Thinkpad Helix 2 has a logical
+ * minimum for magnetic flux axis greater than the maximum */
+ if (hdev->product == USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA &&
+ *rsize == 2558 && rdesc[913] == 0x17 && rdesc[914] == 0x40 &&
+ rdesc[915] == 0x81 && rdesc[916] == 0x08 &&
+ rdesc[917] == 0x00 && rdesc[918] == 0x27 &&
+ rdesc[921] == 0x07 && rdesc[922] == 0x00) {
+ /* Sets negative logical minimum for mag x, y and z */
+ rdesc[914] = rdesc[935] = rdesc[956] = 0xc0;
+ rdesc[915] = rdesc[936] = rdesc[957] = 0x7e;
+ rdesc[916] = rdesc[937] = rdesc[958] = 0xf7;
+ rdesc[917] = rdesc[938] = rdesc[959] = 0xff;
+ }
+
return rdesc;
}
@@ -646,8 +660,8 @@ static int sensor_hub_probe(struct hid_device *hdev,
GFP_KERNEL);
if (sd->hid_sensor_hub_client_devs == NULL) {
hid_err(hdev, "Failed to allocate memory for mfd cells\n");
- ret = -ENOMEM;
- goto err_stop_hw;
+ ret = -ENOMEM;
+ goto err_stop_hw;
}
for (i = 0; i < hdev->maxcollection; ++i) {
@@ -684,8 +698,8 @@ static int sensor_hub_probe(struct hid_device *hdev,
collection->usage);
if (name == NULL) {
hid_err(hdev, "Failed MFD device name\n");
- ret = -ENOMEM;
- goto err_stop_hw;
+ ret = -ENOMEM;
+ goto err_stop_hw;
}
sd->hid_sensor_hub_client_devs[
sd->hid_sensor_client_cnt].name = name;
@@ -777,6 +791,9 @@ static const struct hid_device_id sensor_hub_devices[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE,
USB_DEVICE_ID_ITE_LENOVO_YOGA),
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+ { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE,
+ USB_DEVICE_ID_ITE_LENOVO_YOGA2),
+ .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID,
HID_ANY_ID) },
{ }
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index 2871f3c81a4c..10bd8e6e4c9c 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -1028,6 +1028,7 @@ static int i2c_hid_probe(struct i2c_client *client,
snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX",
client->name, hid->vendor, hid->product);
+ strlcpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys));
ret = hid_add_device(hid);
if (ret) {
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 1dff8f0015ba..94bb137abe32 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -71,6 +71,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT },
{ 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 },
@@ -91,6 +92,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS },
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 9a4912c1828d..e06af5b9f59e 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -211,7 +211,7 @@ static void wacom_usage_mapping(struct hid_device *hdev,
* Bamboo models do not support HID_DG_CONTACTMAX.
* And, Bamboo Pen only descriptor contains touch.
*/
- if (features->type != BAMBOO_PT) {
+ if (features->type > BAMBOO_PT) {
/* ISDv4 touch devices at least supports one touch point */
if (finger && !features->touch_max)
features->touch_max = 1;
@@ -222,7 +222,8 @@ static void wacom_usage_mapping(struct hid_device *hdev,
features->x_max = field->logical_maximum;
if (finger) {
features->x_phy = field->physical_maximum;
- if (features->type != BAMBOO_PT) {
+ if ((features->type != BAMBOO_PT) &&
+ (features->type != BAMBOO_TOUCH)) {
features->unit = field->unit;
features->unitExpo = field->unit_exponent;
}
@@ -232,7 +233,8 @@ static void wacom_usage_mapping(struct hid_device *hdev,
features->y_max = field->logical_maximum;
if (finger) {
features->y_phy = field->physical_maximum;
- if (features->type != BAMBOO_PT) {
+ if ((features->type != BAMBOO_PT) &&
+ (features->type != BAMBOO_TOUCH)) {
features->unit = field->unit;
features->unitExpo = field->unit_exponent;
}
@@ -420,7 +422,7 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
/* MT Tablet PC touch */
return wacom_set_device_mode(hdev, 3, 4, 4);
}
- else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID) {
+ else if (features->type == WACOM_24HDT) {
return wacom_set_device_mode(hdev, 18, 3, 2);
}
else if (features->type == WACOM_27QHDT) {
@@ -430,7 +432,7 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
return wacom_set_device_mode(hdev, 2, 2, 2);
}
} else if (features->device_type & WACOM_DEVICETYPE_PEN) {
- if (features->type <= BAMBOO_PT && features->type != WIRELESS) {
+ if (features->type <= BAMBOO_PT) {
return wacom_set_device_mode(hdev, 2, 2, 2);
}
}
@@ -1547,15 +1549,16 @@ static void wacom_wireless_work(struct work_struct *work)
wacom_wac1->features =
*((struct wacom_features *)id->driver_data);
wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PEN;
- if (wacom_wac1->features.type != INTUOSHT &&
- wacom_wac1->features.type != BAMBOO_PT)
- wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD;
wacom_set_default_phy(&wacom_wac1->features);
wacom_calculate_res(&wacom_wac1->features);
snprintf(wacom_wac1->pen_name, WACOM_NAME_MAX, "%s (WL) Pen",
wacom_wac1->features.name);
- snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad",
- wacom_wac1->features.name);
+ if (wacom_wac1->features.type < BAMBOO_PEN ||
+ wacom_wac1->features.type > BAMBOO_PT) {
+ snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad",
+ wacom_wac1->features.name);
+ wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD;
+ }
wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max;
wacom_wac1->shared->type = wacom_wac1->features.type;
wacom_wac1->pid = wacom_wac->pid;
@@ -1566,7 +1569,8 @@ static void wacom_wireless_work(struct work_struct *work)
/* Touch interface */
if (wacom_wac1->features.touch_max ||
- wacom_wac1->features.type == INTUOSHT) {
+ (wacom_wac1->features.type >= INTUOSHT &&
+ wacom_wac1->features.type <= BAMBOO_PT)) {
wacom_wac2->features =
*((struct wacom_features *)id->driver_data);
wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
@@ -1575,20 +1579,22 @@ static void wacom_wireless_work(struct work_struct *work)
wacom_calculate_res(&wacom_wac2->features);
snprintf(wacom_wac2->touch_name, WACOM_NAME_MAX,
"%s (WL) Finger",wacom_wac2->features.name);
- snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
- "%s (WL) Pad",wacom_wac2->features.name);
if (wacom_wac1->features.touch_max)
wacom_wac2->features.device_type |= WACOM_DEVICETYPE_TOUCH;
- if (wacom_wac1->features.type == INTUOSHT ||
- wacom_wac1->features.type == BAMBOO_PT)
+ if (wacom_wac1->features.type >= INTUOSHT &&
+ wacom_wac1->features.type <= BAMBOO_PT) {
+ snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
+ "%s (WL) Pad",wacom_wac2->features.name);
wacom_wac2->features.device_type |= WACOM_DEVICETYPE_PAD;
+ }
wacom_wac2->pid = wacom_wac->pid;
error = wacom_allocate_inputs(wacom2) ||
wacom_register_inputs(wacom2);
if (error)
goto fail;
- if (wacom_wac1->features.type == INTUOSHT &&
+ if ((wacom_wac1->features.type == INTUOSHT ||
+ wacom_wac1->features.type == INTUOSHT2) &&
wacom_wac1->features.touch_max)
wacom_wac->shared->touch_input = wacom_wac2->touch_input;
}
@@ -1812,11 +1818,27 @@ static int wacom_probe(struct hid_device *hdev,
/* Note that if query fails it is not a hard failure */
wacom_query_tablet_data(hdev, features);
+ /* touch only Bamboo doesn't support pen */
+ if ((features->type == BAMBOO_TOUCH) &&
+ (features->device_type & WACOM_DEVICETYPE_PEN)) {
+ error = -ENODEV;
+ goto fail_hw_start;
+ }
+
+ /* pen only Bamboo neither support touch nor pad */
+ if ((features->type == BAMBOO_PEN) &&
+ ((features->device_type & WACOM_DEVICETYPE_TOUCH) ||
+ (features->device_type & WACOM_DEVICETYPE_PAD))) {
+ error = -ENODEV;
+ goto fail_hw_start;
+ }
+
if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR)
error = hid_hw_open(hdev);
- if (wacom_wac->features.type == INTUOSHT &&
- wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) {
+ if ((wacom_wac->features.type == INTUOSHT ||
+ wacom_wac->features.type == INTUOSHT2) &&
+ (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)) {
wacom_wac->shared->touch_input = wacom_wac->touch_input;
}
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 0215ab62bb93..8b29949507d1 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -765,13 +765,15 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
/* general pen packet */
if ((data[1] & 0xb8) == 0xa0) {
t = (data[6] << 2) | ((data[7] >> 6) & 3);
- if (features->type >= INTUOS4S && features->type <= CINTIQ_HYBRID) {
+ if (features->pressure_max == 2047) {
t = (t << 1) | (data[1] & 1);
}
input_report_abs(input, ABS_PRESSURE, t);
- input_report_abs(input, ABS_TILT_X,
+ if (features->type != INTUOSHT2) {
+ input_report_abs(input, ABS_TILT_X,
(((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64);
- input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 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);
@@ -799,6 +801,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
data[0] != WACOM_REPORT_INTUOSREAD &&
data[0] != WACOM_REPORT_INTUOSWRITE &&
data[0] != WACOM_REPORT_INTUOSPAD &&
+ data[0] != WACOM_REPORT_INTUOS_PEN &&
data[0] != WACOM_REPORT_CINTIQ &&
data[0] != WACOM_REPORT_CINTIQPAD &&
data[0] != WACOM_REPORT_INTUOS5PAD) {
@@ -948,6 +951,27 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
} else {
input_report_abs(input, ABS_MISC, 0);
}
+
+ } else if (features->type == CINTIQ_COMPANION_2) {
+ input_report_key(input, BTN_1, (data[1] & 0x02));
+ input_report_key(input, BTN_2, (data[2] & 0x01));
+ input_report_key(input, BTN_3, (data[2] & 0x02));
+ input_report_key(input, BTN_4, (data[2] & 0x04));
+ input_report_key(input, BTN_5, (data[2] & 0x08));
+ input_report_key(input, BTN_6, (data[1] & 0x04));
+
+ input_report_key(input, BTN_7, (data[2] & 0x10)); /* Right */
+ input_report_key(input, BTN_8, (data[2] & 0x20)); /* Up */
+ input_report_key(input, BTN_9, (data[2] & 0x40)); /* Left */
+ input_report_key(input, BTN_A, (data[2] & 0x80)); /* Down */
+ input_report_key(input, BTN_0, (data[1] & 0x01)); /* Center */
+
+ if (data[2] | (data[1] & 0x07)) {
+ input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+ } else {
+ input_report_abs(input, ABS_MISC, 0);
+ }
+
} else if (features->type >= INTUOS5S && features->type <= INTUOSPL) {
int i;
@@ -1628,6 +1652,7 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0);
break;
case HID_DG_CONTACTCOUNT:
+ wacom_wac->hid_data.cc_report = field->report->id;
wacom_wac->hid_data.cc_index = field->index;
wacom_wac->hid_data.cc_value_index = usage->usage_index;
break;
@@ -1715,7 +1740,32 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev,
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct hid_data* hid_data = &wacom_wac->hid_data;
- if (hid_data->cc_index >= 0) {
+ if (hid_data->cc_report != 0 &&
+ hid_data->cc_report != report->id) {
+ int i;
+
+ hid_data->cc_report = report->id;
+ hid_data->cc_index = -1;
+ hid_data->cc_value_index = -1;
+
+ for (i = 0; i < report->maxfield; i++) {
+ struct hid_field *field = report->field[i];
+ int j;
+
+ for (j = 0; j < field->maxusage; j++) {
+ if (field->usage[j].hid == HID_DG_CONTACTCOUNT) {
+ hid_data->cc_index = i;
+ hid_data->cc_value_index = j;
+
+ /* break */
+ i = report->maxfield;
+ j = field->maxusage;
+ }
+ }
+ }
+ }
+ if (hid_data->cc_report != 0 &&
+ hid_data->cc_index >= 0) {
struct hid_field *field = report->field[hid_data->cc_index];
int value = field->value[hid_data->cc_value_index];
if (value)
@@ -1896,7 +1946,7 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
int y = (data[3] << 4) | (data[4] & 0x0f);
int width, height;
- if (features->type >= INTUOSPS && features->type <= INTUOSHT) {
+ if (features->type >= INTUOSPS && features->type <= INTUOSHT2) {
width = data[5] * 100;
height = data[6] * 100;
} else {
@@ -1924,7 +1974,7 @@ static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data)
struct input_dev *input = wacom->pad_input;
struct wacom_features *features = &wacom->features;
- if (features->type == INTUOSHT) {
+ if (features->type == INTUOSHT || features->type == INTUOSHT2) {
input_report_key(input, BTN_LEFT, (data[1] & 0x02) != 0);
input_report_key(input, BTN_BACK, (data[1] & 0x08) != 0);
} else {
@@ -1939,7 +1989,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
{
unsigned char *data = wacom->data;
int count = data[1] & 0x07;
- int i;
+ int touch_changed = 0, i;
if (data[0] != 0x02)
return 0;
@@ -1949,15 +1999,16 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
int offset = (8 * i) + 2;
int msg_id = data[offset];
- if (msg_id >= 2 && msg_id <= 17)
+ if (msg_id >= 2 && msg_id <= 17) {
wacom_bpt3_touch_msg(wacom, data + offset);
- else if (msg_id == 128)
+ touch_changed++;
+ } else if (msg_id == 128)
wacom_bpt3_button_msg(wacom, data + offset);
}
- /* only update the touch if we actually have a touchpad */
- if (wacom->touch_registered) {
+ /* only update touch if we actually have a touchpad and touch data changed */
+ if (wacom->touch_registered && touch_changed) {
input_mt_sync_frame(wacom->touch_input);
wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
}
@@ -2038,7 +2089,12 @@ static int wacom_bpt_pen(struct wacom_wac *wacom)
static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
{
- if (len == WACOM_PKGLEN_BBTOUCH)
+ struct wacom_features *features = &wacom->features;
+
+ if ((features->type == INTUOSHT2) &&
+ (features->device_type & WACOM_DEVICETYPE_PEN))
+ return wacom_intuos_irq(wacom);
+ else if (len == WACOM_PKGLEN_BBTOUCH)
return wacom_bpt_touch(wacom);
else if (len == WACOM_PKGLEN_BBTOUCH3)
return wacom_bpt3_touch(wacom);
@@ -2145,7 +2201,8 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
if (connected) {
int pid, battery, charging;
- if ((wacom->shared->type == INTUOSHT) &&
+ if ((wacom->shared->type == INTUOSHT ||
+ wacom->shared->type == INTUOSHT2) &&
wacom->shared->touch_input &&
wacom->shared->touch_max) {
input_report_switch(wacom->shared->touch_input,
@@ -2183,7 +2240,8 @@ static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len)
if (data[0] != WACOM_REPORT_USB)
return 0;
- if (features->type == INTUOSHT &&
+ if ((features->type == INTUOSHT ||
+ features->type == INTUOSHT2) &&
wacom_wac->shared->touch_input &&
features->touch_max) {
input_report_switch(wacom_wac->shared->touch_input,
@@ -2264,6 +2322,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
case WACOM_27QHD:
case DTK:
case CINTIQ_HYBRID:
+ case CINTIQ_COMPANION_2:
sync = wacom_intuos_irq(wacom_wac);
break;
@@ -2300,7 +2359,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
break;
case BAMBOO_PT:
+ case BAMBOO_PEN:
+ case BAMBOO_TOUCH:
case INTUOSHT:
+ case INTUOSHT2:
if (wacom_wac->data[0] == WACOM_REPORT_USB)
sync = wacom_status_irq(wacom_wac, len);
else
@@ -2337,22 +2399,31 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
}
}
-static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
+static void wacom_setup_basic_pro_pen(struct wacom_wac *wacom_wac)
{
struct input_dev *input_dev = wacom_wac->pen_input;
input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
- __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
- __set_bit(BTN_TOOL_BRUSH, input_dev->keybit);
- __set_bit(BTN_TOOL_PENCIL, input_dev->keybit);
- __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit);
__set_bit(BTN_STYLUS, input_dev->keybit);
__set_bit(BTN_STYLUS2, input_dev->keybit);
input_set_abs_params(input_dev, ABS_DISTANCE,
0, wacom_wac->features.distance_max, 0, 0);
+}
+
+static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
+{
+ struct input_dev *input_dev = wacom_wac->pen_input;
+
+ wacom_setup_basic_pro_pen(wacom_wac);
+
+ __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+ __set_bit(BTN_TOOL_BRUSH, input_dev->keybit);
+ __set_bit(BTN_TOOL_PENCIL, input_dev->keybit);
+ __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit);
+
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);
@@ -2387,9 +2458,8 @@ void wacom_setup_device_quirks(struct wacom *wacom)
/* The pen and pad share the same interface on most devices */
if (features->type == GRAPHIRE_BT || features->type == WACOM_G4 ||
- features->type == DTUS || features->type == WACOM_MO ||
- (features->type >= INTUOS3S && features->type <= WACOM_13HD &&
- features->type != INTUOSHT)) {
+ features->type == DTUS ||
+ (features->type >= INTUOS3S && features->type <= WACOM_MO)) {
if (features->device_type & WACOM_DEVICETYPE_PEN)
features->device_type |= WACOM_DEVICETYPE_PAD;
}
@@ -2406,12 +2476,12 @@ void wacom_setup_device_quirks(struct wacom *wacom)
* interface (PacketSize of WACOM_PKGLEN_BBTOUCH3), override the
* tablet values.
*/
- if ((features->type >= INTUOS5S && features->type <= INTUOSHT) ||
- (features->type == BAMBOO_PT)) {
+ if ((features->type >= INTUOS5S && features->type <= INTUOSPL) ||
+ (features->type >= INTUOSHT && features->type <= BAMBOO_PT)) {
if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
if (features->touch_max)
features->device_type |= WACOM_DEVICETYPE_TOUCH;
- if (features->type == BAMBOO_PT || features->type == INTUOSHT)
+ if (features->type >= INTUOSHT || features->type <= BAMBOO_PT)
features->device_type |= WACOM_DEVICETYPE_PAD;
features->x_max = 4096;
@@ -2520,6 +2590,7 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
case CINTIQ:
case WACOM_13HD:
case CINTIQ_HYBRID:
+ case CINTIQ_COMPANION_2:
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);
@@ -2598,16 +2669,22 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
case INTUOSHT:
case BAMBOO_PT:
- __clear_bit(ABS_MISC, input_dev->absbit);
-
+ case BAMBOO_PEN:
+ case INTUOSHT2:
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
- __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
- __set_bit(BTN_TOOL_PEN, input_dev->keybit);
- __set_bit(BTN_STYLUS, input_dev->keybit);
- __set_bit(BTN_STYLUS2, input_dev->keybit);
- input_set_abs_params(input_dev, ABS_DISTANCE, 0,
+
+ if (features->type == INTUOSHT2) {
+ wacom_setup_basic_pro_pen(wacom_wac);
+ } else {
+ __clear_bit(ABS_MISC, input_dev->absbit);
+ __set_bit(BTN_TOOL_PEN, input_dev->keybit);
+ __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+ __set_bit(BTN_STYLUS, input_dev->keybit);
+ __set_bit(BTN_STYLUS2, input_dev->keybit);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0,
features->distance_max,
0, 0);
+ }
break;
case BAMBOO_PAD:
__clear_bit(ABS_MISC, input_dev->absbit);
@@ -2688,11 +2765,13 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
break;
case INTUOSHT:
+ case INTUOSHT2:
input_dev->evbit[0] |= BIT_MASK(EV_SW);
__set_bit(SW_MUTE_DEVICE, input_dev->swbit);
/* fall through */
case BAMBOO_PT:
+ case BAMBOO_TOUCH:
if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
input_set_abs_params(input_dev,
ABS_MT_TOUCH_MAJOR,
@@ -2752,6 +2831,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
switch (features->type) {
case CINTIQ_HYBRID:
+ case CINTIQ_COMPANION_2:
case DTK:
case DTUS:
case GRAPHIRE_BT:
@@ -2845,6 +2925,8 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
case INTUOSHT:
case BAMBOO_PT:
+ case BAMBOO_TOUCH:
+ case INTUOSHT2:
__clear_bit(ABS_MISC, input_dev->absbit);
__set_bit(BTN_LEFT, input_dev->keybit);
@@ -3235,11 +3317,10 @@ static const struct wacom_features wacom_features_0x47 =
{ "Wacom Intuos2 6x8", 20320, 16240, 1023, 31,
INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x84 =
- { "Wacom Wireless Receiver", 0, 0, 0, 0,
- WIRELESS, 0, 0, .touch_max = 16 };
+ { "Wacom Wireless Receiver", .type = WIRELESS, .touch_max = 16 };
static const struct wacom_features wacom_features_0xD0 =
{ "Wacom Bamboo 2FG", 14720, 9200, 1023, 31,
- BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+ BAMBOO_TOUCH, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
static const struct wacom_features wacom_features_0xD1 =
{ "Wacom Bamboo 2FG 4x5", 14720, 9200, 1023, 31,
BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
@@ -3251,10 +3332,10 @@ static const struct wacom_features wacom_features_0xD3 =
BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
static const struct wacom_features wacom_features_0xD4 =
{ "Wacom Bamboo Pen", 14720, 9200, 1023, 31,
- BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xD5 =
{ "Wacom Bamboo Pen 6x8", 21648, 13700, 1023, 31,
- BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xD6 =
{ "Wacom BambooPT 2FG 4x5", 14720, 9200, 1023, 31,
BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
@@ -3281,7 +3362,7 @@ static const struct wacom_features wacom_features_0xDF =
BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16 };
static const struct wacom_features wacom_features_0x300 =
{ "Wacom Bamboo One S", 14720, 9225, 1023, 31,
- BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x301 =
{ "Wacom Bamboo One M", 21648, 13530, 1023, 31,
BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -3324,14 +3405,38 @@ static const struct wacom_features wacom_features_0x318 =
static const struct wacom_features wacom_features_0x319 =
{ "Wacom Wireless Bamboo PAD", 4095, 4095, /* Touch */
.type = BAMBOO_PAD, 35, 48, .touch_max = 4 };
+static const struct wacom_features wacom_features_0x325 =
+ { "Wacom ISDv5 325", 59552, 33848, 2047, 63,
+ CINTIQ_COMPANION_2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 11,
+ WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
+ .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x326 };
+static const struct wacom_features wacom_features_0x326 = /* Touch */
+ { "Wacom ISDv5 326", .type = HID_GENERIC, .oVid = USB_VENDOR_ID_WACOM,
+ .oPid = 0x325 };
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_0x331 =
- { "Wacom Express Key Remote", 0, 0, 0, 0,
- REMOTE, 0, 0, 18, .check_for_hid_type = true,
+ { "Wacom Express Key Remote", .type = REMOTE,
+ .numbered_buttons = 18, .check_for_hid_type = true,
.hid_type = HID_TYPE_USBNONE };
+static const struct wacom_features wacom_features_0x33B =
+ { "Wacom Intuos S 2", 15200, 9500, 2047, 63,
+ INTUOSHT2, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
+ .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
+static const struct wacom_features wacom_features_0x33C =
+ { "Wacom Intuos PT S 2", 15200, 9500, 2047, 63,
+ INTUOSHT2, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16,
+ .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
+static const struct wacom_features wacom_features_0x33D =
+ { "Wacom Intuos P M 2", 21600, 13500, 2047, 63,
+ INTUOSHT2, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
+ .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
+static const struct wacom_features wacom_features_0x33E =
+ { "Wacom Intuos PT M 2", 21600, 13500, 2047, 63,
+ INTUOSHT2, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16,
+ .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 };
@@ -3483,6 +3588,8 @@ const struct hid_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x318) },
{ USB_DEVICE_WACOM(0x319) },
{ USB_DEVICE_WACOM(0x323) },
+ { USB_DEVICE_WACOM(0x325) },
+ { USB_DEVICE_WACOM(0x326) },
{ USB_DEVICE_WACOM(0x32A) },
{ USB_DEVICE_WACOM(0x32B) },
{ USB_DEVICE_WACOM(0x32C) },
@@ -3491,6 +3598,10 @@ const struct hid_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x333) },
{ USB_DEVICE_WACOM(0x335) },
{ USB_DEVICE_WACOM(0x336) },
+ { USB_DEVICE_WACOM(0x33B) },
+ { USB_DEVICE_WACOM(0x33C) },
+ { USB_DEVICE_WACOM(0x33D) },
+ { USB_DEVICE_WACOM(0x33E) },
{ 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 1e270d401e18..877c24a5df94 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -68,6 +68,7 @@
#define WACOM_REPORT_BPAD_PEN 3
#define WACOM_REPORT_BPAD_TOUCH 16
#define WACOM_REPORT_DEVICE_LIST 16
+#define WACOM_REPORT_INTUOS_PEN 16
#define WACOM_REPORT_REMOTE 17
/* device quirks */
@@ -117,22 +118,26 @@ enum {
INTUOSPS,
INTUOSPM,
INTUOSPL,
- INTUOSHT,
WACOM_21UX2,
WACOM_22HD,
DTK,
WACOM_24HD,
WACOM_27QHD,
CINTIQ_HYBRID,
+ CINTIQ_COMPANION_2,
CINTIQ,
WACOM_BEE,
WACOM_13HD,
WACOM_MO,
- WIRELESS,
+ BAMBOO_PEN,
+ INTUOSHT,
+ INTUOSHT2,
+ BAMBOO_TOUCH,
BAMBOO_PT,
WACOM_24HDT,
WACOM_27QHDT,
BAMBOO_PAD,
+ WIRELESS,
REMOTE,
TABLETPC, /* add new TPC below */
TABLETPCE,
@@ -198,6 +203,7 @@ struct hid_data {
int width;
int height;
int id;
+ int cc_report;
int cc_index;
int cc_value_index;
int num_expected;