summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/hid
diff options
context:
space:
mode:
authorJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-11 10:41:07 +0300
committerJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-13 08:17:18 +0300
commite09b41010ba33a20a87472ee821fa407a5b8da36 (patch)
treed10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/hid
parentf93b97fd65072de626c074dbe099a1fff05ce060 (diff)
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page. During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/hid')
-rw-r--r--kernel/drivers/hid/Kconfig35
-rw-r--r--kernel/drivers/hid/Makefile9
-rw-r--r--kernel/drivers/hid/hid-apple.c6
-rw-r--r--kernel/drivers/hid/hid-appleir.c4
-rw-r--r--kernel/drivers/hid/hid-aureal.c3
-rw-r--r--kernel/drivers/hid/hid-chicony.c26
-rw-r--r--kernel/drivers/hid/hid-core.c83
-rw-r--r--kernel/drivers/hid/hid-corsair.c673
-rw-r--r--kernel/drivers/hid/hid-cp2112.c97
-rw-r--r--kernel/drivers/hid/hid-cypress.c6
-rw-r--r--kernel/drivers/hid/hid-dr.c58
-rw-r--r--kernel/drivers/hid/hid-elecom.c2
-rw-r--r--kernel/drivers/hid/hid-elo.c4
-rw-r--r--kernel/drivers/hid/hid-gembird.c116
-rw-r--r--kernel/drivers/hid/hid-gfrm.c159
-rw-r--r--kernel/drivers/hid/hid-ids.h67
-rw-r--r--kernel/drivers/hid/hid-input.c18
-rw-r--r--kernel/drivers/hid/hid-lenovo.c120
-rw-r--r--kernel/drivers/hid/hid-lg.c36
-rw-r--r--kernel/drivers/hid/hid-lg4ff.c583
-rw-r--r--kernel/drivers/hid/hid-lg4ff.h4
-rw-r--r--kernel/drivers/hid/hid-logitech-hidpp.c390
-rw-r--r--kernel/drivers/hid/hid-magicmouse.c8
-rw-r--r--kernel/drivers/hid/hid-microsoft.c8
-rw-r--r--kernel/drivers/hid/hid-multitouch.c98
-rw-r--r--kernel/drivers/hid/hid-ntrig.c6
-rw-r--r--kernel/drivers/hid/hid-picolcd_backlight.c3
-rw-r--r--kernel/drivers/hid/hid-picolcd_cir.c3
-rw-r--r--kernel/drivers/hid/hid-picolcd_lcd.c3
-rw-r--r--kernel/drivers/hid/hid-plantronics.c132
-rw-r--r--kernel/drivers/hid/hid-prodikeys.c7
-rw-r--r--kernel/drivers/hid/hid-rmi.c189
-rw-r--r--kernel/drivers/hid/hid-saitek.c2
-rw-r--r--kernel/drivers/hid/hid-sensor-hub.c28
-rw-r--r--kernel/drivers/hid/hid-sjoy.c3
-rw-r--r--kernel/drivers/hid/hid-sony.c409
-rw-r--r--kernel/drivers/hid/hid-uclogic.c6
-rw-r--r--kernel/drivers/hid/i2c-hid/i2c-hid.c36
-rw-r--r--kernel/drivers/hid/usbhid/hid-core.c7
-rw-r--r--kernel/drivers/hid/usbhid/hid-quirks.c27
-rw-r--r--kernel/drivers/hid/wacom.h13
-rw-r--r--kernel/drivers/hid/wacom_sys.c662
-rw-r--r--kernel/drivers/hid/wacom_wac.c1068
-rw-r--r--kernel/drivers/hid/wacom_wac.h52
44 files changed, 4215 insertions, 1054 deletions
diff --git a/kernel/drivers/hid/Kconfig b/kernel/drivers/hid/Kconfig
index 15338afdf..513a16cc6 100644
--- a/kernel/drivers/hid/Kconfig
+++ b/kernel/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
@@ -251,6 +261,18 @@ config HID_EZKEY
---help---
Support for Ezkey BTC 8193 keyboard.
+config HID_GEMBIRD
+ tristate "Gembird Joypad"
+ depends on HID
+ ---help---
+ Support for Gembird JPD-DualForce 2.
+
+config HID_GFRM
+ tristate "Google Fiber TV Box remote control support"
+ depends on HID
+ ---help---
+ Support for Google Fiber TV Box remote controls
+
config HID_HOLTEK
tristate "Holtek HID devices"
depends on USB_HID
@@ -480,6 +502,7 @@ config HID_MULTITOUCH
- Atmel panels
- Cando dual touch panels
- Chunghwa panels
+ - CJTouch panels
- CVTouch panels
- Cypress TrueTouch panels
- Elan Microelectronics touch panels
@@ -634,7 +657,12 @@ config HID_PLANTRONICS
tristate "Plantronics USB HID Driver"
depends on HID
---help---
- Provides HID support for Plantronics telephony devices.
+ Provides HID support for Plantronics USB audio devices.
+ Correctly maps vendor unique volume up/down HID usages to
+ KEY_VOLUMEUP and KEY_VOLUMEDOWN events and prevents core mapping
+ of other vendor unique HID usages to random mouse events.
+
+ Say M here if you may ever plug in a Plantronics USB audio device.
config HID_PRIMAX
tristate "Primax non-fully HID-compliant devices"
@@ -660,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/kernel/drivers/hid/Makefile b/kernel/drivers/hid/Makefile
index e4a21dfd7..00011fee0 100644
--- a/kernel/drivers/hid/Makefile
+++ b/kernel/drivers/hid/Makefile
@@ -24,11 +24,12 @@ obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
obj-$(CONFIG_HID_ACRUX) += hid-axff.o
obj-$(CONFIG_HID_APPLE) += hid-apple.o
obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o
-obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
+obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
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
@@ -36,6 +37,8 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
+obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o
+obj-$(CONFIG_HID_GFRM) += hid-gfrm.o
obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
@@ -46,12 +49,12 @@ obj-$(CONFIG_HID_ICADE) += hid-icade.o
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
obj-$(CONFIG_HID_KYE) += hid-kye.o
-obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
+obj-$(CONFIG_HID_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_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
diff --git a/kernel/drivers/hid/hid-apple.c b/kernel/drivers/hid/hid-apple.c
index f822fd2a1..884d82f91 100644
--- a/kernel/drivers/hid/hid-apple.c
+++ b/kernel/drivers/hid/hid-apple.c
@@ -546,6 +546,12 @@ static const struct hid_device_id apple_devices[] = {
.driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS),
.driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI),
+ .driver_data = APPLE_HAS_FN },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ISO),
+ .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS),
+ .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO),
diff --git a/kernel/drivers/hid/hid-appleir.c b/kernel/drivers/hid/hid-appleir.c
index 0e6a42d37..07cbc70f0 100644
--- a/kernel/drivers/hid/hid-appleir.c
+++ b/kernel/drivers/hid/hid-appleir.c
@@ -256,7 +256,7 @@ out:
return 0;
}
-static void appleir_input_configured(struct hid_device *hid,
+static int appleir_input_configured(struct hid_device *hid,
struct hid_input *hidinput)
{
struct input_dev *input_dev = hidinput->input;
@@ -275,6 +275,8 @@ static void appleir_input_configured(struct hid_device *hid,
for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++)
set_bit(appleir->keymap[i], input_dev->keybit);
clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ return 0;
}
static int appleir_input_mapping(struct hid_device *hid,
diff --git a/kernel/drivers/hid/hid-aureal.c b/kernel/drivers/hid/hid-aureal.c
index 340ba9d39..3280aff28 100644
--- a/kernel/drivers/hid/hid-aureal.c
+++ b/kernel/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/kernel/drivers/hid/hid-chicony.c b/kernel/drivers/hid/hid-chicony.c
index b613d5a79..bc3cec199 100644
--- a/kernel/drivers/hid/hid-chicony.c
+++ b/kernel/drivers/hid/hid-chicony.c
@@ -20,6 +20,7 @@
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/module.h>
+#include <linux/usb.h>
#include "hid-ids.h"
@@ -57,10 +58,34 @@ static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return 1;
}
+static __u8 *ch_switch12_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
+ /* Change usage maximum and logical maximum from 0x7fff to
+ * 0x2fff, so they don't exceed HID_MAX_USAGES */
+ switch (hdev->product) {
+ case USB_DEVICE_ID_CHICONY_ACER_SWITCH12:
+ if (*rsize >= 128 && rdesc[64] == 0xff && rdesc[65] == 0x7f
+ && rdesc[69] == 0xff && rdesc[70] == 0x7f) {
+ hid_info(hdev, "Fixing up report descriptor\n");
+ rdesc[65] = rdesc[70] = 0x2f;
+ }
+ break;
+ }
+
+ }
+ return rdesc;
+}
+
+
static const struct hid_device_id ch_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
{ 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) },
{ }
};
MODULE_DEVICE_TABLE(hid, ch_devices);
@@ -68,6 +93,7 @@ MODULE_DEVICE_TABLE(hid, ch_devices);
static struct hid_driver ch_driver = {
.name = "chicony",
.id_table = ch_devices,
+ .report_fixup = ch_switch12_report_fixup,
.input_mapping = ch_input_mapping,
};
module_hid_driver(ch_driver);
diff --git a/kernel/drivers/hid/hid-core.c b/kernel/drivers/hid/hid-core.c
index 722a92579..c6f7a694f 100644
--- a/kernel/drivers/hid/hid-core.c
+++ b/kernel/drivers/hid/hid-core.c
@@ -427,6 +427,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
{
__u32 data;
unsigned n;
+ __u32 count;
data = item_udata(item);
@@ -490,6 +491,24 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
if (item->size <= 2)
data = (parser->global.usage_page << 16) + data;
+ count = data - parser->local.usage_minimum;
+ if (count + parser->local.usage_index >= HID_MAX_USAGES) {
+ /*
+ * We do not warn if the name is not set, we are
+ * actually pre-scanning the device.
+ */
+ if (dev_name(&parser->device->dev))
+ hid_warn(parser->device,
+ "ignoring exceeding usage max\n");
+ data = HID_MAX_USAGES - parser->local.usage_index +
+ parser->local.usage_minimum - 1;
+ if (data <= 0) {
+ hid_err(parser->device,
+ "no more usage index available\n");
+ return -1;
+ }
+ }
+
for (n = parser->local.usage_minimum; n <= data; n++)
if (hid_add_usage(parser, n)) {
dbg_hid("hid_add_usage failed\n");
@@ -705,8 +724,11 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
hid->group = HID_GROUP_SENSOR_HUB;
if (hid->vendor == USB_VENDOR_ID_MICROSOFT &&
- (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 ||
- hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3_JP) &&
+ (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) &&
hid->group == HID_GROUP_MULTITOUCH)
hid->group = HID_GROUP_GENERIC;
@@ -1061,13 +1083,13 @@ static u32 s32ton(__s32 value, unsigned n)
* Search linux-kernel and linux-usb-devel archives for "hid-core extract".
*/
-static __u32 extract(const struct hid_device *hid, __u8 *report,
+__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
unsigned offset, unsigned n)
{
u64 x;
if (n > 32)
- hid_warn(hid, "extract() called with n (%d) > 32! (%s)\n",
+ hid_warn(hid, "hid_field_extract() called with n (%d) > 32! (%s)\n",
n, current->comm);
report += offset >> 3; /* adjust byte index */
@@ -1076,6 +1098,7 @@ static __u32 extract(const struct hid_device *hid, __u8 *report,
x = (x >> offset) & ((1ULL << n) - 1); /* extract bit field */
return (u32) x;
}
+EXPORT_SYMBOL_GPL(hid_field_extract);
/*
* "implement" : set bits in a little endian bit stream.
@@ -1221,9 +1244,9 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
for (n = 0; n < count; n++) {
value[n] = min < 0 ?
- snto32(extract(hid, data, offset + n * size, size),
- size) :
- extract(hid, data, offset + n * size, size);
+ snto32(hid_field_extract(hid, data, offset + n * size,
+ size), size) :
+ hid_field_extract(hid, data, offset + n * size, size);
/* Ignore report if ErrorRollOver */
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
@@ -1589,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;
@@ -1656,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>";
}
@@ -1780,6 +1806,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) },
@@ -1802,6 +1831,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS) },
{ 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) },
@@ -1818,6 +1849,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GEMBIRD, USB_DEVICE_ID_GEMBIRD_JPD_DUALFORCE2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) },
@@ -1851,6 +1883,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
@@ -1868,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) },
@@ -1899,8 +1933,11 @@ 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_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_TYPE_COVER_3_JP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER) },
{ 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) },
@@ -1951,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) },
@@ -1959,9 +1997,12 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ 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_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
@@ -1997,6 +2038,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_Q_PAD) },
@@ -2259,20 +2301,14 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) },
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0400) },
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0401) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) },
@@ -2399,14 +2435,6 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WTP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) },
#endif
- { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
- { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
- { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
- { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
- { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
- { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) },
- { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
- { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
{ }
@@ -2471,6 +2499,9 @@ static const struct hid_device_id hid_mouse_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
{ }
diff --git a/kernel/drivers/hid/hid-corsair.c b/kernel/drivers/hid/hid-corsair.c
new file mode 100644
index 000000000..bcefb9ebb
--- /dev/null
+++ b/kernel/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/kernel/drivers/hid/hid-cp2112.c b/kernel/drivers/hid/hid-cp2112.c
index 39bf74793..7afc3fcc1 100644
--- a/kernel/drivers/hid/hid-cp2112.c
+++ b/kernel/drivers/hid/hid-cp2112.c
@@ -156,6 +156,7 @@ struct cp2112_device {
wait_queue_head_t wait;
u8 read_data[61];
u8 read_length;
+ u8 hwversion;
int xfer_status;
atomic_t read_avail;
atomic_t xfer_avail;
@@ -446,6 +447,24 @@ static int cp2112_i2c_write_req(void *buf, u8 slave_address, u8 *data,
return data_length + 3;
}
+static int cp2112_i2c_write_read_req(void *buf, u8 slave_address,
+ u8 *addr, int addr_length,
+ int read_length)
+{
+ struct cp2112_write_read_req_report *report = buf;
+
+ if (read_length < 1 || read_length > 512 ||
+ addr_length > sizeof(report->target_address))
+ return -EINVAL;
+
+ report->report = CP2112_DATA_WRITE_READ_REQUEST;
+ report->slave_address = slave_address << 1;
+ report->length = cpu_to_be16(read_length);
+ report->target_address_length = addr_length;
+ memcpy(report->target_address, addr, addr_length);
+ return addr_length + 5;
+}
+
static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
{
@@ -453,26 +472,46 @@ static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
struct hid_device *hdev = dev->hdev;
u8 buf[64];
ssize_t count;
+ ssize_t read_length = 0;
+ u8 *read_buf = NULL;
unsigned int retries;
int ret;
hid_dbg(hdev, "I2C %d messages\n", num);
- if (num != 1) {
+ if (num == 1) {
+ if (msgs->flags & I2C_M_RD) {
+ hid_dbg(hdev, "I2C read %#04x len %d\n",
+ msgs->addr, msgs->len);
+ read_length = msgs->len;
+ read_buf = msgs->buf;
+ count = cp2112_read_req(buf, msgs->addr, msgs->len);
+ } else {
+ hid_dbg(hdev, "I2C write %#04x len %d\n",
+ msgs->addr, msgs->len);
+ count = cp2112_i2c_write_req(buf, msgs->addr,
+ msgs->buf, msgs->len);
+ }
+ if (count < 0)
+ return count;
+ } else if (dev->hwversion > 1 && /* no repeated start in rev 1 */
+ num == 2 &&
+ msgs[0].addr == msgs[1].addr &&
+ !(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD)) {
+ hid_dbg(hdev, "I2C write-read %#04x wlen %d rlen %d\n",
+ msgs[0].addr, msgs[0].len, msgs[1].len);
+ read_length = msgs[1].len;
+ read_buf = msgs[1].buf;
+ count = cp2112_i2c_write_read_req(buf, msgs[0].addr,
+ msgs[0].buf, msgs[0].len, msgs[1].len);
+ if (count < 0)
+ return count;
+ } else {
hid_err(hdev,
"Multi-message I2C transactions not supported\n");
return -EOPNOTSUPP;
}
- if (msgs->flags & I2C_M_RD)
- count = cp2112_read_req(buf, msgs->addr, msgs->len);
- else
- count = cp2112_i2c_write_req(buf, msgs->addr, msgs->buf,
- msgs->len);
-
- if (count < 0)
- return count;
-
ret = hid_hw_power(hdev, PM_HINT_FULLON);
if (ret < 0) {
hid_err(hdev, "power management error: %d\n", ret);
@@ -508,21 +547,34 @@ static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
goto power_normal;
}
- if (!(msgs->flags & I2C_M_RD))
- goto finish;
-
- ret = cp2112_read(dev, msgs->buf, msgs->len);
- if (ret < 0)
- goto power_normal;
- if (ret != msgs->len) {
- hid_warn(hdev, "short read: %d < %d\n", ret, msgs->len);
- ret = -EIO;
- goto power_normal;
+ for (count = 0; count < read_length;) {
+ ret = cp2112_read(dev, read_buf + count, read_length - count);
+ if (ret < 0)
+ goto power_normal;
+ if (ret == 0) {
+ hid_err(hdev, "read returned 0\n");
+ ret = -EIO;
+ goto power_normal;
+ }
+ count += ret;
+ if (count > read_length) {
+ /*
+ * The hardware returned too much data.
+ * This is mostly harmless because cp2112_read()
+ * has a limit check so didn't overrun our
+ * buffer. Nevertheless, we return an error
+ * because something is seriously wrong and
+ * it shouldn't go unnoticed.
+ */
+ hid_err(hdev, "long read: %d > %zd\n",
+ ret, read_length - count + ret);
+ ret = -EIO;
+ goto power_normal;
+ }
}
-finish:
/* return the number of transferred messages */
- ret = 1;
+ ret = num;
power_normal:
hid_hw_power(hdev, PM_HINT_NORMAL);
@@ -1030,6 +1082,7 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
dev->adap.dev.parent = &hdev->dev;
snprintf(dev->adap.name, sizeof(dev->adap.name),
"CP2112 SMBus Bridge on hiddev%d", hdev->minor);
+ dev->hwversion = buf[2];
init_waitqueue_head(&dev->wait);
hid_device_io_start(hdev);
diff --git a/kernel/drivers/hid/hid-cypress.c b/kernel/drivers/hid/hid-cypress.c
index c4ef3bc72..1b764d174 100644
--- a/kernel/drivers/hid/hid-cypress.c
+++ b/kernel/drivers/hid/hid-cypress.c
@@ -41,13 +41,9 @@ static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
for (i = 0; i < *rsize - 4; i++)
if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
- __u8 tmp;
-
rdesc[i] = 0x19;
rdesc[i + 2] = 0x29;
- tmp = rdesc[i + 3];
- rdesc[i + 3] = rdesc[i + 1];
- rdesc[i + 1] = tmp;
+ swap(rdesc[i + 3], rdesc[i + 1]);
}
return rdesc;
}
diff --git a/kernel/drivers/hid/hid-dr.c b/kernel/drivers/hid/hid-dr.c
index ce0644424..1d78ba3b7 100644
--- a/kernel/drivers/hid/hid-dr.c
+++ b/kernel/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/kernel/drivers/hid/hid-elecom.c b/kernel/drivers/hid/hid-elecom.c
index d0bd13b62..6e3848a8d 100644
--- a/kernel/drivers/hid/hid-elecom.c
+++ b/kernel/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/kernel/drivers/hid/hid-elo.c b/kernel/drivers/hid/hid-elo.c
index 4e4946287..aad8c162a 100644
--- a/kernel/drivers/hid/hid-elo.c
+++ b/kernel/drivers/hid/hid-elo.c
@@ -37,7 +37,7 @@ static bool use_fw_quirk = true;
module_param(use_fw_quirk, bool, S_IRUGO);
MODULE_PARM_DESC(use_fw_quirk, "Do periodic pokes for broken M firmwares (default = true)");
-static void elo_input_configured(struct hid_device *hdev,
+static int elo_input_configured(struct hid_device *hdev,
struct hid_input *hidinput)
{
struct input_dev *input = hidinput->input;
@@ -45,6 +45,8 @@ static void elo_input_configured(struct hid_device *hdev,
set_bit(BTN_TOUCH, input->keybit);
set_bit(ABS_PRESSURE, input->absbit);
input_set_abs_params(input, ABS_PRESSURE, 0, 256, 0, 0);
+
+ return 0;
}
static void elo_process_data(struct input_dev *input, const u8 *data, int size)
diff --git a/kernel/drivers/hid/hid-gembird.c b/kernel/drivers/hid/hid-gembird.c
new file mode 100644
index 000000000..e55e519f3
--- /dev/null
+++ b/kernel/drivers/hid/hid-gembird.c
@@ -0,0 +1,116 @@
+/*
+ * HID driver for Gembird Joypad, "PC Game Controller"
+ *
+ * Copyright (c) 2015 Red Hat, Inc
+ * Copyright (c) 2015 Benjamin Tissoires
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GEMBIRD_START_FAULTY_RDESC 8
+
+static const __u8 gembird_jpd_faulty_rdesc[] = {
+ 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, 0x31, /* Usage (Y) */
+ 0x09, 0x32, /* Usage (Z) */
+ 0x09, 0x32, /* Usage (Z) */
+ 0x09, 0x35, /* Usage (Rz) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+};
+
+/*
+ * we fix the report descriptor by:
+ * - marking the first Z axis as constant (so it is ignored by HID)
+ * - assign the original second Z to Rx
+ * - assign the original Rz to Ry
+ */
+static const __u8 gembird_jpd_fixed_rdesc[] = {
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x02, /* Report Count (2) */
+ 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, 0x31, /* Usage (Y) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x09, 0x32, /* Usage (Z) */
+ 0x81, 0x01, /* Input (Cnst,Arr,Abs) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0x09, 0x33, /* Usage (Rx) */
+ 0x09, 0x34, /* Usage (Ry) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+};
+
+static __u8 *gembird_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ __u8 *new_rdesc;
+ /* delta_size is > 0 */
+ size_t delta_size = sizeof(gembird_jpd_fixed_rdesc) -
+ sizeof(gembird_jpd_faulty_rdesc);
+ size_t new_size = *rsize + delta_size;
+
+ if (*rsize >= 31 && !memcmp(&rdesc[GEMBIRD_START_FAULTY_RDESC],
+ gembird_jpd_faulty_rdesc,
+ sizeof(gembird_jpd_faulty_rdesc))) {
+ new_rdesc = devm_kzalloc(&hdev->dev, new_size, GFP_KERNEL);
+ if (new_rdesc == NULL)
+ return rdesc;
+
+ dev_info(&hdev->dev,
+ "fixing Gembird JPD-DualForce 2 report descriptor.\n");
+
+ /* start by copying the end of the rdesc */
+ memcpy(new_rdesc + delta_size, rdesc, *rsize);
+
+ /* add the correct beginning */
+ memcpy(new_rdesc, rdesc, GEMBIRD_START_FAULTY_RDESC);
+
+ /* replace the faulty part with the fixed one */
+ memcpy(new_rdesc + GEMBIRD_START_FAULTY_RDESC,
+ gembird_jpd_fixed_rdesc,
+ sizeof(gembird_jpd_fixed_rdesc));
+
+ *rsize = new_size;
+ rdesc = new_rdesc;
+ }
+
+ return rdesc;
+}
+
+static const struct hid_device_id gembird_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_GEMBIRD,
+ USB_DEVICE_ID_GEMBIRD_JPD_DUALFORCE2) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, gembird_devices);
+
+static struct hid_driver gembird_driver = {
+ .name = "gembird",
+ .id_table = gembird_devices,
+ .report_fixup = gembird_report_fixup,
+};
+module_hid_driver(gembird_driver);
+
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
+MODULE_DESCRIPTION("HID Gembird joypad driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/hid/hid-gfrm.c b/kernel/drivers/hid/hid-gfrm.c
new file mode 100644
index 000000000..075b1c020
--- /dev/null
+++ b/kernel/drivers/hid/hid-gfrm.c
@@ -0,0 +1,159 @@
+/*
+ * HID driver for Google Fiber TV Box remote controls
+ *
+ * Copyright (c) 2014-2015 Google Inc.
+ *
+ * Author: Petri Gynther <pgynther@google.com>
+ *
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GFRM100 1 /* Google Fiber GFRM100 (Bluetooth classic) */
+#define GFRM200 2 /* Google Fiber GFRM200 (Bluetooth LE) */
+
+#define GFRM100_SEARCH_KEY_REPORT_ID 0xF7
+#define GFRM100_SEARCH_KEY_DOWN 0x0
+#define GFRM100_SEARCH_KEY_AUDIO_DATA 0x1
+#define GFRM100_SEARCH_KEY_UP 0x2
+
+static u8 search_key_dn[3] = {0x40, 0x21, 0x02};
+static u8 search_key_up[3] = {0x40, 0x00, 0x00};
+
+static int gfrm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ unsigned long hdev_type = (unsigned long) hid_get_drvdata(hdev);
+
+ if (hdev_type == GFRM100) {
+ if (usage->hid == (HID_UP_CONSUMER | 0x4)) {
+ /* Consumer.0004 -> KEY_INFO */
+ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_INFO);
+ return 1;
+ }
+
+ if (usage->hid == (HID_UP_CONSUMER | 0x41)) {
+ /* Consumer.0041 -> KEY_OK */
+ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_OK);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,
+ u8 *data, int size)
+{
+ unsigned long hdev_type = (unsigned long) hid_get_drvdata(hdev);
+ int ret = 0;
+
+ if (hdev_type != GFRM100)
+ return 0;
+
+ if (size < 2 || data[0] != GFRM100_SEARCH_KEY_REPORT_ID)
+ return 0;
+
+ /*
+ * Convert GFRM100 Search key reports into Consumer.0221 (Key.Search)
+ * reports. Ignore audio data.
+ */
+ switch (data[1]) {
+ case GFRM100_SEARCH_KEY_DOWN:
+ ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_dn,
+ sizeof(search_key_dn), 1);
+ break;
+
+ case GFRM100_SEARCH_KEY_AUDIO_DATA:
+ break;
+
+ case GFRM100_SEARCH_KEY_UP:
+ ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_up,
+ sizeof(search_key_up), 1);
+ break;
+
+ default:
+ break;
+ }
+
+ return (ret < 0) ? ret : -1;
+}
+
+static int gfrm_input_configured(struct hid_device *hid, struct hid_input *hidinput)
+{
+ /*
+ * Enable software autorepeat with:
+ * - repeat delay: 400 msec
+ * - repeat period: 100 msec
+ */
+ input_enable_softrepeat(hidinput->input, 400, 100);
+ return 0;
+}
+
+static int gfrm_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+
+ hid_set_drvdata(hdev, (void *) id->driver_data);
+
+ ret = hid_parse(hdev);
+ if (ret)
+ goto done;
+
+ if (id->driver_data == GFRM100) {
+ /*
+ * GFRM100 HID Report Descriptor does not describe the Search
+ * key reports. Thus, we need to add it manually here, so that
+ * those reports reach gfrm_raw_event() from hid_input_report().
+ */
+ if (!hid_register_report(hdev, HID_INPUT_REPORT,
+ GFRM100_SEARCH_KEY_REPORT_ID)) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+done:
+ return ret;
+}
+
+static void gfrm_remove(struct hid_device *hdev)
+{
+ hid_hw_stop(hdev);
+ hid_set_drvdata(hdev, NULL);
+}
+
+static const struct hid_device_id gfrm_devices[] = {
+ { HID_BLUETOOTH_DEVICE(0x58, 0x2000),
+ .driver_data = GFRM100 },
+ { HID_BLUETOOTH_DEVICE(0x471, 0x2210),
+ .driver_data = GFRM200 },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, gfrm_devices);
+
+static struct hid_driver gfrm_driver = {
+ .name = "gfrm",
+ .id_table = gfrm_devices,
+ .probe = gfrm_probe,
+ .remove = gfrm_remove,
+ .input_mapping = gfrm_input_mapping,
+ .raw_event = gfrm_raw_event,
+ .input_configured = gfrm_input_configured,
+};
+
+module_hid_driver(gfrm_driver);
+
+MODULE_AUTHOR("Petri Gynther <pgynther@google.com>");
+MODULE_DESCRIPTION("Google Fiber TV Box remote control driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/hid/hid-ids.h b/kernel/drivers/hid/hid-ids.h
index 7ce93d927..8b78a7f1f 100644
--- a/kernel/drivers/hid/hid-ids.h
+++ b/kernel/drivers/hid/hid-ids.h
@@ -142,6 +142,9 @@
#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290
#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291
#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292
+#define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI 0x0272
+#define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO 0x0273
+#define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS 0x0274
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
@@ -227,14 +230,20 @@
#define USB_DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418
#define USB_DEVICE_ID_CHICONY_MULTI_TOUCH 0xb19d
#define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618
+#define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE 0x1053
#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123
#define USB_DEVICE_ID_CHICONY_AK1D 0x1125
+#define USB_DEVICE_ID_CHICONY_ACER_SWITCH12 0x1421
#define USB_VENDOR_ID_CHUNGHWAT 0x2247
#define USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH 0x0001
#define USB_VENDOR_ID_CIDC 0x1677
+#define USB_VENDOR_ID_CJTOUCH 0x24b8
+#define USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0020 0x0020
+#define USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0040 0x0040
+
#define USB_VENDOR_ID_CMEDIA 0x0d8c
#define USB_DEVICE_ID_CM109 0x000e
@@ -242,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
@@ -279,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
@@ -303,11 +316,6 @@
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
#define USB_VENDOR_ID_ELAN 0x04f3
-#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
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
@@ -354,6 +362,9 @@
#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001
#define USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR 0x0002
+#define USB_VENDOR_ID_GEMBIRD 0x11ff
+#define USB_DEVICE_ID_GEMBIRD_JPD_DUALFORCE2 0x3331
+
#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100
@@ -363,16 +374,6 @@
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100
-#define USB_VENDOR_ID_GLAB 0x06c2
-#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
-#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
-#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
-#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044
-#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
-#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051
-#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
-#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058
-
#define USB_VENDOR_ID_GOODTOUCH 0x1aad
#define USB_DEVICE_ID_GOODTOUCH_000f 0x000f
@@ -506,6 +507,10 @@
#define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615
#define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070
+#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
#define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420
@@ -586,6 +591,7 @@
#define USB_DEVICE_ID_LENOVO_TPKBD 0x6009
#define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047
#define USB_DEVICE_ID_LENOVO_CBTKBD 0x6048
+#define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
#define USB_VENDOR_ID_LG 0x1fd2
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
@@ -598,6 +604,7 @@
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
#define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306
+#define USB_DEVICE_ID_LOGITECH_KEYBOARD_G710_PLUS 0xc24d
#define USB_DEVICE_ID_LOGITECH_MOUSE_C01A 0xc01a
#define USB_DEVICE_ID_LOGITECH_MOUSE_C05A 0xc05a
#define USB_DEVICE_ID_LOGITECH_MOUSE_C06A 0xc06a
@@ -607,6 +614,7 @@
#define USB_DEVICE_ID_LOGITECH_DUAL_ACTION 0xc216
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219
+#define USB_DEVICE_ID_LOGITECH_G29_WHEEL 0xc24f
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283
#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286
#define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287
@@ -639,6 +647,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
@@ -671,8 +680,11 @@
#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_DEVICE_ID_MS_TYPE_COVER_3_JP 0x07dd
+#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
#define USB_VENDOR_ID_MOJO 0x8282
#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
@@ -852,6 +864,7 @@
#define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
#define USB_DEVICE_ID_SONY_PS4_CONTROLLER 0x05c4
+#define USB_DEVICE_ID_SONY_MOTION_CONTROLLER 0x03d5
#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000
@@ -928,7 +941,8 @@
#define USB_DEVICE_ID_TOUCHPACK_RTS 0x1688
#define USB_VENDOR_ID_TPV 0x25aa
-#define USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN 0x8883
+#define USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8882 0x8882
+#define USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8883 0x8883
#define USB_VENDOR_ID_TURBOX 0x062a
#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201
@@ -958,13 +972,6 @@
#define USB_DEVICE_ID_VELLEMAN_K8061_FIRST 0x8061
#define USB_DEVICE_ID_VELLEMAN_K8061_LAST 0x8068
-#define USB_VENDOR_ID_VERNIER 0x08f7
-#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
-#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
-#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
-#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
@@ -983,9 +990,6 @@
#define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
-#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
-#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
-#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
#define USB_DEVICE_ID_SUPER_JOY_BOX_3 0x8888
#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800
#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
@@ -1039,4 +1043,11 @@
#define USB_VENDOR_ID_RISO_KAGAKU 0x1294 /* Riso Kagaku Corp. */
#define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320 /* Webmail Notifier */
+#define USB_VENDOR_ID_MULTIPLE_1781 0x1781
+#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD 0x0a8d
+
+#define USB_VENDOR_ID_DRACAL_RAPHNET 0x289b
+#define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002
+#define USB_DEVICE_ID_RAPHNET_4NES4SNES 0x0003
+
#endif
diff --git a/kernel/drivers/hid/hid-input.c b/kernel/drivers/hid/hid-input.c
index 32d52d29c..2ba6bf69b 100644
--- a/kernel/drivers/hid/hid-input.c
+++ b/kernel/drivers/hid/hid-input.c
@@ -1160,13 +1160,17 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
return;
/* report the usage code as scancode if the key status has changed */
- if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
+ if (usage->type == EV_KEY &&
+ (!test_bit(usage->code, input->key)) == value)
input_event(input, EV_MSC, MSC_SCAN, usage->hid);
input_event(input, usage->type, usage->code, value);
- if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
+ if ((field->flags & HID_MAIN_ITEM_RELATIVE) &&
+ usage->type == EV_KEY && value) {
+ input_sync(input);
input_event(input, usage->type, usage->code, 0);
+ }
}
void hidinput_report_event(struct hid_device *hid, struct hid_report *report)
@@ -1506,8 +1510,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
* UGCI) cram a lot of unrelated inputs into the
* same interface. */
hidinput->report = report;
- if (drv->input_configured)
- drv->input_configured(hid, hidinput);
+ if (drv->input_configured &&
+ drv->input_configured(hid, hidinput))
+ goto out_cleanup;
if (input_register_device(hidinput->input))
goto out_cleanup;
hidinput = NULL;
@@ -1528,8 +1533,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
}
if (hidinput) {
- if (drv->input_configured)
- drv->input_configured(hid, hidinput);
+ if (drv->input_configured &&
+ drv->input_configured(hid, hidinput))
+ goto out_cleanup;
if (input_register_device(hidinput->input))
goto out_cleanup;
}
diff --git a/kernel/drivers/hid/hid-lenovo.c b/kernel/drivers/hid/hid-lenovo.c
index c4c3f0952..8979f1fd5 100644
--- a/kernel/drivers/hid/hid-lenovo.c
+++ b/kernel/drivers/hid/hid-lenovo.c
@@ -37,12 +37,42 @@ struct lenovo_drvdata_tpkbd {
};
struct lenovo_drvdata_cptkbd {
+ u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */
bool fn_lock;
int sensitivity;
};
#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
+static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
+ 0x05, 0x88, /* Usage Page (Vendor Usage Page 0x88) */
+ 0x09, 0x01, /* Usage (Vendor Usage 0x01) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x85, 0x04, /* Report ID (4) */
+ 0x19, 0x00, /* Usage Minimum (0) */
+ 0x2a, 0xff, 0xff, /* Usage Maximum (65535) */
+};
+
+static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ switch (hdev->product) {
+ case USB_DEVICE_ID_LENOVO_TPPRODOCK:
+ /* the fixups that need to be done:
+ * - get a reasonable usage max for the vendor collection
+ * 0x8801 from the report ID 4
+ */
+ if (*rsize >= 153 &&
+ memcmp(&rdesc[140], lenovo_pro_dock_need_fixup_collection,
+ sizeof(lenovo_pro_dock_need_fixup_collection)) == 0) {
+ rdesc[151] = 0x01;
+ rdesc[152] = 0x00;
+ }
+ break;
+ }
+ return rdesc;
+}
+
static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
struct hid_input *hi, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max)
@@ -117,10 +147,10 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev,
switch (usage->hid & HID_USAGE) {
case 0x0000:
- hid_map_usage(hi, usage, bit, max, EV_REL, 0x06);
+ hid_map_usage(hi, usage, bit, max, EV_REL, REL_HWHEEL);
return 1;
case 0x0001:
- hid_map_usage(hi, usage, bit, max, EV_REL, 0x08);
+ hid_map_usage(hi, usage, bit, max, EV_REL, REL_WHEEL);
return 1;
default:
return -1;
@@ -178,9 +208,12 @@ static void lenovo_features_set_cptkbd(struct hid_device *hdev)
struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock);
- ret = lenovo_send_cmd_cptkbd(hdev, 0x02, cptkbd_data->sensitivity);
if (ret)
hid_err(hdev, "Fn-lock setting failed: %d\n", ret);
+
+ ret = lenovo_send_cmd_cptkbd(hdev, 0x02, cptkbd_data->sensitivity);
+ if (ret)
+ hid_err(hdev, "Sensitivity setting failed: %d\n", ret);
}
static ssize_t attr_fn_lock_show_cptkbd(struct device *dev,
@@ -284,6 +317,53 @@ static int lenovo_raw_event(struct hid_device *hdev,
return 0;
}
+static int lenovo_event_cptkbd(struct hid_device *hdev,
+ struct hid_field *field, struct hid_usage *usage, __s32 value)
+{
+ struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
+
+ /* "wheel" scroll events */
+ if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
+ usage->code == REL_HWHEEL)) {
+ /* Scroll events disable middle-click event */
+ cptkbd_data->middlebutton_state = 2;
+ return 0;
+ }
+
+ /* Middle click events */
+ if (usage->type == EV_KEY && usage->code == BTN_MIDDLE) {
+ if (value == 1) {
+ cptkbd_data->middlebutton_state = 1;
+ } else if (value == 0) {
+ if (cptkbd_data->middlebutton_state == 1) {
+ /* No scrolling inbetween, send middle-click */
+ input_event(field->hidinput->input,
+ EV_KEY, BTN_MIDDLE, 1);
+ input_sync(field->hidinput->input);
+ input_event(field->hidinput->input,
+ EV_KEY, BTN_MIDDLE, 0);
+ input_sync(field->hidinput->input);
+ }
+ cptkbd_data->middlebutton_state = 0;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ switch (hdev->product) {
+ case USB_DEVICE_ID_LENOVO_CUSBKBD:
+ case USB_DEVICE_ID_LENOVO_CBTKBD:
+ return lenovo_event_cptkbd(hdev, field, usage, value);
+ default:
+ return 0;
+ }
+}
+
static int lenovo_features_set_tpkbd(struct hid_device *hdev)
{
struct hid_report *report;
@@ -599,7 +679,8 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
GFP_KERNEL);
if (data_pointer == NULL) {
hid_err(hdev, "Could not allocate memory for driver data\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err;
}
// set same default values as windows driver
@@ -610,7 +691,8 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
if (name_mute == NULL || name_micmute == NULL) {
hid_err(hdev, "Could not allocate memory for led data\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err;
}
snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
@@ -634,6 +716,9 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
lenovo_features_set_tpkbd(hdev);
return 0;
+err:
+ sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd);
+ return ret;
}
static int lenovo_probe_cptkbd(struct hid_device *hdev)
@@ -671,6 +756,7 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev)
hid_warn(hdev, "Failed to switch middle button: %d\n", ret);
/* Set keyboard settings to known state */
+ cptkbd_data->middlebutton_state = 0;
cptkbd_data->fn_lock = true;
cptkbd_data->sensitivity = 0x05;
lenovo_features_set_cptkbd(hdev);
@@ -762,10 +848,31 @@ static void lenovo_remove(struct hid_device *hdev)
hid_hw_stop(hdev);
}
+static int lenovo_input_configured(struct hid_device *hdev,
+ struct hid_input *hi)
+{
+ switch (hdev->product) {
+ case USB_DEVICE_ID_LENOVO_TPKBD:
+ case USB_DEVICE_ID_LENOVO_CUSBKBD:
+ case USB_DEVICE_ID_LENOVO_CBTKBD:
+ if (test_bit(EV_REL, hi->input->evbit)) {
+ /* set only for trackpoint device */
+ __set_bit(INPUT_PROP_POINTER, hi->input->propbit);
+ __set_bit(INPUT_PROP_POINTING_STICK,
+ hi->input->propbit);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+
static const struct hid_device_id lenovo_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
{ }
};
@@ -774,10 +881,13 @@ MODULE_DEVICE_TABLE(hid, lenovo_devices);
static struct hid_driver lenovo_driver = {
.name = "lenovo",
.id_table = lenovo_devices,
+ .input_configured = lenovo_input_configured,
.input_mapping = lenovo_input_mapping,
.probe = lenovo_probe,
.remove = lenovo_remove,
.raw_event = lenovo_raw_event,
+ .event = lenovo_event,
+ .report_fixup = lenovo_report_fixup,
};
module_hid_driver(lenovo_driver);
diff --git a/kernel/drivers/hid/hid-lg.c b/kernel/drivers/hid/hid-lg.c
index b86c18e65..c690fae02 100644
--- a/kernel/drivers/hid/hid-lg.c
+++ b/kernel/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,19 @@ 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;
+ /* G29 only work with the 1st interface */
+ if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
+ (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");
@@ -700,7 +710,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
/* insert a little delay of 10 jiffies ~ 40ms */
wait_queue_head_t wait;
init_waitqueue_head (&wait);
- wait_event_interruptible_timeout(wait, 0, 10);
+ wait_event_interruptible_timeout(wait, 0,
+ msecs_to_jiffies(40));
/* Select random Address */
buf[1] = 0xB2;
@@ -712,13 +723,16 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
if (drv_data->quirks & LG_FF)
- lgff_init(hdev);
- if (drv_data->quirks & LG_FF2)
- lg2ff_init(hdev);
- if (drv_data->quirks & LG_FF3)
- lg3ff_init(hdev);
- if (drv_data->quirks & LG_FF4)
- lg4ff_init(hdev);
+ ret = lgff_init(hdev);
+ else if (drv_data->quirks & LG_FF2)
+ ret = lg2ff_init(hdev);
+ else if (drv_data->quirks & LG_FF3)
+ ret = lg3ff_init(hdev);
+ else if (drv_data->quirks & LG_FF4)
+ ret = lg4ff_init(hdev);
+
+ if (ret)
+ goto err_free;
return 0;
err_free:
@@ -731,8 +745,8 @@ static void lg_remove(struct hid_device *hdev)
struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if (drv_data->quirks & LG_FF4)
lg4ff_deinit(hdev);
-
- hid_hw_stop(hdev);
+ else
+ hid_hw_stop(hdev);
kfree(drv_data);
}
@@ -772,6 +786,8 @@ static const struct hid_device_id lg_devices[] = {
.driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
.driver_data = LG_FF },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL),
+ .driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
.driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
diff --git a/kernel/drivers/hid/hid-lg4ff.c b/kernel/drivers/hid/hid-lg4ff.c
index 1232210b1..fbddcb37a 100644
--- a/kernel/drivers/hid/hid-lg4ff.c
+++ b/kernel/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,32 +64,40 @@
#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"
#define LG4FF_FFEX_REV_MAJ 0x21
#define LG4FF_FFEX_REV_MIN 0x00
-static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
-static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
+static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
+static void lg4ff_set_range_g25(struct hid_device *hid, u16 range);
-struct lg4ff_device_entry {
- __u32 product_id;
- __u16 range;
- __u16 min_range;
- __u16 max_range;
+struct lg4ff_wheel_data {
+ const u32 product_id;
+ u16 range;
+ const u16 min_range;
+ const u16 max_range;
#ifdef CONFIG_LEDS_CLASS
- __u8 led_state;
+ u8 led_state;
struct led_classdev *led[5];
#endif
- u32 alternate_modes;
- const char *real_tag;
- const char *real_name;
- u16 real_product_id;
- struct list_head list;
+ const u32 alternate_modes;
+ const char * const real_tag;
+ const char * const real_name;
+ const u16 real_product_id;
+
void (*set_range)(struct hid_device *hid, u16 range);
};
+struct lg4ff_device_entry {
+ spinlock_t report_lock; /* Protect output HID report */
+ struct hid_report *report;
+ struct lg4ff_wheel_data wdata;
+};
+
static const signed short lg4ff_wheel_effects[] = {
FF_CONSTANT,
FF_AUTOCENTER,
@@ -95,29 +105,25 @@ static const signed short lg4ff_wheel_effects[] = {
};
struct lg4ff_wheel {
- const __u32 product_id;
+ const u32 product_id;
const signed short *ff_effects;
- const __u16 min_range;
- const __u16 max_range;
+ const u16 min_range;
+ const u16 max_range;
void (*set_range)(struct hid_device *hid, u16 range);
};
struct lg4ff_compat_mode_switch {
- const __u8 cmd_count; /* Number of commands to send */
- const __u8 cmd[];
+ const u8 cmd_count; /* Number of commands to send */
+ const u8 cmd[];
};
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;
@@ -134,10 +140,11 @@ struct lg4ff_alternate_mode {
static const struct lg4ff_wheel lg4ff_devices[] = {
{USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
{USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
- {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_dfp},
- {USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
- {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
- {USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
+ {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_dfp},
+ {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}
};
@@ -155,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[] = {
@@ -163,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 */
@@ -232,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,
@@ -245,10 +281,10 @@ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = {
};
/* Recalculates X axis value accordingly to currently selected range */
-static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
+static s32 lg4ff_adjust_dfp_x_axis(s32 value, u16 range)
{
- __u16 max_range;
- __s32 new_value;
+ u16 max_range;
+ s32 new_value;
if (range == 900)
return value;
@@ -269,21 +305,21 @@ static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
}
int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
- struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data)
+ struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data)
{
struct lg4ff_device_entry *entry = drv_data->device_props;
- __s32 new_value = 0;
+ s32 new_value = 0;
if (!entry) {
hid_err(hid, "Device properties not found");
return 0;
}
- switch (entry->product_id) {
+ switch (entry->wdata.product_id) {
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
switch (usage->code) {
case ABS_X:
- new_value = lg4ff_adjust_dfp_x_axis(value, entry->range);
+ new_value = lg4ff_adjust_dfp_x_axis(value, entry->wdata.range);
input_event(field->hidinput->input, usage->type, usage->code, new_value);
return 1;
default:
@@ -294,14 +330,56 @@ int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
}
}
-static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const struct lg4ff_wheel *wheel,
+ const struct lg4ff_multimode_wheel *mmode_wheel,
+ const u16 real_product_id)
+{
+ u32 alternate_modes = 0;
+ const char *real_tag = NULL;
+ const char *real_name = NULL;
+
+ if (mmode_wheel) {
+ alternate_modes = mmode_wheel->alternate_modes;
+ real_tag = mmode_wheel->real_tag;
+ real_name = mmode_wheel->real_name;
+ }
+
+ {
+ struct lg4ff_wheel_data t_wdata = { .product_id = wheel->product_id,
+ .real_product_id = real_product_id,
+ .min_range = wheel->min_range,
+ .max_range = wheel->max_range,
+ .set_range = wheel->set_range,
+ .alternate_modes = alternate_modes,
+ .real_tag = real_tag,
+ .real_name = real_name };
+
+ memcpy(wdata, &t_wdata, sizeof(t_wdata));
+ }
+}
+
+static int lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
+ s32 *value;
int x;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return -EINVAL;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return -EINVAL;
+ }
+ value = entry->report->field[0]->value;
+
#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
switch (effect->type) {
@@ -309,6 +387,7 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
CLAMP(x);
+ spin_lock_irqsave(&entry->report_lock, flags);
if (x == 0x80) {
/* De-activate force in slot-1*/
value[0] = 0x13;
@@ -319,7 +398,8 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
return 0;
}
@@ -331,7 +411,8 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
break;
}
return 0;
@@ -339,15 +420,16 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
/* Sends default autocentering command compatible with
* all wheels except Formula Force EX */
-static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
+static void lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
{
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
- __u32 expand_a, expand_b;
+ s32 *value = report->field[0]->value;
+ u32 expand_a, expand_b;
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
+ unsigned long flags;
drv_data = hid_get_drvdata(hid);
if (!drv_data) {
@@ -360,8 +442,10 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
hid_err(hid, "Device properties not found!\n");
return;
}
+ value = entry->report->field[0]->value;
/* De-activate Auto-Center */
+ spin_lock_irqsave(&entry->report_lock, flags);
if (magnitude == 0) {
value[0] = 0xf5;
value[1] = 0x00;
@@ -371,7 +455,8 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
return;
}
@@ -384,7 +469,7 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
}
/* Adjust for non-MOMO wheels */
- switch (entry->product_id) {
+ switch (entry->wdata.product_id) {
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
break;
@@ -401,7 +486,7 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
/* Activate Auto-Center */
value[0] = 0x14;
@@ -412,18 +497,34 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
/* Sends autocentering command compatible with Formula Force EX */
-static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
+static void lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
{
struct hid_device *hid = input_get_drvdata(dev);
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
+ s32 *value;
magnitude = magnitude * 90 / 65535;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+ value = entry->report->field[0]->value;
+
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xfe;
value[1] = 0x03;
value[2] = magnitude >> 14;
@@ -432,18 +533,33 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
/* Sends command to set range compatible with G25/G27/Driving Force GT */
-static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
+static void lg4ff_set_range_g25(struct hid_device *hid, u16 range)
{
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
+ s32 *value;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+ value = entry->report->field[0]->value;
dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xf8;
value[1] = 0x81;
value[2] = range & 0x00ff;
@@ -452,20 +568,35 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
/* Sends commands to set range compatible with Driving Force Pro wheel */
-static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
+static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range)
{
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
int start_left, start_right, full_range;
- __s32 *value = report->field[0]->value;
+ s32 *value;
+
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+ value = entry->report->field[0]->value;
dbg_hid("Driving Force Pro: setting range to %u\n", range);
/* Prepare "coarse" limit command */
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xf8;
value[1] = 0x00; /* Set later */
value[2] = 0x00;
@@ -475,13 +606,13 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
value[6] = 0x00;
if (range > 200) {
- report->field[0]->value[1] = 0x03;
+ value[1] = 0x03;
full_range = 900;
} else {
- report->field[0]->value[1] = 0x02;
+ value[1] = 0x02;
full_range = 200;
}
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
/* Prepare "fine" limit command */
value[0] = 0x81;
@@ -493,7 +624,8 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
value[6] = 0x00;
if (range == 200 || range == 900) { /* Do not apply any fine limit */
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
return;
}
@@ -507,7 +639,8 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
value[6] = 0xff;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(const u16 real_product_id, const u16 target_product_id)
@@ -548,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:
@@ -569,19 +719,35 @@ static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(cons
static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct lg4ff_compat_mode_switch *s)
{
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
+ s32 *value;
u8 i;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return -EINVAL;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return -EINVAL;
+ }
+ value = entry->report->field[0]->value;
+
+ spin_lock_irqsave(&entry->report_lock, flags);
for (i = 0; i < s->cmd_count; i++) {
u8 j;
for (j = 0; j < 7; j++)
value[j] = s->cmd[j + (7*i)];
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
}
+ spin_unlock_irqrestore(&entry->report_lock, flags);
hid_hw_wait(hid);
return 0;
}
@@ -606,23 +772,23 @@ static ssize_t lg4ff_alternate_modes_show(struct device *dev, struct device_attr
return 0;
}
- if (!entry->real_name) {
+ if (!entry->wdata.real_name) {
hid_err(hid, "NULL pointer to string\n");
return 0;
}
for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) {
- if (entry->alternate_modes & BIT(i)) {
+ if (entry->wdata.alternate_modes & BIT(i)) {
/* Print tag and full name */
count += scnprintf(buf + count, PAGE_SIZE - count, "%s: %s",
lg4ff_alternate_modes[i].tag,
- !lg4ff_alternate_modes[i].product_id ? entry->real_name : lg4ff_alternate_modes[i].name);
+ !lg4ff_alternate_modes[i].product_id ? entry->wdata.real_name : lg4ff_alternate_modes[i].name);
if (count >= PAGE_SIZE - 1)
return count;
/* Mark the currently active mode with an asterisk */
- if (lg4ff_alternate_modes[i].product_id == entry->product_id ||
- (lg4ff_alternate_modes[i].product_id == 0 && entry->product_id == entry->real_product_id))
+ if (lg4ff_alternate_modes[i].product_id == entry->wdata.product_id ||
+ (lg4ff_alternate_modes[i].product_id == 0 && entry->wdata.product_id == entry->wdata.real_product_id))
count += scnprintf(buf + count, PAGE_SIZE - count, " *\n");
else
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
@@ -675,10 +841,10 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
const u16 mode_product_id = lg4ff_alternate_modes[i].product_id;
const char *tag = lg4ff_alternate_modes[i].tag;
- if (entry->alternate_modes & BIT(i)) {
+ if (entry->wdata.alternate_modes & BIT(i)) {
if (!strcmp(tag, lbuf)) {
if (!mode_product_id)
- target_product_id = entry->real_product_id;
+ target_product_id = entry->wdata.real_product_id;
else
target_product_id = mode_product_id;
break;
@@ -693,24 +859,24 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
}
kfree(lbuf); /* Not needed anymore */
- if (target_product_id == entry->product_id) /* Nothing to do */
+ if (target_product_id == entry->wdata.product_id) /* Nothing to do */
return count;
/* Automatic switching has to be disabled for the switch to DF-EX mode to work correctly */
if (target_product_id == USB_DEVICE_ID_LOGITECH_WHEEL && !lg4ff_no_autoswitch) {
hid_info(hid, "\"%s\" cannot be switched to \"DF-EX\" mode. Load the \"hid_logitech\" module with \"lg4ff_no_autoswitch=1\" parameter set and try again\n",
- entry->real_name);
+ entry->wdata.real_name);
return -EINVAL;
}
/* Take care of hardware limitations */
- if ((entry->real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry->real_product_id == USB_DEVICE_ID_LOGITECH_G25_WHEEL) &&
- entry->product_id > target_product_id) {
- hid_info(hid, "\"%s\" cannot be switched back into \"%s\" mode\n", entry->real_name, lg4ff_alternate_modes[i].name);
+ if ((entry->wdata.real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry->wdata.real_product_id == USB_DEVICE_ID_LOGITECH_G25_WHEEL) &&
+ entry->wdata.product_id > target_product_id) {
+ hid_info(hid, "\"%s\" cannot be switched back into \"%s\" mode\n", entry->wdata.real_name, lg4ff_alternate_modes[i].name);
return -EINVAL;
}
- s = lg4ff_get_mode_switch_command(entry->real_product_id, target_product_id);
+ s = lg4ff_get_mode_switch_command(entry->wdata.real_product_id, target_product_id);
if (!s) {
hid_err(hid, "Invalid target product ID %X\n", target_product_id);
return -EINVAL;
@@ -721,9 +887,9 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
}
static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store);
-/* Read current range and display it in terminal */
-static ssize_t range_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+/* Export the currently set range of the wheel */
+static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct hid_device *hid = to_hid_device(dev);
struct lg4ff_device_entry *entry;
@@ -742,19 +908,19 @@ static ssize_t range_show(struct device *dev, struct device_attribute *attr,
return 0;
}
- count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->range);
+ count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.range);
return count;
}
/* Set range to user specified value, call appropriate function
* according to the type of the wheel */
-static ssize_t range_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct hid_device *hid = to_hid_device(dev);
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
- __u16 range = simple_strtoul(buf, NULL, 10);
+ u16 range = simple_strtoul(buf, NULL, 10);
drv_data = hid_get_drvdata(hid);
if (!drv_data) {
@@ -769,18 +935,18 @@ static ssize_t range_store(struct device *dev, struct device_attribute *attr,
}
if (range == 0)
- range = entry->max_range;
+ range = entry->wdata.max_range;
/* Check if the wheel supports range setting
* and that the range is within limits for the wheel */
- if (entry->set_range != NULL && range >= entry->min_range && range <= entry->max_range) {
- entry->set_range(hid, range);
- entry->range = range;
+ if (entry->wdata.set_range && range >= entry->wdata.min_range && range <= entry->wdata.max_range) {
+ entry->wdata.set_range(hid, range);
+ entry->wdata.range = range;
}
return count;
}
-static DEVICE_ATTR_RW(range);
+static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_range_show, lg4ff_range_store);
static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -801,12 +967,12 @@ static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *a
return 0;
}
- if (!entry->real_tag || !entry->real_name) {
+ if (!entry->wdata.real_tag || !entry->wdata.real_name) {
hid_err(hid, "NULL pointer to string\n");
return 0;
}
- count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->real_tag, entry->real_name);
+ count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->wdata.real_tag, entry->wdata.real_name);
return count;
}
@@ -818,12 +984,27 @@ static ssize_t lg4ff_real_id_store(struct device *dev, struct device_attribute *
static DEVICE_ATTR(real_id, S_IRUGO, lg4ff_real_id_show, lg4ff_real_id_store);
#ifdef CONFIG_LEDS_CLASS
-static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
+static void lg4ff_set_leds(struct hid_device *hid, u8 leds)
{
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
+ struct lg_drv_data *drv_data;
+ struct lg4ff_device_entry *entry;
+ unsigned long flags;
+ s32 *value;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+ value = entry->report->field[0]->value;
+
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xf8;
value[1] = 0x12;
value[2] = leds;
@@ -831,7 +1012,8 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
value[4] = 0x00;
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
@@ -848,7 +1030,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
return;
}
- entry = (struct lg4ff_device_entry *)drv_data->device_props;
+ entry = drv_data->device_props;
if (!entry) {
hid_err(hid, "Device properties not found.");
@@ -856,15 +1038,15 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
}
for (i = 0; i < 5; i++) {
- if (led_cdev != entry->led[i])
+ if (led_cdev != entry->wdata.led[i])
continue;
- state = (entry->led_state >> i) & 1;
+ state = (entry->wdata.led_state >> i) & 1;
if (value == LED_OFF && state) {
- entry->led_state &= ~(1 << i);
- lg4ff_set_leds(hid, entry->led_state);
+ entry->wdata.led_state &= ~(1 << i);
+ lg4ff_set_leds(hid, entry->wdata.led_state);
} else if (value != LED_OFF && !state) {
- entry->led_state |= 1 << i;
- lg4ff_set_leds(hid, entry->led_state);
+ entry->wdata.led_state |= 1 << i;
+ lg4ff_set_leds(hid, entry->wdata.led_state);
}
break;
}
@@ -883,7 +1065,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
return LED_OFF;
}
- entry = (struct lg4ff_device_entry *)drv_data->device_props;
+ entry = drv_data->device_props;
if (!entry) {
hid_err(hid, "Device properties not found.");
@@ -891,8 +1073,8 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
}
for (i = 0; i < 5; i++)
- if (led_cdev == entry->led[i]) {
- value = (entry->led_state >> i) & 1;
+ if (led_cdev == entry->wdata.led[i]) {
+ value = (entry->wdata.led_state >> i) & 1;
break;
}
@@ -902,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;
}
@@ -991,8 +1160,11 @@ int lg4ff_init(struct hid_device *hid)
{
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
struct input_dev *dev = hidinput->input;
+ struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
+ const struct lg4ff_multimode_wheel *mmode_wheel = NULL;
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
int error, i, j;
@@ -1003,6 +1175,18 @@ int lg4ff_init(struct hid_device *hid)
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
return -1;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Cannot add device, private driver data not allocated\n");
+ return -1;
+ }
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+ spin_lock_init(&entry->report_lock);
+ entry->report = report;
+ drv_data->device_props = entry;
+
/* Check if a multimode wheel has been connected and
* handle it appropriately */
mmode_ret = lg4ff_handle_multimode_wheel(hid, &real_product_id, bcdDevice);
@@ -1012,6 +1196,11 @@ int lg4ff_init(struct hid_device *hid)
*/
if (mmode_ret == LG4FF_MMODE_SWITCHED)
return 0;
+ else if (mmode_ret < 0) {
+ hid_err(hid, "Unable to switch device mode during initialization, errno %d\n", mmode_ret);
+ error = mmode_ret;
+ goto err_init;
+ }
/* Check what wheel has been connected */
for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
@@ -1022,9 +1211,11 @@ int lg4ff_init(struct hid_device *hid)
}
if (i == ARRAY_SIZE(lg4ff_devices)) {
- hid_err(hid, "Device is not supported by lg4ff driver. If you think it should be, consider reporting a bug to"
- "LKML, Simon Wood <simon@mungewell.org> or Michal Maly <madcatxster@gmail.com>\n");
- return -1;
+ hid_err(hid, "This device is flagged to be handled by the lg4ff module but this module does not know how to handle it. "
+ "Please report this as a bug to LKML, Simon Wood <simon@mungewell.org> or "
+ "Michal Maly <madcatxster@devoid-pointer.net>\n");
+ error = -1;
+ goto err_init;
}
if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
@@ -1035,7 +1226,8 @@ int lg4ff_init(struct hid_device *hid)
if (mmode_idx == ARRAY_SIZE(lg4ff_multimode_wheels)) {
hid_err(hid, "Device product ID %X is not listed as a multimode wheel", real_product_id);
- return -1;
+ error = -1;
+ goto err_init;
}
}
@@ -1043,37 +1235,17 @@ int lg4ff_init(struct hid_device *hid)
for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
- error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
+ error = input_ff_create_memless(dev, NULL, lg4ff_play);
if (error)
- return error;
-
- /* Get private driver data */
- drv_data = hid_get_drvdata(hid);
- if (!drv_data) {
- hid_err(hid, "Cannot add device, private driver data not allocated\n");
- return -1;
- }
+ goto err_init;
/* Initialize device properties */
- entry = kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
- if (!entry) {
- hid_err(hid, "Cannot add device, insufficient memory to allocate device properties.\n");
- return -ENOMEM;
- }
- drv_data->device_props = entry;
-
- entry->product_id = lg4ff_devices[i].product_id;
- entry->real_product_id = real_product_id;
- entry->min_range = lg4ff_devices[i].min_range;
- entry->max_range = lg4ff_devices[i].max_range;
- entry->set_range = lg4ff_devices[i].set_range;
if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
BUG_ON(mmode_idx == -1);
- entry->alternate_modes = lg4ff_multimode_wheels[mmode_idx].alternate_modes;
- entry->real_tag = lg4ff_multimode_wheels[mmode_idx].real_tag;
- entry->real_name = lg4ff_multimode_wheels[mmode_idx].real_name;
+ mmode_wheel = &lg4ff_multimode_wheels[mmode_idx];
}
+ lg4ff_init_wheel_data(&entry->wdata, &lg4ff_devices[i], mmode_wheel, real_product_id);
/* Check if autocentering is available and
* set the centering force to zero by default */
@@ -1081,9 +1253,9 @@ int lg4ff_init(struct hid_device *hid)
/* Formula Force EX expects different autocentering command */
if ((bcdDevice >> 8) == LG4FF_FFEX_REV_MAJ &&
(bcdDevice & 0xff) == LG4FF_FFEX_REV_MIN)
- dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
+ dev->ff->set_autocenter = lg4ff_set_autocenter_ffex;
else
- dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
+ dev->ff->set_autocenter = lg4ff_set_autocenter_default;
dev->ff->set_autocenter(dev, 0);
}
@@ -1091,29 +1263,30 @@ int lg4ff_init(struct hid_device *hid)
/* Create sysfs interface */
error = device_create_file(&hid->dev, &dev_attr_range);
if (error)
- return error;
+ hid_warn(hid, "Unable to create sysfs interface for \"range\", errno %d\n", error);
if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
error = device_create_file(&hid->dev, &dev_attr_real_id);
if (error)
- return error;
+ hid_warn(hid, "Unable to create sysfs interface for \"real_id\", errno %d\n", error);
error = device_create_file(&hid->dev, &dev_attr_alternate_modes);
if (error)
- return error;
+ hid_warn(hid, "Unable to create sysfs interface for \"alternate_modes\", errno %d\n", error);
}
dbg_hid("sysfs interface created\n");
/* Set the maximum range to start with */
- entry->range = entry->max_range;
- if (entry->set_range != NULL)
- entry->set_range(hid, entry->range);
+ entry->wdata.range = entry->wdata.max_range;
+ if (entry->wdata.set_range)
+ entry->wdata.set_range(hid, entry->wdata.range);
#ifdef CONFIG_LEDS_CLASS
- /* register led subsystem - G27 only */
- entry->led_state = 0;
+ /* register led subsystem - G27/G29 only */
+ entry->wdata.led_state = 0;
for (j = 0; j < 5; j++)
- entry->led[j] = NULL;
+ 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;
@@ -1126,7 +1299,7 @@ int lg4ff_init(struct hid_device *hid)
led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
if (!led) {
hid_err(hid, "can't allocate memory for LED %d\n", j);
- goto err;
+ goto err_leds;
}
name = (void *)(&led[1]);
@@ -1137,16 +1310,16 @@ int lg4ff_init(struct hid_device *hid)
led->brightness_get = lg4ff_led_get_brightness;
led->brightness_set = lg4ff_led_set_brightness;
- entry->led[j] = led;
+ entry->wdata.led[j] = led;
error = led_classdev_register(&hid->dev, led);
if (error) {
hid_err(hid, "failed to register LED %d. Aborting.\n", j);
-err:
+err_leds:
/* Deregister LEDs (if any) */
for (j = 0; j < 5; j++) {
- led = entry->led[j];
- entry->led[j] = NULL;
+ led = entry->wdata.led[j];
+ entry->wdata.led[j] = NULL;
if (!led)
continue;
led_classdev_unregister(led);
@@ -1160,6 +1333,11 @@ out:
#endif
hid_info(hid, "Force feedback support for Logitech Gaming Wheels\n");
return 0;
+
+err_init:
+ drv_data->device_props = NULL;
+ kfree(entry);
+ return error;
}
int lg4ff_deinit(struct hid_device *hid)
@@ -1176,14 +1354,13 @@ int lg4ff_deinit(struct hid_device *hid)
if (!entry)
goto out; /* Nothing more to do */
- device_remove_file(&hid->dev, &dev_attr_range);
-
/* Multimode devices will have at least the "MODE_NATIVE" bit set */
- if (entry->alternate_modes) {
+ if (entry->wdata.alternate_modes) {
device_remove_file(&hid->dev, &dev_attr_real_id);
device_remove_file(&hid->dev, &dev_attr_alternate_modes);
}
+ device_remove_file(&hid->dev, &dev_attr_range);
#ifdef CONFIG_LEDS_CLASS
{
int j;
@@ -1192,8 +1369,8 @@ int lg4ff_deinit(struct hid_device *hid)
/* Deregister LEDs (if any) */
for (j = 0; j < 5; j++) {
- led = entry->led[j];
- entry->led[j] = NULL;
+ led = entry->wdata.led[j];
+ entry->wdata.led[j] = NULL;
if (!led)
continue;
led_classdev_unregister(led);
@@ -1201,10 +1378,10 @@ int lg4ff_deinit(struct hid_device *hid)
}
}
#endif
+ hid_hw_stop(hid);
+ drv_data->device_props = NULL;
- /* Deallocate memory */
kfree(entry);
-
out:
dbg_hid("Device successfully unregistered\n");
return 0;
diff --git a/kernel/drivers/hid/hid-lg4ff.h b/kernel/drivers/hid/hid-lg4ff.h
index 5b6a5086c..66201af44 100644
--- a/kernel/drivers/hid/hid-lg4ff.h
+++ b/kernel/drivers/hid/hid-lg4ff.h
@@ -5,12 +5,12 @@
extern int lg4ff_no_autoswitch; /* From hid-lg.c */
int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
- struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
+ struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data);
int lg4ff_init(struct hid_device *hdev);
int lg4ff_deinit(struct hid_device *hdev);
#else
static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
- struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; }
+ struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data) { return 0; }
static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
#endif
diff --git a/kernel/drivers/hid/hid-logitech-hidpp.c b/kernel/drivers/hid/hid-logitech-hidpp.c
index 5fd530acf..5fd97860a 100644
--- a/kernel/drivers/hid/hid-logitech-hidpp.c
+++ b/kernel/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
@@ -40,10 +45,16 @@ MODULE_PARM_DESC(disable_raw_mode,
#define HIDPP_REPORT_LONG_LENGTH 20
#define HIDPP_QUIRK_CLASS_WTP BIT(0)
+#define HIDPP_QUIRK_CLASS_M560 BIT(1)
+#define HIDPP_QUIRK_CLASS_K400 BIT(2)
-/* bits 1..20 are reserved for classes */
-#define HIDPP_QUIRK_DELAYED_INIT BIT(21)
+/* bits 2..20 are reserved for classes */
+#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
@@ -552,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 */
/* -------------------------------------------------------------------------- */
@@ -930,6 +987,276 @@ static int wtp_connect(struct hid_device *hdev, bool connected)
true, true);
}
+/* ------------------------------------------------------------------------- */
+/* Logitech M560 devices */
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Logitech M560 protocol overview
+ *
+ * The Logitech M560 mouse, is designed for windows 8. When the middle and/or
+ * the sides buttons are pressed, it sends some keyboard keys events
+ * instead of buttons ones.
+ * To complicate things further, the middle button keys sequence
+ * is different from the odd press and the even press.
+ *
+ * forward button -> Super_R
+ * backward button -> Super_L+'d' (press only)
+ * middle button -> 1st time: Alt_L+SuperL+XF86TouchpadOff (press only)
+ * 2nd time: left-click (press only)
+ * NB: press-only means that when the button is pressed, the
+ * KeyPress/ButtonPress and KeyRelease/ButtonRelease events are generated
+ * together sequentially; instead when the button is released, no event is
+ * generated !
+ *
+ * With the command
+ * 10<xx>0a 3500af03 (where <xx> is the mouse id),
+ * the mouse reacts differently:
+ * - it never sends a keyboard key event
+ * - for the three mouse button it sends:
+ * middle button press 11<xx>0a 3500af00...
+ * side 1 button (forward) press 11<xx>0a 3500b000...
+ * side 2 button (backward) press 11<xx>0a 3500ae00...
+ * middle/side1/side2 button release 11<xx>0a 35000000...
+ */
+
+static const u8 m560_config_parameter[] = {0x00, 0xaf, 0x03};
+
+struct m560_private_data {
+ struct input_dev *input;
+};
+
+/* how buttons are mapped in the report */
+#define M560_MOUSE_BTN_LEFT 0x01
+#define M560_MOUSE_BTN_RIGHT 0x02
+#define M560_MOUSE_BTN_WHEEL_LEFT 0x08
+#define M560_MOUSE_BTN_WHEEL_RIGHT 0x10
+
+#define M560_SUB_ID 0x0a
+#define M560_BUTTON_MODE_REGISTER 0x35
+
+static int m560_send_config_command(struct hid_device *hdev, bool connected)
+{
+ struct hidpp_report response;
+ struct hidpp_device *hidpp_dev;
+
+ hidpp_dev = hid_get_drvdata(hdev);
+
+ if (!connected)
+ return -ENODEV;
+
+ return hidpp_send_rap_command_sync(
+ hidpp_dev,
+ REPORT_ID_HIDPP_SHORT,
+ M560_SUB_ID,
+ M560_BUTTON_MODE_REGISTER,
+ (u8 *)m560_config_parameter,
+ sizeof(m560_config_parameter),
+ &response
+ );
+}
+
+static int m560_allocate(struct hid_device *hdev)
+{
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ struct m560_private_data *d;
+
+ d = devm_kzalloc(&hdev->dev, sizeof(struct m560_private_data),
+ GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ hidpp->private_data = d;
+
+ return 0;
+};
+
+static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
+{
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ struct m560_private_data *mydata = hidpp->private_data;
+
+ /* sanity check */
+ if (!mydata || !mydata->input) {
+ hid_err(hdev, "error in parameter\n");
+ return -EINVAL;
+ }
+
+ if (size < 7) {
+ hid_err(hdev, "error in report\n");
+ return 0;
+ }
+
+ if (data[0] == REPORT_ID_HIDPP_LONG &&
+ data[2] == M560_SUB_ID && data[6] == 0x00) {
+ /*
+ * m560 mouse report for middle, forward and backward button
+ *
+ * data[0] = 0x11
+ * data[1] = device-id
+ * data[2] = 0x0a
+ * data[5] = 0xaf -> middle
+ * 0xb0 -> forward
+ * 0xae -> backward
+ * 0x00 -> release all
+ * data[6] = 0x00
+ */
+
+ switch (data[5]) {
+ case 0xaf:
+ input_report_key(mydata->input, BTN_MIDDLE, 1);
+ break;
+ case 0xb0:
+ input_report_key(mydata->input, BTN_FORWARD, 1);
+ break;
+ case 0xae:
+ input_report_key(mydata->input, BTN_BACK, 1);
+ break;
+ case 0x00:
+ input_report_key(mydata->input, BTN_BACK, 0);
+ input_report_key(mydata->input, BTN_FORWARD, 0);
+ input_report_key(mydata->input, BTN_MIDDLE, 0);
+ break;
+ default:
+ hid_err(hdev, "error in report\n");
+ return 0;
+ }
+ input_sync(mydata->input);
+
+ } else if (data[0] == 0x02) {
+ /*
+ * Logitech M560 mouse report
+ *
+ * data[0] = type (0x02)
+ * data[1..2] = buttons
+ * data[3..5] = xy
+ * data[6] = wheel
+ */
+
+ int v;
+
+ input_report_key(mydata->input, BTN_LEFT,
+ !!(data[1] & M560_MOUSE_BTN_LEFT));
+ input_report_key(mydata->input, BTN_RIGHT,
+ !!(data[1] & M560_MOUSE_BTN_RIGHT));
+
+ if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT)
+ input_report_rel(mydata->input, REL_HWHEEL, -1);
+ else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT)
+ input_report_rel(mydata->input, REL_HWHEEL, 1);
+
+ v = hid_snto32(hid_field_extract(hdev, data+3, 0, 12), 12);
+ input_report_rel(mydata->input, REL_X, v);
+
+ v = hid_snto32(hid_field_extract(hdev, data+3, 12, 12), 12);
+ input_report_rel(mydata->input, REL_Y, v);
+
+ v = hid_snto32(data[6], 8);
+ input_report_rel(mydata->input, REL_WHEEL, v);
+
+ input_sync(mydata->input);
+ }
+
+ return 1;
+}
+
+static void m560_populate_input(struct hidpp_device *hidpp,
+ struct input_dev *input_dev, bool origin_is_hid_core)
+{
+ struct m560_private_data *mydata = hidpp->private_data;
+
+ mydata->input = input_dev;
+
+ __set_bit(EV_KEY, mydata->input->evbit);
+ __set_bit(BTN_MIDDLE, mydata->input->keybit);
+ __set_bit(BTN_RIGHT, mydata->input->keybit);
+ __set_bit(BTN_LEFT, mydata->input->keybit);
+ __set_bit(BTN_BACK, mydata->input->keybit);
+ __set_bit(BTN_FORWARD, mydata->input->keybit);
+
+ __set_bit(EV_REL, mydata->input->evbit);
+ __set_bit(REL_X, mydata->input->relbit);
+ __set_bit(REL_Y, mydata->input->relbit);
+ __set_bit(REL_WHEEL, mydata->input->relbit);
+ __set_bit(REL_HWHEEL, mydata->input->relbit);
+}
+
+static int m560_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;
+}
+
+/* ------------------------------------------------------------------------- */
+/* 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 */
/* -------------------------------------------------------------------------- */
@@ -942,6 +1269,9 @@ static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
return wtp_input_mapping(hdev, hi, field, usage, bit, max);
+ else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560 &&
+ field->application != HID_GD_MOUSE)
+ return m560_input_mapping(hdev, hi, field, usage, bit, max);
return 0;
}
@@ -951,15 +1281,19 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
{
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
wtp_populate_input(hidpp, input, origin_is_hid_core);
+ else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
+ m560_populate_input(hidpp, input, origin_is_hid_core);
}
-static void hidpp_input_configured(struct hid_device *hdev,
+static int 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;
hidpp_populate_input(hidpp, input, true);
+
+ return 0;
}
static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
@@ -996,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;
@@ -1038,6 +1372,8 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
return wtp_raw_event(hdev, data, size);
+ else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
+ return m560_raw_event(hdev, data, size);
return 0;
}
@@ -1115,23 +1451,34 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
ret = wtp_connect(hdev, connected);
if (ret)
return;
+ } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
+ 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);
@@ -1184,13 +1531,22 @@ 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) {
ret = wtp_allocate(hdev, id);
if (ret)
- goto wtp_allocate_fail;
+ goto allocate_fail;
+ } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
+ 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);
@@ -1231,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);
@@ -1240,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);
@@ -1253,7 +1609,7 @@ hid_hw_start_fail:
hid_parse_fail:
cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex);
-wtp_allocate_fail:
+allocate_fail:
hid_set_drvdata(hdev, NULL);
return ret;
}
@@ -1281,6 +1637,14 @@ 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 },
+ { /* Mouse logitech M560 */
+ 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/kernel/drivers/hid/hid-magicmouse.c b/kernel/drivers/hid/hid-magicmouse.c
index 29a74c1ef..d6fa496d0 100644
--- a/kernel/drivers/hid/hid-magicmouse.c
+++ b/kernel/drivers/hid/hid-magicmouse.c
@@ -471,18 +471,22 @@ static int magicmouse_input_mapping(struct hid_device *hdev,
return 0;
}
-static void magicmouse_input_configured(struct hid_device *hdev,
+static int magicmouse_input_configured(struct hid_device *hdev,
struct hid_input *hi)
{
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
+ int ret;
- int ret = magicmouse_setup_input(msc->input, hdev);
+ ret = magicmouse_setup_input(msc->input, hdev);
if (ret) {
hid_err(hdev, "magicmouse setup input failed (%d)\n", ret);
/* clean msc->input to notify probe() of the failure */
msc->input = NULL;
+ return ret;
}
+
+ return 0;
}
diff --git a/kernel/drivers/hid/hid-microsoft.c b/kernel/drivers/hid/hid-microsoft.c
index af935eb19..77a2cf3e4 100644
--- a/kernel/drivers/hid/hid-microsoft.c
+++ b/kernel/drivers/hid/hid-microsoft.c
@@ -276,9 +276,15 @@ 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_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),
.driver_data = MS_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP),
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
.driver_data = MS_HIDINPUT },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
diff --git a/kernel/drivers/hid/hid-multitouch.c b/kernel/drivers/hid/hid-multitouch.c
index 6a9b05b32..2b8ff18d3 100644
--- a/kernel/drivers/hid/hid-multitouch.c
+++ b/kernel/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)
{
@@ -322,11 +357,24 @@ static void mt_feature_mapping(struct hid_device *hdev,
break;
}
- td->inputmode = field->report->id;
- td->inputmode_index = usage->usage_index;
+ if (td->inputmode < 0) {
+ td->inputmode = field->report->id;
+ td->inputmode_index = usage->usage_index;
+ } else {
+ /*
+ * Some elan panels wrongly declare 2 input mode
+ * features, and silently ignore when we set the
+ * value in the second field. Skip the second feature
+ * and hope for the best.
+ */
+ dev_info(&hdev->dev,
+ "Ignoring the extra HID_DG_INPUTMODE\n");
+ }
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 +391,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;
@@ -725,12 +774,13 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
mt_sync_frame(td, report->field[0]->hidinput->input);
}
-static void mt_touch_input_configured(struct hid_device *hdev,
+static int mt_touch_input_configured(struct hid_device *hdev,
struct hid_input *hi)
{
struct mt_device *td = hid_get_drvdata(hdev);
struct mt_class *cls = &td->mtclass;
struct input_dev *input = hi->input;
+ int ret;
if (!td->maxcontacts)
td->maxcontacts = MT_DEFAULT_MAXCONTACT;
@@ -752,9 +802,12 @@ static void mt_touch_input_configured(struct hid_device *hdev,
if (td->is_buttonpad)
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
- input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
+ ret = input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
+ if (ret)
+ return ret;
td->mt_flags = 0;
+ return 0;
}
static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@@ -778,9 +831,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
/*
* some egalax touchscreens have "application == HID_DG_TOUCHSCREEN"
* for the stylus.
+ * The check for mt_report_id ensures we don't process
+ * HID_DG_CONTACTCOUNT from the pen report as it is outside the physical
+ * collection, but within the report ID.
*/
if (field->physical == HID_DG_STYLUS)
return 0;
+ else if ((field->physical == 0) &&
+ (field->report->id != td->mt_report_id) &&
+ (td->mt_report_id != -1))
+ return 0;
if (field->application == HID_DG_TOUCHSCREEN ||
field->application == HID_DG_TOUCHPAD)
@@ -923,15 +983,19 @@ static void mt_post_parse(struct mt_device *td)
cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE;
}
-static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
+static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct mt_device *td = hid_get_drvdata(hdev);
char *name;
const char *suffix = NULL;
struct hid_field *field = hi->report->field[0];
+ int ret;
- if (hi->report->id == td->mt_report_id)
- mt_touch_input_configured(hdev, hi);
+ if (hi->report->id == td->mt_report_id) {
+ ret = mt_touch_input_configured(hdev, hi);
+ if (ret)
+ return ret;
+ }
/*
* some egalax touchscreens have "application == HID_DG_TOUCHSCREEN"
@@ -961,6 +1025,9 @@ static void 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;
@@ -982,6 +1049,8 @@ static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
hi->input->name = name;
}
}
+
+ return 0;
}
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
@@ -1019,8 +1088,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) {
@@ -1138,6 +1212,14 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT,
USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH) },
+ /* CJTouch panels */
+ { .driver_data = MT_CLS_NSMU,
+ MT_USB_DEVICE(USB_VENDOR_ID_CJTOUCH,
+ USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0020) },
+ { .driver_data = MT_CLS_NSMU,
+ MT_USB_DEVICE(USB_VENDOR_ID_CJTOUCH,
+ USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0040) },
+
/* CVTouch panels */
{ .driver_data = MT_CLS_NSMU,
MT_USB_DEVICE(USB_VENDOR_ID_CVTOUCH,
diff --git a/kernel/drivers/hid/hid-ntrig.c b/kernel/drivers/hid/hid-ntrig.c
index 600f20755..756d1ef9b 100644
--- a/kernel/drivers/hid/hid-ntrig.c
+++ b/kernel/drivers/hid/hid-ntrig.c
@@ -859,14 +859,14 @@ not_claimed_input:
return 1;
}
-static void ntrig_input_configured(struct hid_device *hid,
+static int ntrig_input_configured(struct hid_device *hid,
struct hid_input *hidinput)
{
struct input_dev *input = hidinput->input;
if (hidinput->report->maxfield < 1)
- return;
+ return 0;
switch (hidinput->report->field[0]->application) {
case HID_DG_PEN:
@@ -890,6 +890,8 @@ static void ntrig_input_configured(struct hid_device *hid,
"N-Trig MultiTouch";
break;
}
+
+ return 0;
}
static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
diff --git a/kernel/drivers/hid/hid-picolcd_backlight.c b/kernel/drivers/hid/hid-picolcd_backlight.c
index a32c5f86b..808807ad3 100644
--- a/kernel/drivers/hid/hid-picolcd_backlight.c
+++ b/kernel/drivers/hid/hid-picolcd_backlight.c
@@ -94,8 +94,7 @@ void picolcd_exit_backlight(struct picolcd_data *data)
struct backlight_device *bdev = data->backlight;
data->backlight = NULL;
- if (bdev)
- backlight_device_unregister(bdev);
+ backlight_device_unregister(bdev);
}
int picolcd_resume_backlight(struct picolcd_data *data)
diff --git a/kernel/drivers/hid/hid-picolcd_cir.c b/kernel/drivers/hid/hid-picolcd_cir.c
index 045f8ebf1..96286510f 100644
--- a/kernel/drivers/hid/hid-picolcd_cir.c
+++ b/kernel/drivers/hid/hid-picolcd_cir.c
@@ -145,7 +145,6 @@ void picolcd_exit_cir(struct picolcd_data *data)
struct rc_dev *rdev = data->rc_dev;
data->rc_dev = NULL;
- if (rdev)
- rc_unregister_device(rdev);
+ rc_unregister_device(rdev);
}
diff --git a/kernel/drivers/hid/hid-picolcd_lcd.c b/kernel/drivers/hid/hid-picolcd_lcd.c
index 89821c2da..22dcbe13d 100644
--- a/kernel/drivers/hid/hid-picolcd_lcd.c
+++ b/kernel/drivers/hid/hid-picolcd_lcd.c
@@ -92,8 +92,7 @@ void picolcd_exit_lcd(struct picolcd_data *data)
struct lcd_device *ldev = data->lcd;
data->lcd = NULL;
- if (ldev)
- lcd_device_unregister(ldev);
+ lcd_device_unregister(ldev);
}
int picolcd_resume_lcd(struct picolcd_data *data)
diff --git a/kernel/drivers/hid/hid-plantronics.c b/kernel/drivers/hid/hid-plantronics.c
index 2180e0789..febb21ee1 100644
--- a/kernel/drivers/hid/hid-plantronics.c
+++ b/kernel/drivers/hid/hid-plantronics.c
@@ -2,7 +2,7 @@
* Plantronics USB HID Driver
*
* Copyright (c) 2014 JD Cole <jd.cole@plantronics.com>
- * Copyright (c) 2014 Terry Junge <terry.junge@plantronics.com>
+ * Copyright (c) 2015 Terry Junge <terry.junge@plantronics.com>
*/
/*
@@ -17,23 +17,138 @@
#include <linux/hid.h>
#include <linux/module.h>
+#define PLT_HID_1_0_PAGE 0xffa00000
+#define PLT_HID_2_0_PAGE 0xffa20000
+
+#define PLT_BASIC_TELEPHONY 0x0003
+#define PLT_BASIC_EXCEPTION 0x0005
+
+#define PLT_VOL_UP 0x00b1
+#define PLT_VOL_DOWN 0x00b2
+
+#define PLT1_VOL_UP (PLT_HID_1_0_PAGE | PLT_VOL_UP)
+#define PLT1_VOL_DOWN (PLT_HID_1_0_PAGE | PLT_VOL_DOWN)
+#define PLT2_VOL_UP (PLT_HID_2_0_PAGE | PLT_VOL_UP)
+#define PLT2_VOL_DOWN (PLT_HID_2_0_PAGE | PLT_VOL_DOWN)
+
+#define PLT_DA60 0xda60
+#define PLT_BT300_MIN 0x0413
+#define PLT_BT300_MAX 0x0418
+
+
+#define PLT_ALLOW_CONSUMER (field->application == HID_CP_CONSUMERCONTROL && \
+ (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER)
+
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;
+ unsigned short mapped_key;
+ unsigned long plt_type = (unsigned long)hid_get_drvdata(hdev);
+
+ /* handle volume up/down mapping */
+ /* non-standard types or multi-HID interfaces - plt_type is PID */
+ if (!(plt_type & HID_USAGE_PAGE)) {
+ switch (plt_type) {
+ case PLT_DA60:
+ if (PLT_ALLOW_CONSUMER)
+ goto defaulted;
+ goto ignored;
+ default:
+ if (PLT_ALLOW_CONSUMER)
+ goto defaulted;
+ }
+ }
+ /* handle standard types - plt_type is 0xffa0uuuu or 0xffa2uuuu */
+ /* 'basic telephony compliant' - allow default consumer page map */
+ else if ((plt_type & HID_USAGE) >= PLT_BASIC_TELEPHONY &&
+ (plt_type & HID_USAGE) != PLT_BASIC_EXCEPTION) {
+ if (PLT_ALLOW_CONSUMER)
+ goto defaulted;
+ }
+ /* not 'basic telephony' - apply legacy mapping */
+ /* only map if the field is in the device's primary vendor page */
+ else if (!((field->application ^ plt_type) & HID_USAGE_PAGE)) {
+ switch (usage->hid) {
+ case PLT1_VOL_UP:
+ case PLT2_VOL_UP:
+ mapped_key = KEY_VOLUMEUP;
+ goto mapped;
+ case PLT1_VOL_DOWN:
+ case PLT2_VOL_DOWN:
+ mapped_key = KEY_VOLUMEDOWN;
+ goto mapped;
+ }
}
- hid_dbg(hdev, "usage: %08x (appl: %08x) - ignored\n",
- usage->hid, field->application);
+/*
+ * Future mapping of call control or other usages,
+ * if and when keys are defined would go here
+ * otherwise, ignore everything else that was not mapped
+ */
+ignored:
return -1;
+
+defaulted:
+ hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
+ usage->hid, field->application);
+ return 0;
+
+mapped:
+ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, mapped_key);
+ hid_dbg(hdev, "usage: %08x (appl: %08x) - mapped to key %d\n",
+ usage->hid, field->application, mapped_key);
+ return 1;
+}
+
+static unsigned long plantronics_device_type(struct hid_device *hdev)
+{
+ unsigned i, col_page;
+ unsigned long plt_type = hdev->product;
+
+ /* multi-HID interfaces? - plt_type is PID */
+ if (plt_type >= PLT_BT300_MIN && plt_type <= PLT_BT300_MAX)
+ goto exit;
+
+ /* determine primary vendor page */
+ for (i = 0; i < hdev->maxcollection; i++) {
+ col_page = hdev->collection[i].usage & HID_USAGE_PAGE;
+ if (col_page == PLT_HID_2_0_PAGE) {
+ plt_type = hdev->collection[i].usage;
+ break;
+ }
+ if (col_page == PLT_HID_1_0_PAGE)
+ plt_type = hdev->collection[i].usage;
+ }
+
+exit:
+ hid_dbg(hdev, "plt_type decoded as: %08lx\n", plt_type);
+ return plt_type;
+}
+
+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;
+ }
+
+ hid_set_drvdata(hdev, (void *)plantronics_device_type(hdev));
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
+ HID_CONNECT_HIDINPUT_FORCE | HID_CONNECT_HIDDEV_FORCE);
+ if (ret)
+ hid_err(hdev, "hw start failed\n");
+
+err:
+ return ret;
}
static const struct hid_device_id plantronics_devices[] = {
@@ -46,6 +161,7 @@ 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);
diff --git a/kernel/drivers/hid/hid-prodikeys.c b/kernel/drivers/hid/hid-prodikeys.c
index 91fab9750..3a207c0ac 100644
--- a/kernel/drivers/hid/hid-prodikeys.c
+++ b/kernel/drivers/hid/hid-prodikeys.c
@@ -395,11 +395,10 @@ static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data)
/* break keys */
for (bit_index = 0; bit_index < 24; bit_index++) {
- key = pm->last_key[bit_index];
if (!((0x01 << bit_index) & bit_mask)) {
input_event(pm->input_ep82, EV_KEY,
pm->last_key[bit_index], 0);
- pm->last_key[bit_index] = 0;
+ pm->last_key[bit_index] = 0;
}
}
@@ -428,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;
@@ -696,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/kernel/drivers/hid/hid-rmi.c b/kernel/drivers/hid/hid-rmi.c
index 368ffdf2c..67cd059a8 100644
--- a/kernel/drivers/hid/hid-rmi.c
+++ b/kernel/drivers/hid/hid-rmi.c
@@ -29,14 +29,25 @@
#define RMI_SET_RMI_MODE_REPORT_ID 0x0f /* Feature Report */
/* flags */
-#define RMI_READ_REQUEST_PENDING BIT(0)
-#define RMI_READ_DATA_PENDING BIT(1)
-#define RMI_STARTED BIT(2)
+#define RMI_READ_REQUEST_PENDING 0
+#define RMI_READ_DATA_PENDING 1
+#define RMI_STARTED 2
+
+#define RMI_SLEEP_NORMAL 0x0
+#define RMI_SLEEP_DEEP_SLEEP 0x1
/* device flags */
#define RMI_DEVICE BIT(0)
#define RMI_DEVICE_HAS_PHYS_BUTTONS BIT(1)
+/*
+ * retrieve the ctrl registers
+ * the ctrl register has a size of 20 but a fw bug split it into 16 + 4,
+ * and there is no way to know if the first 20 bytes are here or not.
+ * We use only the first 12 bytes, so get only them.
+ */
+#define RMI_F11_CTRL_REG_COUNT 12
+
enum rmi_mode_type {
RMI_MODE_OFF = 0,
RMI_MODE_ATTN_REPORTS = 1,
@@ -113,6 +124,8 @@ struct rmi_data {
unsigned int max_y;
unsigned int x_size_mm;
unsigned int y_size_mm;
+ bool read_f11_ctrl_regs;
+ u8 f11_ctrl_regs[RMI_F11_CTRL_REG_COUNT];
unsigned int gpio_led_count;
unsigned int button_count;
@@ -126,6 +139,10 @@ struct rmi_data {
unsigned long device_flags;
unsigned long firmware_id;
+
+ u8 f01_ctrl0;
+ u8 interrupt_enable_mask;
+ bool restore_interrupt_mask;
};
#define RMI_PAGE(addr) (((addr) >> 8) & 0xff)
@@ -346,13 +363,34 @@ static void rmi_f11_process_touch(struct rmi_data *hdata, int slot,
}
}
+static int rmi_reset_attn_mode(struct hid_device *hdev)
+{
+ struct rmi_data *data = hid_get_drvdata(hdev);
+ int ret;
+
+ ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+ if (ret)
+ return ret;
+
+ if (data->restore_interrupt_mask) {
+ ret = rmi_write(hdev, data->f01.control_base_addr + 1,
+ &data->interrupt_enable_mask);
+ if (ret) {
+ hid_err(hdev, "can not write F01 control register\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static void rmi_reset_work(struct work_struct *work)
{
struct rmi_data *hdata = container_of(work, struct rmi_data,
reset_work);
/* switch the device to RMI if we receive a generic mouse report */
- rmi_set_mode(hdata->hdev, RMI_MODE_ATTN_REPORTS);
+ rmi_reset_attn_mode(hdata->hdev);
}
static inline int rmi_schedule_reset(struct hid_device *hdev)
@@ -532,14 +570,77 @@ static int rmi_event(struct hid_device *hdev, struct hid_field *field,
}
#ifdef CONFIG_PM
+static int rmi_set_sleep_mode(struct hid_device *hdev, int sleep_mode)
+{
+ struct rmi_data *data = hid_get_drvdata(hdev);
+ int ret;
+ u8 f01_ctrl0;
+
+ f01_ctrl0 = (data->f01_ctrl0 & ~0x3) | sleep_mode;
+
+ ret = rmi_write(hdev, data->f01.control_base_addr,
+ &f01_ctrl0);
+ if (ret) {
+ hid_err(hdev, "can not write sleep mode\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rmi_suspend(struct hid_device *hdev, pm_message_t message)
+{
+ struct rmi_data *data = hid_get_drvdata(hdev);
+ int ret;
+ u8 buf[RMI_F11_CTRL_REG_COUNT];
+
+ ret = rmi_read_block(hdev, data->f11.control_base_addr, buf,
+ RMI_F11_CTRL_REG_COUNT);
+ if (ret)
+ hid_warn(hdev, "can not read F11 control registers\n");
+ else
+ memcpy(data->f11_ctrl_regs, buf, RMI_F11_CTRL_REG_COUNT);
+
+
+ if (!device_may_wakeup(hdev->dev.parent))
+ return rmi_set_sleep_mode(hdev, RMI_SLEEP_DEEP_SLEEP);
+
+ return 0;
+}
+
static int rmi_post_reset(struct hid_device *hdev)
{
- return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+ struct rmi_data *data = hid_get_drvdata(hdev);
+ int ret;
+
+ ret = rmi_reset_attn_mode(hdev);
+ if (ret) {
+ hid_err(hdev, "can not set rmi mode\n");
+ return ret;
+ }
+
+ if (data->read_f11_ctrl_regs) {
+ ret = rmi_write_block(hdev, data->f11.control_base_addr,
+ data->f11_ctrl_regs, RMI_F11_CTRL_REG_COUNT);
+ if (ret)
+ hid_warn(hdev,
+ "can not write F11 control registers after reset\n");
+ }
+
+ if (!device_may_wakeup(hdev->dev.parent)) {
+ ret = rmi_set_sleep_mode(hdev, RMI_SLEEP_NORMAL);
+ if (ret) {
+ hid_err(hdev, "can not write sleep mode\n");
+ return ret;
+ }
+ }
+
+ return ret;
}
static int rmi_post_resume(struct hid_device *hdev)
{
- return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+ return rmi_reset_attn_mode(hdev);
}
#endif /* CONFIG_PM */
@@ -595,6 +696,7 @@ static void rmi_register_function(struct rmi_data *data,
f->interrupt_count = pdt_entry->interrupt_source_count;
f->irq_mask = rmi_gen_mask(f->interrupt_base,
f->interrupt_count);
+ data->interrupt_enable_mask |= f->irq_mask;
}
}
@@ -732,6 +834,35 @@ static int rmi_populate_f01(struct hid_device *hdev)
data->firmware_id += info[2] * 65536;
}
+ ret = rmi_read_block(hdev, data->f01.control_base_addr, info,
+ 2);
+
+ if (ret) {
+ hid_err(hdev, "can not read f01 ctrl registers\n");
+ return ret;
+ }
+
+ data->f01_ctrl0 = info[0];
+
+ if (!info[1]) {
+ /*
+ * Do to a firmware bug in some touchpads the F01 interrupt
+ * enable control register will be cleared on reset.
+ * This will stop the touchpad from reporting data, so
+ * if F01 CTRL1 is 0 then we need to explicitly enable
+ * interrupts for the functions we want data for.
+ */
+ data->restore_interrupt_mask = true;
+
+ ret = rmi_write(hdev, data->f01.control_base_addr + 1,
+ &data->interrupt_enable_mask);
+ if (ret) {
+ hid_err(hdev, "can not write to control reg 1: %d.\n",
+ ret);
+ return ret;
+ }
+ }
+
return 0;
}
@@ -904,24 +1035,23 @@ static int rmi_populate_f11(struct hid_device *hdev)
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,
- * and there is no way to know if the first 20 bytes are here or not.
- * We use only the first 12 bytes, so get only them.
- */
- ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 12);
+ ret = rmi_read_block(hdev, data->f11.control_base_addr,
+ data->f11_ctrl_regs, RMI_F11_CTRL_REG_COUNT);
if (ret) {
hid_err(hdev, "can not read ctrl block of size 11: %d.\n", ret);
return ret;
}
- data->max_x = buf[6] | (buf[7] << 8);
- data->max_y = buf[8] | (buf[9] << 8);
+ /* data->f11_ctrl_regs now contains valid register data */
+ data->read_f11_ctrl_regs = true;
+
+ data->max_x = data->f11_ctrl_regs[6] | (data->f11_ctrl_regs[7] << 8);
+ data->max_y = data->f11_ctrl_regs[8] | (data->f11_ctrl_regs[9] << 8);
if (has_dribble) {
- buf[0] = buf[0] & ~BIT(6);
- ret = rmi_write(hdev, data->f11.control_base_addr, buf);
+ data->f11_ctrl_regs[0] = data->f11_ctrl_regs[0] & ~BIT(6);
+ ret = rmi_write(hdev, data->f11.control_base_addr,
+ data->f11_ctrl_regs);
if (ret) {
hid_err(hdev, "can not write to control reg 0: %d.\n",
ret);
@@ -930,9 +1060,9 @@ static int rmi_populate_f11(struct hid_device *hdev)
}
if (has_palm_detect) {
- buf[11] = buf[11] & ~BIT(0);
+ data->f11_ctrl_regs[11] = data->f11_ctrl_regs[11] & ~BIT(0);
ret = rmi_write(hdev, data->f11.control_base_addr + 11,
- &buf[11]);
+ &data->f11_ctrl_regs[11]);
if (ret) {
hid_err(hdev, "can not write to control reg 11: %d.\n",
ret);
@@ -1013,6 +1143,7 @@ static int rmi_populate_f30(struct hid_device *hdev)
static int rmi_populate(struct hid_device *hdev)
{
+ struct rmi_data *data = hid_get_drvdata(hdev);
int ret;
ret = rmi_scan_pdt(hdev);
@@ -1033,14 +1164,16 @@ static int rmi_populate(struct hid_device *hdev)
return ret;
}
- ret = rmi_populate_f30(hdev);
- if (ret)
- hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
+ if (!(data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS)) {
+ ret = rmi_populate_f30(hdev);
+ if (ret)
+ hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
+ }
return 0;
}
-static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
+static int rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct rmi_data *data = hid_get_drvdata(hdev);
struct input_dev *input = hi->input;
@@ -1052,10 +1185,10 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
hid_dbg(hdev, "Opening low level driver\n");
ret = hid_hw_open(hdev);
if (ret)
- return;
+ return ret;
if (!(data->device_flags & RMI_DEVICE))
- return;
+ return 0;
/* Allow incoming hid reports */
hid_device_io_start(hdev);
@@ -1095,7 +1228,9 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0);
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0);
- input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER);
+ ret = input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER);
+ if (ret < 0)
+ goto exit;
if (data->button_count) {
__set_bit(EV_KEY, input->evbit);
@@ -1111,6 +1246,7 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
exit:
hid_device_io_stop(hdev);
hid_hw_close(hdev);
+ return ret;
}
static int rmi_input_mapping(struct hid_device *hdev,
@@ -1270,6 +1406,7 @@ static struct hid_driver rmi_driver = {
.input_mapping = rmi_input_mapping,
.input_configured = rmi_input_configured,
#ifdef CONFIG_PM
+ .suspend = rmi_suspend,
.resume = rmi_post_resume,
.reset_resume = rmi_post_reset,
#endif
diff --git a/kernel/drivers/hid/hid-saitek.c b/kernel/drivers/hid/hid-saitek.c
index a014f2127..2f84b26f1 100644
--- a/kernel/drivers/hid/hid-saitek.c
+++ b/kernel/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/kernel/drivers/hid/hid-sensor-hub.c b/kernel/drivers/hid/hid-sensor-hub.c
index 090a1ba0a..92870cdb5 100644
--- a/kernel/drivers/hid/hid-sensor-hub.c
+++ b/kernel/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;
@@ -774,6 +788,12 @@ static const struct hid_device_id sensor_hub_devices[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_TEXAS_INSTRUMENTS,
USB_DEVICE_ID_TEXAS_INSTRUMENTS_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_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/kernel/drivers/hid/hid-sjoy.c b/kernel/drivers/hid/hid-sjoy.c
index 37845eccd..36b6470af 100644
--- a/kernel/drivers/hid/hid-sjoy.c
+++ b/kernel/drivers/hid/hid-sjoy.c
@@ -166,6 +166,9 @@ static const struct hid_device_id sjoy_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD),
.driver_data = HID_QUIRK_MULTI_INPUT |
HID_QUIRK_SKIP_OUTPUT_REPORTS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII),
+ .driver_data = HID_QUIRK_MULTI_INPUT |
+ HID_QUIRK_SKIP_OUTPUT_REPORTS },
{ }
};
MODULE_DEVICE_TABLE(hid, sjoy_devices);
diff --git a/kernel/drivers/hid/hid-sony.c b/kernel/drivers/hid/hid-sony.c
index 6ca96cebb..774cd2210 100644
--- a/kernel/drivers/hid/hid-sony.c
+++ b/kernel/drivers/hid/hid-sony.c
@@ -46,20 +46,37 @@
#define PS3REMOTE BIT(4)
#define DUALSHOCK4_CONTROLLER_USB BIT(5)
#define DUALSHOCK4_CONTROLLER_BT BIT(6)
+#define MOTION_CONTROLLER_USB BIT(7)
+#define MOTION_CONTROLLER_BT BIT(8)
+#define NAVIGATION_CONTROLLER_USB BIT(9)
+#define NAVIGATION_CONTROLLER_BT BIT(10)
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
+#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
+#define NAVIGATION_CONTROLLER (NAVIGATION_CONTROLLER_USB |\
+ NAVIGATION_CONTROLLER_BT)
#define DUALSHOCK4_CONTROLLER (DUALSHOCK4_CONTROLLER_USB |\
DUALSHOCK4_CONTROLLER_BT)
#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\
- DUALSHOCK4_CONTROLLER)
-#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
-#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
+ DUALSHOCK4_CONTROLLER | MOTION_CONTROLLER |\
+ NAVIGATION_CONTROLLER)
+#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
+ MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER)
+#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
+ MOTION_CONTROLLER)
#define MAX_LEDS 4
+/*
+ * The Sixaxis reports both digital and analog values for each button on the
+ * controller except for Start, Select and the PS button. The controller ends
+ * up reporting 27 axes which causes them to spill over into the multi-touch
+ * axis values. Additionally, the controller only has 20 actual, physical axes
+ * so there are several unused axes in between the used ones.
+ */
static __u8 sixaxis_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
- 0x09, 0x04, /* Usage (Joystik), */
+ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x85, 0x01, /* Report ID (1), */
@@ -134,6 +151,193 @@ static __u8 sixaxis_rdesc[] = {
0xC0 /* End Collection */
};
+/* PS/3 Motion controller */
+static __u8 motion_rdesc[] = {
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x04, /* Usage (Joystick), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0x01, /* Report ID (1), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x15, /* Report Count (21), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x35, 0x00, /* Physical Minimum (0), */
+ 0x45, 0x01, /* Physical Maximum (1), */
+ 0x05, 0x09, /* Usage Page (Button), */
+ 0x19, 0x01, /* Usage Minimum (01h), */
+ 0x29, 0x15, /* Usage Maximum (15h), */
+ 0x81, 0x02, /* Input (Variable), * Buttons */
+ 0x95, 0x0B, /* Report Count (11), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x81, 0x03, /* Input (Constant, Variable), * Padding */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0xA1, 0x00, /* Collection (Physical), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x35, 0x00, /* Physical Minimum (0), */
+ 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x81, 0x02, /* Input (Variable), * Trigger */
+ 0xC0, /* End Collection, */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x07, /* Report Count (7), * skip 7 bytes */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x46, 0xFF, 0xFF, /* Physical Maximum (65535), */
+ 0x27, 0xFF, 0xFF, 0x00, 0x00, /* Logical Maximum (65535), */
+ 0x95, 0x03, /* Report Count (3), * 3x Accels */
+ 0x09, 0x33, /* Usage (rX), */
+ 0x09, 0x34, /* Usage (rY), */
+ 0x09, 0x35, /* Usage (rZ), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x95, 0x03, /* Report Count (3), * Skip Accels 2nd frame */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0x95, 0x03, /* Report Count (3), * 3x Gyros */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x95, 0x03, /* Report Count (3), * Skip Gyros 2nd frame */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x0C, /* Report Size (12), */
+ 0x46, 0xFF, 0x0F, /* Physical Maximum (4095), */
+ 0x26, 0xFF, 0x0F, /* Logical Maximum (4095), */
+ 0x95, 0x04, /* Report Count (4), * Skip Temp and Magnetometers */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x95, 0x06, /* Report Count (6), * Skip Timestamp and Extension Bytes */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0x02, /* Report ID (2), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0xEE, /* Report ID (238), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0xEF, /* Report ID (239), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xC0 /* End Collection */
+};
+
+/* PS/3 Navigation controller */
+static __u8 navigation_rdesc[] = {
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x04, /* Usage (Joystik), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0x01, /* Report ID (1), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x81, 0x03, /* Input (Constant, Variable), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x13, /* Report Count (19), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x35, 0x00, /* Physical Minimum (0), */
+ 0x45, 0x01, /* Physical Maximum (1), */
+ 0x05, 0x09, /* Usage Page (Button), */
+ 0x19, 0x01, /* Usage Minimum (01h), */
+ 0x29, 0x13, /* Usage Maximum (13h), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x0D, /* Report Count (13), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x81, 0x03, /* Input (Constant, Variable), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xA1, 0x00, /* Collection (Physical), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x02, /* Report Count (2), */
+ 0x35, 0x00, /* Physical Minimum (0), */
+ 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x09, 0x31, /* Usage (Y), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xC0, /* End Collection, */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x95, 0x06, /* Report Count (6), */
+ 0x81, 0x03, /* Input (Constant, Variable), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x05, /* Report Count (5), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x95, 0x1E, /* Report Count (24), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0x02, /* Report ID (2), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0xEE, /* Report ID (238), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0xEF, /* Report ID (239), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xC0 /* End Collection */
+};
+
/*
* The default descriptor doesn't provide mapping for the accelerometers
* or orientation sensors. This fixed descriptor maps the accelerometers
@@ -798,12 +1002,20 @@ union sixaxis_output_report_01 {
__u8 buf[36];
};
+struct motion_output_report_02 {
+ u8 type, zero;
+ u8 r, g, b;
+ u8 zero2;
+ u8 rumble;
+};
+
#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 17
#define SIXAXIS_REPORT_0xF5_SIZE 8
+#define MOTION_REPORT_0x02_SIZE 49
static DEFINE_SPINLOCK(sony_dev_list_lock);
static LIST_HEAD(sony_device_list);
@@ -844,6 +1056,20 @@ static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
return sixaxis_rdesc;
}
+static u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc,
+ unsigned int *rsize)
+{
+ *rsize = sizeof(motion_rdesc);
+ return motion_rdesc;
+}
+
+static u8 *navigation_fixup(struct hid_device *hdev, u8 *rdesc,
+ unsigned int *rsize)
+{
+ *rsize = sizeof(navigation_rdesc);
+ return navigation_rdesc;
+}
+
static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
@@ -924,6 +1150,12 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
if (sc->quirks & SIXAXIS_CONTROLLER)
return sixaxis_fixup(hdev, rdesc, rsize);
+ if (sc->quirks & MOTION_CONTROLLER)
+ return motion_fixup(hdev, rdesc, rsize);
+
+ if (sc->quirks & NAVIGATION_CONTROLLER)
+ return navigation_fixup(hdev, rdesc, rsize);
+
if (sc->quirks & PS3REMOTE)
return ps3remote_fixup(hdev, rdesc, rsize);
@@ -934,6 +1166,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
{
static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
unsigned long flags;
+ int offset;
__u8 cable_state, battery_capacity, battery_charging;
/*
@@ -942,12 +1175,14 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
* It does not report the actual level while charging so it
* is set to 100% while charging is in progress.
*/
- if (rd[30] >= 0xee) {
+ offset = (sc->quirks & MOTION_CONTROLLER) ? 12 : 30;
+
+ if (rd[offset] >= 0xee) {
battery_capacity = 100;
- battery_charging = !(rd[30] & 0x01);
+ battery_charging = !(rd[offset] & 0x01);
cable_state = 1;
} else {
- __u8 index = rd[30] <= 5 ? rd[30] : 5;
+ __u8 index = rd[offset] <= 5 ? rd[offset] : 5;
battery_capacity = sixaxis_battery_capacity[index];
battery_charging = 0;
cable_state = 0;
@@ -1042,12 +1277,28 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
* has to be BYTE_SWAPPED before passing up to joystick interface
*/
if ((sc->quirks & SIXAXIS_CONTROLLER) && rd[0] == 0x01 && size == 49) {
+ /*
+ * When connected via Bluetooth the Sixaxis occasionally sends
+ * a report with the second byte 0xff and the rest zeroed.
+ *
+ * This report does not reflect the actual state of the
+ * controller must be ignored to avoid generating false input
+ * events.
+ */
+ if (rd[1] == 0xff)
+ return -EINVAL;
+
swap(rd[41], rd[42]);
swap(rd[43], rd[44]);
swap(rd[45], rd[46]);
swap(rd[47], rd[48]);
sixaxis_parse_report(sc, rd, size);
+ } else if ((sc->quirks & MOTION_CONTROLLER_BT) && rd[0] == 0x01 && size == 49) {
+ sixaxis_parse_report(sc, rd, size);
+ } else if ((sc->quirks & NAVIGATION_CONTROLLER) && rd[0] == 0x01 &&
+ size == 49) {
+ sixaxis_parse_report(sc, rd, size);
} else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
&& rd[0] == 0x11 && size == 78)) {
@@ -1109,20 +1360,27 @@ static int sony_register_touchpad(struct hid_input *hi, int touch_count,
return 0;
}
-static void sony_input_configured(struct hid_device *hdev,
+static int sony_input_configured(struct hid_device *hdev,
struct hid_input *hidinput)
{
struct sony_sc *sc = hid_get_drvdata(hdev);
+ int ret;
/*
* The Dualshock 4 touchpad supports 2 touches and has a
* resolution of 1920x942 (44.86 dots/mm).
*/
if (sc->quirks & DUALSHOCK4_CONTROLLER) {
- if (sony_register_touchpad(hidinput, 2, 1920, 942) != 0)
+ ret = sony_register_touchpad(hidinput, 2, 1920, 942);
+ if (ret) {
hid_err(sc->hdev,
- "Unable to initialize multi-touch slots\n");
+ "Unable to initialize multi-touch slots: %d\n",
+ ret);
+ return ret;
+ }
}
+
+ return 0;
}
/*
@@ -1208,7 +1466,7 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
return ret;
}
-static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+static void sixaxis_set_leds_from_id(struct sony_sc *sc)
{
static const __u8 sixaxis_leds[10][4] = {
{ 0x01, 0x00, 0x00, 0x00 },
@@ -1223,16 +1481,18 @@ static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
{ 0x01, 0x01, 0x01, 0x01 }
};
- BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
+ int id = sc->device_id;
+
+ BUILD_BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
if (id < 0)
return;
id %= 10;
- memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
+ memcpy(sc->led_state, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
}
-static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+static void dualshock4_set_leds_from_id(struct sony_sc *sc)
{
/* The first 4 color/index entries match what the PS4 assigns */
static const __u8 color_code[7][3] = {
@@ -1245,46 +1505,44 @@ static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
/* White */ { 0x01, 0x01, 0x01 }
};
- BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
+ int id = sc->device_id;
+
+ BUILD_BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
if (id < 0)
return;
id %= 7;
- memcpy(values, color_code[id], sizeof(color_code[id]));
+ memcpy(sc->led_state, color_code[id], sizeof(color_code[id]));
}
-static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
+static void buzz_set_leds(struct sony_sc *sc)
{
+ struct hid_device *hdev = sc->hdev;
struct list_head *report_list =
&hdev->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next,
struct hid_report, list);
__s32 *value = report->field[0]->value;
+ BUILD_BUG_ON(MAX_LEDS < 4);
+
value[0] = 0x00;
- value[1] = leds[0] ? 0xff : 0x00;
- value[2] = leds[1] ? 0xff : 0x00;
- value[3] = leds[2] ? 0xff : 0x00;
- value[4] = leds[3] ? 0xff : 0x00;
+ value[1] = sc->led_state[0] ? 0xff : 0x00;
+ value[2] = sc->led_state[1] ? 0xff : 0x00;
+ value[3] = sc->led_state[2] ? 0xff : 0x00;
+ value[4] = sc->led_state[3] ? 0xff : 0x00;
value[5] = 0x00;
value[6] = 0x00;
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
}
-static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count)
+static void sony_set_leds(struct sony_sc *sc)
{
- int n;
-
- BUG_ON(count > MAX_LEDS);
-
- if (sc->quirks & BUZZ_CONTROLLER && count == 4) {
- buzz_set_leds(sc->hdev, leds);
- } else {
- for (n = 0; n < count; n++)
- sc->led_state[n] = leds[n];
+ if (!(sc->quirks & BUZZ_CONTROLLER))
schedule_work(&sc->state_worker);
- }
+ else
+ buzz_set_leds(sc);
}
static void sony_led_set_brightness(struct led_classdev *led,
@@ -1324,8 +1582,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
drv_data->led_delay_on[n] = 0;
drv_data->led_delay_off[n] = 0;
- sony_set_leds(drv_data, drv_data->led_state,
- drv_data->led_count);
+ sony_set_leds(drv_data);
break;
}
}
@@ -1431,7 +1688,6 @@ static int sony_leds_init(struct sony_sc *sc)
const char *name_fmt;
static const char * const ds4_name_str[] = { "red", "green", "blue",
"global" };
- __u8 initial_values[MAX_LEDS] = { 0 };
__u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
__u8 use_hw_blink[MAX_LEDS] = { 0 };
@@ -1446,16 +1702,31 @@ static int sony_leds_init(struct sony_sc *sc)
if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
return -ENODEV;
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
- dualshock4_set_leds_from_id(sc->device_id, initial_values);
- initial_values[3] = 1;
+ dualshock4_set_leds_from_id(sc);
+ sc->led_state[3] = 1;
sc->led_count = 4;
memset(max_brightness, 255, 3);
use_hw_blink[3] = 1;
use_ds4_names = 1;
name_len = 0;
name_fmt = "%s:%s";
+ } else if (sc->quirks & MOTION_CONTROLLER) {
+ sc->led_count = 3;
+ memset(max_brightness, 255, 3);
+ use_ds4_names = 1;
+ name_len = 0;
+ name_fmt = "%s:%s";
+ } else if (sc->quirks & NAVIGATION_CONTROLLER) {
+ static const __u8 navigation_leds[4] = {0x01, 0x00, 0x00, 0x00};
+
+ memcpy(sc->led_state, navigation_leds, sizeof(navigation_leds));
+ sc->led_count = 1;
+ memset(use_hw_blink, 1, 4);
+ use_ds4_names = 0;
+ name_len = strlen("::sony#");
+ name_fmt = "%s::sony%d";
} else {
- sixaxis_set_leds_from_id(sc->device_id, initial_values);
+ sixaxis_set_leds_from_id(sc);
sc->led_count = 4;
memset(use_hw_blink, 1, 4);
use_ds4_names = 0;
@@ -1468,7 +1739,7 @@ static int sony_leds_init(struct sony_sc *sc)
* only relevant if the driver is loaded after somebody actively set the
* LEDs to on
*/
- sony_set_leds(sc, initial_values, sc->led_count);
+ sony_set_leds(sc);
name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
@@ -1491,7 +1762,7 @@ static int sony_leds_init(struct sony_sc *sc)
else
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
led->name = name;
- led->brightness = initial_values[n];
+ led->brightness = sc->led_state[n];
led->max_brightness = max_brightness[n];
led->brightness_get = sony_led_get_brightness;
led->brightness_set = sony_led_set_brightness;
@@ -1590,7 +1861,7 @@ static void dualshock4_state_worker(struct work_struct *work)
} else {
memset(buf, 0, DS4_REPORT_0x11_SIZE);
buf[0] = 0x11;
- buf[1] = 0xB0;
+ buf[1] = 0x80;
buf[3] = 0x0F;
offset = 6;
}
@@ -1622,9 +1893,31 @@ static void dualshock4_state_worker(struct work_struct *work)
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
}
+static void motion_state_worker(struct work_struct *work)
+{
+ struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
+ struct hid_device *hdev = sc->hdev;
+ struct motion_output_report_02 *report =
+ (struct motion_output_report_02 *)sc->output_report_dmabuf;
+
+ memset(report, 0, MOTION_REPORT_0x02_SIZE);
+
+ report->type = 0x02; /* set leds */
+ report->r = sc->led_state[0];
+ report->g = sc->led_state[1];
+ report->b = sc->led_state[2];
+
+#ifdef CONFIG_SONY_FF
+ report->rumble = max(sc->right, sc->left);
+#endif
+
+ hid_hw_output_report(hdev, (__u8 *)report, MOTION_REPORT_0x02_SIZE);
+}
+
static int sony_allocate_output_report(struct sony_sc *sc)
{
- if (sc->quirks & SIXAXIS_CONTROLLER)
+ if ((sc->quirks & SIXAXIS_CONTROLLER) ||
+ (sc->quirks & NAVIGATION_CONTROLLER))
sc->output_report_dmabuf =
kmalloc(sizeof(union sixaxis_output_report_01),
GFP_KERNEL);
@@ -1634,6 +1927,9 @@ static int sony_allocate_output_report(struct sony_sc *sc)
else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x05_SIZE,
GFP_KERNEL);
+ else if (sc->quirks & MOTION_CONTROLLER)
+ sc->output_report_dmabuf = kmalloc(MOTION_REPORT_0x02_SIZE,
+ GFP_KERNEL);
else
return 0;
@@ -1839,6 +2135,8 @@ static int sony_check_add(struct sony_sc *sc)
int n, ret;
if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
+ (sc->quirks & MOTION_CONTROLLER_BT) ||
+ (sc->quirks & NAVIGATION_CONTROLLER_BT) ||
(sc->quirks & SIXAXIS_CONTROLLER_BT)) {
/*
* sony_get_bt_devaddr() attempts to parse the Bluetooth MAC
@@ -1871,7 +2169,8 @@ static int sony_check_add(struct sony_sc *sc)
}
memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
- } else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
+ } else if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
+ (sc->quirks & NAVIGATION_CONTROLLER_USB)) {
buf = kmalloc(SIXAXIS_REPORT_0xF2_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -1993,19 +2292,20 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
- ret = sony_allocate_output_report(sc);
+ ret = sony_set_device_id(sc);
if (ret < 0) {
- hid_err(hdev, "failed to allocate the output report buffer\n");
+ hid_err(hdev, "failed to allocate the device id\n");
goto err_stop;
}
- ret = sony_set_device_id(sc);
+ ret = sony_allocate_output_report(sc);
if (ret < 0) {
- hid_err(hdev, "failed to allocate the device id\n");
+ hid_err(hdev, "failed to allocate the output report buffer\n");
goto err_stop;
}
- if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
+ if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
+ (sc->quirks & NAVIGATION_CONTROLLER_USB)) {
/*
* The Sony Sixaxis does not handle HID Output Reports on the
* Interrupt EP like it could, so we need to force HID Output
@@ -2020,7 +2320,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
ret = sixaxis_set_operational_usb(hdev);
sony_init_work(sc, sixaxis_state_worker);
- } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
+ } else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) ||
+ (sc->quirks & NAVIGATION_CONTROLLER_BT)) {
/*
* The Sixaxis wants output reports sent on the ctrl endpoint
* when connected via Bluetooth.
@@ -2043,6 +2344,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
sony_init_work(sc, dualshock4_state_worker);
+ } else if (sc->quirks & MOTION_CONTROLLER) {
+ sony_init_work(sc, motion_state_worker);
} else {
ret = 0;
}
@@ -2122,7 +2425,13 @@ static const struct hid_device_id sony_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
.driver_data = SIXAXIS_CONTROLLER_USB },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER),
- .driver_data = SIXAXIS_CONTROLLER_USB },
+ .driver_data = NAVIGATION_CONTROLLER_USB },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER),
+ .driver_data = NAVIGATION_CONTROLLER_BT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER),
+ .driver_data = MOTION_CONTROLLER_USB },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER),
+ .driver_data = MOTION_CONTROLLER_BT },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
.driver_data = SIXAXIS_CONTROLLER_BT },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE),
diff --git a/kernel/drivers/hid/hid-uclogic.c b/kernel/drivers/hid/hid-uclogic.c
index b905d501e..85ac43517 100644
--- a/kernel/drivers/hid/hid-uclogic.c
+++ b/kernel/drivers/hid/hid-uclogic.c
@@ -731,7 +731,7 @@ static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return 0;
}
-static void uclogic_input_configured(struct hid_device *hdev,
+static int uclogic_input_configured(struct hid_device *hdev,
struct hid_input *hi)
{
char *name;
@@ -741,7 +741,7 @@ static void uclogic_input_configured(struct hid_device *hdev,
/* no report associated (HID_QUIRK_MULTI_INPUT not set) */
if (!hi->report)
- return;
+ return 0;
field = hi->report->field[0];
@@ -774,6 +774,8 @@ static void uclogic_input_configured(struct hid_device *hdev,
hi->input->name = name;
}
}
+
+ return 0;
}
/**
diff --git a/kernel/drivers/hid/i2c-hid/i2c-hid.c b/kernel/drivers/hid/i2c-hid/i2c-hid.c
index 92d6cdf02..10bd8e6e4 100644
--- a/kernel/drivers/hid/i2c-hid/i2c-hid.c
+++ b/kernel/drivers/hid/i2c-hid/i2c-hid.c
@@ -42,9 +42,9 @@
#include <linux/i2c/i2c-hid.h>
/* flags */
-#define I2C_HID_STARTED (1 << 0)
-#define I2C_HID_RESET_PENDING (1 << 1)
-#define I2C_HID_READ_PENDING (1 << 2)
+#define I2C_HID_STARTED 0
+#define I2C_HID_RESET_PENDING 1
+#define I2C_HID_READ_PENDING 2
#define I2C_HID_PWR_ON 0x00
#define I2C_HID_PWR_SLEEP 0x01
@@ -149,6 +149,8 @@ struct i2c_hid {
int irq;
struct i2c_hid_platform_data pdata;
+
+ bool irq_wake_enabled;
};
static int __i2c_hid_command(struct i2c_client *client,
@@ -1019,7 +1021,6 @@ static int i2c_hid_probe(struct i2c_client *client,
hid->driver_data = client;
hid->ll_driver = &i2c_hid_ll_driver;
hid->dev.parent = &client->dev;
- ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
hid->bus = BUS_I2C;
hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
@@ -1027,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) {
@@ -1092,14 +1094,21 @@ static int i2c_hid_suspend(struct device *dev)
struct i2c_hid *ihid = i2c_get_clientdata(client);
struct hid_device *hid = ihid->hid;
int ret = 0;
-
- disable_irq(ihid->irq);
- if (device_may_wakeup(&client->dev))
- enable_irq_wake(ihid->irq);
+ int wake_status;
if (hid->driver && hid->driver->suspend)
ret = hid->driver->suspend(hid, PMSG_SUSPEND);
+ disable_irq(ihid->irq);
+ if (device_may_wakeup(&client->dev)) {
+ wake_status = enable_irq_wake(ihid->irq);
+ if (!wake_status)
+ ihid->irq_wake_enabled = true;
+ else
+ hid_warn(hid, "Failed to enable irq wake: %d\n",
+ wake_status);
+ }
+
/* Save some power */
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
@@ -1112,14 +1121,21 @@ static int i2c_hid_resume(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
struct i2c_hid *ihid = i2c_get_clientdata(client);
struct hid_device *hid = ihid->hid;
+ int wake_status;
enable_irq(ihid->irq);
ret = i2c_hid_hwreset(client);
if (ret)
return ret;
- if (device_may_wakeup(&client->dev))
- disable_irq_wake(ihid->irq);
+ if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) {
+ wake_status = disable_irq_wake(ihid->irq);
+ if (!wake_status)
+ ihid->irq_wake_enabled = false;
+ else
+ hid_warn(hid, "Failed to disable irq wake: %d\n",
+ wake_status);
+ }
if (hid->driver && hid->driver->reset_resume) {
ret = hid->driver->reset_resume(hid);
diff --git a/kernel/drivers/hid/usbhid/hid-core.c b/kernel/drivers/hid/usbhid/hid-core.c
index eab5bd6a2..5dd426fee 100644
--- a/kernel/drivers/hid/usbhid/hid-core.c
+++ b/kernel/drivers/hid/usbhid/hid-core.c
@@ -477,8 +477,6 @@ static void hid_ctrl(struct urb *urb)
struct usbhid_device *usbhid = hid->driver_data;
int unplug = 0, status = urb->status;
- spin_lock(&usbhid->lock);
-
switch (status) {
case 0: /* success */
if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
@@ -498,6 +496,8 @@ static void hid_ctrl(struct urb *urb)
hid_warn(urb->dev, "ctrl urb status %d received\n", status);
}
+ spin_lock(&usbhid->lock);
+
if (unplug) {
usbhid->ctrltail = usbhid->ctrlhead;
} else {
@@ -710,7 +710,8 @@ int usbhid_open(struct hid_device *hid)
* Wait 50 msec for the queue to empty before allowing events
* to go through hid.
*/
- msleep(50);
+ if (res == 0 && !(hid->quirks & HID_QUIRK_ALWAYS_POLL))
+ msleep(50);
clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
}
done:
diff --git a/kernel/drivers/hid/usbhid/hid-quirks.c b/kernel/drivers/hid/usbhid/hid-quirks.c
index 4696895eb..7dd0953cd 100644
--- a/kernel/drivers/hid/usbhid/hid-quirks.c
+++ b/kernel/drivers/hid/usbhid/hid-quirks.c
@@ -52,7 +52,6 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH_2968, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
- { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_AIREN, USB_DEVICE_ID_AIREN_SLIMPLUS, HID_QUIRK_NOGET },
@@ -70,12 +69,10 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
{ 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_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_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_ELAN, HID_ANY_ID, 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 },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
@@ -83,12 +80,19 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077, HID_QUIRK_ALWAYS_POLL },
+ { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KEYBOARD_G710_PLUS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C01A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C05A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C06A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2, HID_QUIRK_NO_INIT_REPORTS },
+ { 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_TYPE_COVER_3_JP, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, 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 },
@@ -113,7 +117,8 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_1, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_2, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8882, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8883, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_KNA5, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWA60, HID_QUIRK_MULTI_INPUT },
@@ -141,6 +146,9 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_INPUT_REPORTS },
+ { USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_2NES2SNES, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_4NES4SNES, HID_QUIRK_MULTI_INPUT },
{ 0, 0 }
};
@@ -328,7 +336,8 @@ static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor,
for (; hid_blacklist[n].idVendor; n++)
if (hid_blacklist[n].idVendor == idVendor &&
- hid_blacklist[n].idProduct == idProduct)
+ (hid_blacklist[n].idProduct == (__u16) HID_ANY_ID ||
+ hid_blacklist[n].idProduct == idProduct))
bl_entry = &hid_blacklist[n];
if (bl_entry != NULL)
diff --git a/kernel/drivers/hid/wacom.h b/kernel/drivers/hid/wacom.h
index 024f4d89d..4681a65a4 100644
--- a/kernel/drivers/hid/wacom.h
+++ b/kernel/drivers/hid/wacom.h
@@ -113,7 +113,7 @@ struct wacom {
struct mutex lock;
struct work_struct work;
struct wacom_led {
- u8 select[2]; /* status led selector (0..3) */
+ u8 select[5]; /* status led selector (0..3) */
u8 llv; /* status led brightness no button (1..127) */
u8 hlv; /* status led brightness button pressed (1..127) */
u8 img_lum; /* OLED matrix display brightness */
@@ -123,6 +123,8 @@ struct wacom {
struct power_supply *ac;
struct power_supply_desc battery_desc;
struct power_supply_desc ac_desc;
+ struct kobject *remote_dir;
+ struct attribute_group remote_group[5];
};
static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
@@ -134,8 +136,10 @@ static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
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_pentouch_input_capabilities(struct input_dev *input_dev,
+void wacom_setup_device_quirks(struct wacom *wacom);
+int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
+ struct wacom_wac *wacom_wac);
+int wacom_setup_touch_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);
@@ -145,4 +149,7 @@ int wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value);
void wacom_wac_report(struct hid_device *hdev, struct hid_report *report);
void wacom_battery_work(struct work_struct *work);
+int wacom_remote_create_attr_group(struct wacom *wacom, __u32 serial,
+ int index);
+void wacom_remote_destroy_attr_group(struct wacom *wacom, __u32 serial);
#endif
diff --git a/kernel/drivers/hid/wacom_sys.c b/kernel/drivers/hid/wacom_sys.c
index e8607d096..e06af5b9f 100644
--- a/kernel/drivers/hid/wacom_sys.c
+++ b/kernel/drivers/hid/wacom_sys.c
@@ -23,9 +23,13 @@
#define WAC_CMD_ICON_XFER 0x23
#define WAC_CMD_ICON_BT_XFER 0x26
#define WAC_CMD_RETRIES 10
+#define WAC_CMD_DELETE_PAIRING 0x20
+#define WAC_CMD_UNPAIR_ALL 0xFF
+#define WAC_REMOTE_SERIAL_MAX_STRLEN 9
#define DEV_ATTR_RW_PERM (S_IRUGO | S_IWUSR | S_IWGRP)
#define DEV_ATTR_WO_PERM (S_IWUSR | S_IWGRP)
+#define DEV_ATTR_RO_PERM (S_IRUSR | S_IRGRP)
static int wacom_get_report(struct hid_device *hdev, u8 type, u8 *buf,
size_t size, unsigned int retries)
@@ -35,7 +39,11 @@ static int wacom_get_report(struct hid_device *hdev, u8 type, u8 *buf,
do {
retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
HID_REQ_GET_REPORT);
- } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+ } while ((retval == -ETIMEDOUT || retval == -EAGAIN) && --retries);
+
+ if (retval < 0)
+ hid_err(hdev, "wacom_get_report: ran out of retries "
+ "(last error = %d)\n", retval);
return retval;
}
@@ -48,7 +56,11 @@ static int wacom_set_report(struct hid_device *hdev, u8 type, u8 *buf,
do {
retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
HID_REQ_SET_REPORT);
- } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+ } while ((retval == -ETIMEDOUT || retval == -EAGAIN) && --retries);
+
+ if (retval < 0)
+ hid_err(hdev, "wacom_set_report: ran out of retries "
+ "(last error = %d)\n", retval);
return retval;
}
@@ -117,9 +129,16 @@ static void wacom_feature_mapping(struct hid_device *hdev,
break;
data[0] = field->report->id;
ret = wacom_get_report(hdev, HID_FEATURE_REPORT,
- data, 2, 0);
- if (ret == 2)
+ data, 2, WAC_CMD_RETRIES);
+ if (ret == 2) {
features->touch_max = data[1];
+ } else {
+ features->touch_max = 16;
+ hid_warn(hdev, "wacom_feature_mapping: "
+ "could not get HID_DG_CONTACTMAX, "
+ "defaulting to %d\n",
+ features->touch_max);
+ }
kfree(data);
}
break;
@@ -181,14 +200,18 @@ static void wacom_usage_mapping(struct hid_device *hdev,
* X/Y values and some cases of invalid Digitizer X/Y
* values commonly reported.
*/
- if (!pen && !finger)
+ if (pen)
+ features->device_type |= WACOM_DEVICETYPE_PEN;
+ else if (finger)
+ features->device_type |= WACOM_DEVICETYPE_TOUCH;
+ else
return;
/*
* 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;
@@ -198,21 +221,20 @@ static void wacom_usage_mapping(struct hid_device *hdev,
case HID_GD_X:
features->x_max = field->logical_maximum;
if (finger) {
- features->device_type = BTN_TOOL_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;
}
- } else {
- features->device_type = BTN_TOOL_PEN;
}
break;
case HID_GD_Y:
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;
}
@@ -237,7 +259,7 @@ static void wacom_post_parse_hid(struct hid_device *hdev,
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_init_slots(wacom_wac->touch_input, wacom_wac->features.touch_max,
INPUT_MT_DIRECT);
}
}
@@ -319,7 +341,7 @@ static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
if (error >= 0)
error = wacom_get_report(hdev, HID_FEATURE_REPORT,
rep_data, length, 1);
- } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES);
+ } while (error >= 0 && rep_data[1] != mode && limit++ < WAC_MSG_RETRIES);
kfree(rep_data);
@@ -395,12 +417,12 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
if (features->type == HID_GENERIC)
return wacom_hid_set_device_mode(hdev);
- if (features->device_type == BTN_TOOL_FINGER) {
+ if (features->device_type & WACOM_DEVICETYPE_TOUCH) {
if (features->type > TABLETPC) {
/* 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) {
@@ -409,8 +431,8 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
else if (features->type == BAMBOO_PAD) {
return wacom_set_device_mode(hdev, 2, 2, 2);
}
- } else if (features->device_type == BTN_TOOL_PEN) {
- if (features->type <= BAMBOO_PT && features->type != WIRELESS) {
+ } else if (features->device_type & WACOM_DEVICETYPE_PEN) {
+ if (features->type <= BAMBOO_PT) {
return wacom_set_device_mode(hdev, 2, 2, 2);
}
}
@@ -425,7 +447,6 @@ static void wacom_retrieve_hid_descriptor(struct hid_device *hdev,
struct usb_interface *intf = wacom->intf;
/* default features */
- features->device_type = BTN_TOOL_PEN;
features->x_fuzz = 4;
features->y_fuzz = 4;
features->pressure_fuzz = 0;
@@ -438,17 +459,12 @@ static void wacom_retrieve_hid_descriptor(struct hid_device *hdev,
* interface number.
*/
if (features->type == WIRELESS) {
- if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
- features->device_type = 0;
- } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) {
- features->device_type = BTN_TOOL_FINGER;
- features->pktlen = WACOM_PKGLEN_BBTOUCH3;
- }
- }
-
- /* only devices that support touch need to retrieve the info */
- if (features->type < BAMBOO_PT)
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 0)
+ features->device_type = WACOM_DEVICETYPE_WL_MONITOR;
+ else
+ features->device_type = WACOM_DEVICETYPE_NONE;
return;
+ }
wacom_parse_hid(hdev, features);
}
@@ -527,9 +543,9 @@ static int wacom_add_shared_data(struct hid_device *hdev)
wacom_wac->shared = &data->shared;
- if (wacom_wac->features.device_type == BTN_TOOL_FINGER)
+ if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)
wacom_wac->shared->touch = hdev;
- else if (wacom_wac->features.device_type == BTN_TOOL_PEN)
+ else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN)
wacom_wac->shared->pen = hdev;
out:
@@ -848,6 +864,9 @@ static int wacom_initialize_leds(struct wacom *wacom)
{
int error;
+ if (!(wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD))
+ return 0;
+
/* Initialize default values */
switch (wacom->wacom_wac.features.type) {
case INTUOS4S:
@@ -881,17 +900,14 @@ static int wacom_initialize_leds(struct wacom *wacom)
case INTUOSPS:
case INTUOSPM:
case INTUOSPL:
- if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) {
- wacom->led.select[0] = 0;
- wacom->led.select[1] = 0;
- wacom->led.llv = 32;
- wacom->led.hlv = 0;
- wacom->led.img_lum = 0;
-
- error = sysfs_create_group(&wacom->hdev->dev.kobj,
- &intuos5_led_attr_group);
- } else
- return 0;
+ wacom->led.select[0] = 0;
+ wacom->led.select[1] = 0;
+ wacom->led.llv = 32;
+ wacom->led.hlv = 0;
+ wacom->led.img_lum = 0;
+
+ error = sysfs_create_group(&wacom->hdev->dev.kobj,
+ &intuos5_led_attr_group);
break;
default:
@@ -914,6 +930,9 @@ static void wacom_destroy_leds(struct wacom *wacom)
if (!wacom->led_initialized)
return;
+ if (!(wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD))
+ return;
+
wacom->led_initialized = false;
switch (wacom->wacom_wac.features.type) {
@@ -937,9 +956,8 @@ static void wacom_destroy_leds(struct wacom *wacom)
case INTUOSPS:
case INTUOSPM:
case INTUOSPL:
- if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN)
- sysfs_remove_group(&wacom->hdev->dev.kobj,
- &intuos5_led_attr_group);
+ sysfs_remove_group(&wacom->hdev->dev.kobj,
+ &intuos5_led_attr_group);
break;
}
}
@@ -1107,6 +1125,189 @@ static ssize_t wacom_store_speed(struct device *dev,
static DEVICE_ATTR(speed, DEV_ATTR_RW_PERM,
wacom_show_speed, wacom_store_speed);
+
+static ssize_t wacom_show_remote_mode(struct kobject *kobj,
+ struct kobj_attribute *kattr,
+ char *buf, int index)
+{
+ struct device *dev = container_of(kobj->parent, struct device, kobj);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct wacom *wacom = hid_get_drvdata(hdev);
+ u8 mode;
+
+ mode = wacom->led.select[index];
+ if (mode >= 0 && mode < 3)
+ return snprintf(buf, PAGE_SIZE, "%d\n", mode);
+ else
+ return snprintf(buf, PAGE_SIZE, "%d\n", -1);
+}
+
+#define DEVICE_EKR_ATTR_GROUP(SET_ID) \
+static ssize_t wacom_show_remote##SET_ID##_mode(struct kobject *kobj, \
+ struct kobj_attribute *kattr, char *buf) \
+{ \
+ return wacom_show_remote_mode(kobj, kattr, buf, SET_ID); \
+} \
+static struct kobj_attribute remote##SET_ID##_mode_attr = { \
+ .attr = {.name = "remote_mode", \
+ .mode = DEV_ATTR_RO_PERM}, \
+ .show = wacom_show_remote##SET_ID##_mode, \
+}; \
+static struct attribute *remote##SET_ID##_serial_attrs[] = { \
+ &remote##SET_ID##_mode_attr.attr, \
+ NULL \
+}; \
+static struct attribute_group remote##SET_ID##_serial_group = { \
+ .name = NULL, \
+ .attrs = remote##SET_ID##_serial_attrs, \
+}
+
+DEVICE_EKR_ATTR_GROUP(0);
+DEVICE_EKR_ATTR_GROUP(1);
+DEVICE_EKR_ATTR_GROUP(2);
+DEVICE_EKR_ATTR_GROUP(3);
+DEVICE_EKR_ATTR_GROUP(4);
+
+int wacom_remote_create_attr_group(struct wacom *wacom, __u32 serial, int index)
+{
+ int error = 0;
+ char *buf;
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
+ wacom_wac->serial[index] = serial;
+
+ buf = kzalloc(WAC_REMOTE_SERIAL_MAX_STRLEN, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ snprintf(buf, WAC_REMOTE_SERIAL_MAX_STRLEN, "%d", serial);
+ wacom->remote_group[index].name = buf;
+
+ error = sysfs_create_group(wacom->remote_dir,
+ &wacom->remote_group[index]);
+ if (error) {
+ hid_err(wacom->hdev,
+ "cannot create sysfs group err: %d\n", error);
+ kobject_put(wacom->remote_dir);
+ return error;
+ }
+
+ return 0;
+}
+
+void wacom_remote_destroy_attr_group(struct wacom *wacom, __u32 serial)
+{
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ int i;
+
+ if (!serial)
+ return;
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ if (wacom_wac->serial[i] == serial) {
+ wacom_wac->serial[i] = 0;
+ wacom->led.select[i] = WACOM_STATUS_UNKNOWN;
+ if (wacom->remote_group[i].name) {
+ sysfs_remove_group(wacom->remote_dir,
+ &wacom->remote_group[i]);
+ kfree(wacom->remote_group[i].name);
+ wacom->remote_group[i].name = NULL;
+ }
+ }
+ }
+}
+
+static int wacom_cmd_unpair_remote(struct wacom *wacom, unsigned char selector)
+{
+ const size_t buf_size = 2;
+ unsigned char *buf;
+ int retval;
+
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = WAC_CMD_DELETE_PAIRING;
+ buf[1] = selector;
+
+ retval = wacom_set_report(wacom->hdev, HID_OUTPUT_REPORT, buf,
+ buf_size, WAC_CMD_RETRIES);
+ kfree(buf);
+
+ return retval;
+}
+
+static ssize_t wacom_store_unpair_remote(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned char selector = 0;
+ struct device *dev = container_of(kobj->parent, struct device, kobj);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct wacom *wacom = hid_get_drvdata(hdev);
+ int err;
+
+ if (!strncmp(buf, "*\n", 2)) {
+ selector = WAC_CMD_UNPAIR_ALL;
+ } else {
+ hid_info(wacom->hdev, "remote: unrecognized unpair code: %s\n",
+ buf);
+ return -1;
+ }
+
+ mutex_lock(&wacom->lock);
+
+ err = wacom_cmd_unpair_remote(wacom, selector);
+ mutex_unlock(&wacom->lock);
+
+ return err < 0 ? err : count;
+}
+
+static struct kobj_attribute unpair_remote_attr = {
+ .attr = {.name = "unpair_remote", .mode = 0200},
+ .store = wacom_store_unpair_remote,
+};
+
+static const struct attribute *remote_unpair_attrs[] = {
+ &unpair_remote_attr.attr,
+ NULL
+};
+
+static int wacom_initialize_remote(struct wacom *wacom)
+{
+ int error = 0;
+ struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
+ int i;
+
+ if (wacom->wacom_wac.features.type != REMOTE)
+ return 0;
+
+ wacom->remote_group[0] = remote0_serial_group;
+ wacom->remote_group[1] = remote1_serial_group;
+ wacom->remote_group[2] = remote2_serial_group;
+ wacom->remote_group[3] = remote3_serial_group;
+ wacom->remote_group[4] = remote4_serial_group;
+
+ wacom->remote_dir = kobject_create_and_add("wacom_remote",
+ &wacom->hdev->dev.kobj);
+ if (!wacom->remote_dir)
+ return -ENOMEM;
+
+ error = sysfs_create_files(wacom->remote_dir, remote_unpair_attrs);
+
+ if (error) {
+ hid_err(wacom->hdev,
+ "cannot create sysfs group err: %d\n", error);
+ return error;
+ }
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ wacom->led.select[i] = WACOM_STATUS_UNKNOWN;
+ wacom_wac->serial[i] = 0;
+ }
+
+ return 0;
+}
+
static struct input_dev *wacom_allocate_input(struct wacom *wacom)
{
struct input_dev *input_dev;
@@ -1117,7 +1318,7 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom)
if (!input_dev)
return NULL;
- input_dev->name = wacom_wac->name;
+ input_dev->name = wacom_wac->features.name;
input_dev->phys = hdev->phys;
input_dev->dev.parent = &hdev->dev;
input_dev->open = wacom_open;
@@ -1132,74 +1333,90 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom)
return input_dev;
}
-static void wacom_free_inputs(struct wacom *wacom)
+static void wacom_clean_inputs(struct wacom *wacom)
{
- struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
-
- if (wacom_wac->input)
- input_free_device(wacom_wac->input);
- if (wacom_wac->pad_input)
- input_free_device(wacom_wac->pad_input);
- wacom_wac->input = NULL;
- wacom_wac->pad_input = NULL;
+ if (wacom->wacom_wac.pen_input) {
+ if (wacom->wacom_wac.pen_registered)
+ input_unregister_device(wacom->wacom_wac.pen_input);
+ else
+ input_free_device(wacom->wacom_wac.pen_input);
+ }
+ if (wacom->wacom_wac.touch_input) {
+ if (wacom->wacom_wac.touch_registered)
+ input_unregister_device(wacom->wacom_wac.touch_input);
+ else
+ input_free_device(wacom->wacom_wac.touch_input);
+ }
+ if (wacom->wacom_wac.pad_input) {
+ if (wacom->wacom_wac.pad_registered)
+ input_unregister_device(wacom->wacom_wac.pad_input);
+ else
+ input_free_device(wacom->wacom_wac.pad_input);
+ }
+ if (wacom->remote_dir)
+ kobject_put(wacom->remote_dir);
+ wacom->wacom_wac.pen_input = NULL;
+ wacom->wacom_wac.touch_input = NULL;
+ wacom->wacom_wac.pad_input = NULL;
+ wacom_destroy_leds(wacom);
}
static int wacom_allocate_inputs(struct wacom *wacom)
{
- struct input_dev *input_dev, *pad_input_dev;
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
- input_dev = wacom_allocate_input(wacom);
- pad_input_dev = wacom_allocate_input(wacom);
- if (!input_dev || !pad_input_dev) {
- wacom_free_inputs(wacom);
+ wacom_wac->pen_input = wacom_allocate_input(wacom);
+ wacom_wac->touch_input = wacom_allocate_input(wacom);
+ wacom_wac->pad_input = wacom_allocate_input(wacom);
+ if (!wacom_wac->pen_input || !wacom_wac->touch_input || !wacom_wac->pad_input) {
+ wacom_clean_inputs(wacom);
return -ENOMEM;
}
- wacom_wac->input = input_dev;
- wacom_wac->pad_input = pad_input_dev;
+ wacom_wac->pen_input->name = wacom_wac->pen_name;
+ wacom_wac->touch_input->name = wacom_wac->touch_name;
wacom_wac->pad_input->name = wacom_wac->pad_name;
return 0;
}
-static void wacom_clean_inputs(struct wacom *wacom)
-{
- if (wacom->wacom_wac.input) {
- if (wacom->wacom_wac.input_registered)
- input_unregister_device(wacom->wacom_wac.input);
- else
- input_free_device(wacom->wacom_wac.input);
- }
- if (wacom->wacom_wac.pad_input) {
- if (wacom->wacom_wac.pad_registered)
- input_unregister_device(wacom->wacom_wac.pad_input);
- else
- input_free_device(wacom->wacom_wac.pad_input);
- }
- wacom->wacom_wac.input = NULL;
- wacom->wacom_wac.pad_input = NULL;
- wacom_destroy_leds(wacom);
-}
-
static int wacom_register_inputs(struct wacom *wacom)
{
- struct input_dev *input_dev, *pad_input_dev;
+ struct input_dev *pen_input_dev, *touch_input_dev, *pad_input_dev;
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
- int error;
+ int error = 0;
- input_dev = wacom_wac->input;
+ pen_input_dev = wacom_wac->pen_input;
+ touch_input_dev = wacom_wac->touch_input;
pad_input_dev = wacom_wac->pad_input;
- if (!input_dev || !pad_input_dev)
+ if (!pen_input_dev || !touch_input_dev || !pad_input_dev)
return -EINVAL;
- error = wacom_setup_pentouch_input_capabilities(input_dev, wacom_wac);
- if (!error) {
- error = input_register_device(input_dev);
+ error = wacom_setup_pen_input_capabilities(pen_input_dev, wacom_wac);
+ if (error) {
+ /* no pen in use on this interface */
+ input_free_device(pen_input_dev);
+ wacom_wac->pen_input = NULL;
+ pen_input_dev = NULL;
+ } else {
+ error = input_register_device(pen_input_dev);
+ if (error)
+ goto fail_register_pen_input;
+ wacom_wac->pen_registered = true;
+ }
+
+ error = wacom_setup_touch_input_capabilities(touch_input_dev, wacom_wac);
+ if (error) {
+ /* no touch in use on this interface */
+ input_free_device(touch_input_dev);
+ wacom_wac->touch_input = NULL;
+ touch_input_dev = NULL;
+ } else {
+ error = input_register_device(touch_input_dev);
if (error)
- return error;
- wacom_wac->input_registered = true;
+ goto fail_register_touch_input;
+ wacom_wac->touch_registered = true;
}
error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac);
@@ -1217,21 +1434,67 @@ static int wacom_register_inputs(struct wacom *wacom)
error = wacom_initialize_leds(wacom);
if (error)
goto fail_leds;
+
+ error = wacom_initialize_remote(wacom);
+ if (error)
+ goto fail_remote;
}
return 0;
+fail_remote:
+ wacom_destroy_leds(wacom);
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;
+ if (touch_input_dev)
+ input_unregister_device(touch_input_dev);
+ wacom_wac->touch_input = NULL;
+ wacom_wac->touch_registered = false;
+fail_register_touch_input:
+ if (pen_input_dev)
+ input_unregister_device(pen_input_dev);
+ wacom_wac->pen_input = NULL;
+ wacom_wac->pen_registered = false;
+fail_register_pen_input:
return error;
}
+/*
+ * Not all devices report physical dimensions from HID.
+ * Compute the default from hardcoded logical dimension
+ * and resolution before driver overwrites them.
+ */
+static void wacom_set_default_phy(struct wacom_features *features)
+{
+ if (features->x_resolution) {
+ features->x_phy = (features->x_max * 100) /
+ features->x_resolution;
+ features->y_phy = (features->y_max * 100) /
+ features->y_resolution;
+ }
+}
+
+static void wacom_calculate_res(struct wacom_features *features)
+{
+ /* set unit to "100th of a mm" for devices not reported by HID */
+ if (!features->unit) {
+ features->unit = 0x11;
+ features->unitExpo = -3;
+ }
+
+ features->x_resolution = wacom_calc_hid_res(features->x_max,
+ features->x_phy,
+ features->unit,
+ features->unitExpo);
+ features->y_resolution = wacom_calc_hid_res(features->y_max,
+ features->y_phy,
+ features->unit,
+ features->unitExpo);
+}
+
static void wacom_wireless_work(struct work_struct *work)
{
struct wacom *wacom = container_of(work, struct wacom, work);
@@ -1285,11 +1548,17 @@ static void wacom_wireless_work(struct work_struct *work)
/* Stylus interface */
wacom_wac1->features =
*((struct wacom_features *)id->driver_data);
- wacom_wac1->features.device_type = BTN_TOOL_PEN;
- snprintf(wacom_wac1->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.device_type |= WACOM_DEVICETYPE_PEN;
+ 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);
+ 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;
@@ -1300,29 +1569,34 @@ 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;
- wacom_wac2->features.device_type = BTN_TOOL_FINGER;
+ wacom_set_default_phy(&wacom_wac2->features);
wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
- if (wacom_wac2->features.touch_max)
- snprintf(wacom_wac2->name, WACOM_NAME_MAX,
- "%s (WL) Finger",wacom_wac2->features.name);
- else
- snprintf(wacom_wac2->name, WACOM_NAME_MAX,
+ wacom_calculate_res(&wacom_wac2->features);
+ snprintf(wacom_wac2->touch_name, WACOM_NAME_MAX,
+ "%s (WL) Finger",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) {
+ snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
"%s (WL) Pad",wacom_wac2->features.name);
- 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->input;
+ wacom_wac->shared->touch_input = wacom_wac2->touch_input;
}
error = wacom_initialize_battery(wacom);
@@ -1352,33 +1626,6 @@ void wacom_battery_work(struct work_struct *work)
}
}
-/*
- * Not all devices report physical dimensions from HID.
- * Compute the default from hardcoded logical dimension
- * and resolution before driver overwrites them.
- */
-static void wacom_set_default_phy(struct wacom_features *features)
-{
- if (features->x_resolution) {
- features->x_phy = (features->x_max * 100) /
- features->x_resolution;
- features->y_phy = (features->y_max * 100) /
- features->y_resolution;
- }
-}
-
-static void wacom_calculate_res(struct wacom_features *features)
-{
- features->x_resolution = wacom_calc_hid_res(features->x_max,
- features->x_phy,
- features->unit,
- features->unitExpo);
- features->y_resolution = wacom_calc_hid_res(features->y_max,
- features->y_phy,
- features->unit,
- features->unitExpo);
-}
-
static size_t wacom_compute_pktlen(struct hid_device *hdev)
{
struct hid_report_enum *report_enum;
@@ -1396,6 +1643,49 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev)
return size;
}
+static void wacom_update_name(struct wacom *wacom)
+{
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct wacom_features *features = &wacom_wac->features;
+ char name[WACOM_NAME_MAX];
+
+ /* Generic devices name unspecified */
+ if ((features->type == HID_GENERIC) && !strcmp("Wacom HID", features->name)) {
+ if (strstr(wacom->hdev->name, "Wacom") ||
+ strstr(wacom->hdev->name, "wacom") ||
+ strstr(wacom->hdev->name, "WACOM")) {
+ /* name is in HID descriptor, use it */
+ strlcpy(name, wacom->hdev->name, sizeof(name));
+
+ /* strip out excess whitespaces */
+ while (1) {
+ char *gap = strstr(name, " ");
+ if (gap == NULL)
+ break;
+ /* shift everything including the terminator */
+ memmove(gap, gap+1, strlen(gap));
+ }
+ /* get rid of trailing whitespace */
+ if (name[strlen(name)-1] == ' ')
+ name[strlen(name)-1] = '\0';
+ } else {
+ /* no meaningful name retrieved. use product ID */
+ snprintf(name, sizeof(name),
+ "%s %X", features->name, wacom->hdev->product);
+ }
+ } else {
+ strlcpy(name, features->name, sizeof(name));
+ }
+
+ /* Append the device type to the name */
+ snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
+ "%s Pen", name);
+ snprintf(wacom_wac->touch_name, sizeof(wacom_wac->touch_name),
+ "%s Finger", name);
+ snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
+ "%s Pad", name);
+}
+
static int wacom_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
@@ -1448,11 +1738,9 @@ static int wacom_probe(struct hid_device *hdev,
mutex_init(&wacom->lock);
INIT_WORK(&wacom->work, wacom_wireless_work);
- if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
- error = wacom_allocate_inputs(wacom);
- if (error)
- goto fail_allocate_inputs;
- }
+ error = wacom_allocate_inputs(wacom);
+ if (error)
+ goto fail_allocate_inputs;
/*
* Bamboo Pad has a generic hid handling for the Pen, and we switch it
@@ -1474,81 +1762,40 @@ static int wacom_probe(struct hid_device *hdev,
/* Retrieve the physical and logical size for touch devices */
wacom_retrieve_hid_descriptor(hdev, features);
+ wacom_setup_device_quirks(wacom);
- /*
- * Intuos5 has no useful data about its touch interface in its
- * HID descriptor. If this is the touch interface (PacketSize
- * of WACOM_PKGLEN_BBTOUCH3), override the table values.
- */
- if (features->type >= INTUOS5S && features->type <= INTUOSHT) {
- if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
- features->device_type = BTN_TOOL_FINGER;
+ if (features->device_type == WACOM_DEVICETYPE_NONE &&
+ features->type != WIRELESS) {
+ error = features->type == HID_GENERIC ? -ENODEV : 0;
- features->x_max = 4096;
- features->y_max = 4096;
- } else {
- features->device_type = BTN_TOOL_PEN;
- }
- }
+ dev_warn(&hdev->dev, "Unknown device_type for '%s'. %s.",
+ hdev->name,
+ error ? "Ignoring" : "Assuming pen");
- /*
- * Same thing for Bamboo 3rd gen.
- */
- if ((features->type == BAMBOO_PT) &&
- (features->pktlen == WACOM_PKGLEN_BBTOUCH3) &&
- (features->device_type == BTN_TOOL_PEN)) {
- features->device_type = BTN_TOOL_FINGER;
+ if (error)
+ goto fail_shared_data;
- features->x_max = 4096;
- features->y_max = 4096;
+ features->device_type |= WACOM_DEVICETYPE_PEN;
}
- /*
- * Same thing for Bamboo PAD
- */
- if (features->type == BAMBOO_PAD)
- features->device_type = BTN_TOOL_FINGER;
-
- if (hdev->bus == BUS_BLUETOOTH)
- features->quirks |= WACOM_QUIRK_BATTERY;
-
- wacom_setup_device_quirks(features);
-
- /* set unit to "100th of a mm" for devices not reported by HID */
- if (!features->unit) {
- features->unit = 0x11;
- features->unitExpo = -3;
- }
wacom_calculate_res(features);
- strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
- snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
- "%s Pad", features->name);
-
- /* Append the device type to the name */
- if (features->device_type != BTN_TOOL_FINGER)
- strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX);
- else if (features->touch_max)
- strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX);
- else
- strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX);
+ wacom_update_name(wacom);
error = wacom_add_shared_data(hdev);
if (error)
goto fail_shared_data;
- if (!(features->quirks & WACOM_QUIRK_MONITOR) &&
+ if (!(features->device_type & WACOM_DEVICETYPE_WL_MONITOR) &&
(features->quirks & WACOM_QUIRK_BATTERY)) {
error = wacom_initialize_battery(wacom);
if (error)
goto fail_battery;
}
- if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
- error = wacom_register_inputs(wacom);
- if (error)
- goto fail_register_inputs;
- }
+ error = wacom_register_inputs(wacom);
+ if (error)
+ goto fail_register_inputs;
if (hdev->bus == BUS_BLUETOOTH) {
error = device_create_file(&hdev->dev, &dev_attr_speed);
@@ -1571,12 +1818,28 @@ 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);
- if (features->quirks & WACOM_QUIRK_MONITOR)
+ /* 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.touch_max) {
- if (wacom_wac->features.device_type == BTN_TOOL_FINGER)
- wacom_wac->shared->touch_input = wacom_wac->input;
+ 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;
}
return 0;
@@ -1645,7 +1908,6 @@ static struct hid_driver wacom_driver = {
.id_table = wacom_ids,
.probe = wacom_probe,
.remove = wacom_remove,
- .event = wacom_wac_event,
.report = wacom_wac_report,
#ifdef CONFIG_PM
.resume = wacom_resume,
diff --git a/kernel/drivers/hid/wacom_wac.c b/kernel/drivers/hid/wacom_wac.c
index adf959dcf..01a4f05c1 100644
--- a/kernel/drivers/hid/wacom_wac.c
+++ b/kernel/drivers/hid/wacom_wac.c
@@ -69,7 +69,7 @@ static void wacom_notify_battery(struct wacom_wac *wacom_wac,
static int wacom_penpartner_irq(struct wacom_wac *wacom)
{
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
switch (data[0]) {
case 1:
@@ -114,7 +114,7 @@ static int wacom_pl_irq(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
int prox, pressure;
if (data[0] != WACOM_REPORT_PENABLED) {
@@ -125,68 +125,54 @@ static int wacom_pl_irq(struct wacom_wac *wacom)
prox = data[1] & 0x40;
- if (prox) {
- wacom->id[0] = ERASER_DEVICE_ID;
- pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
- if (features->pressure_max > 255)
- pressure = (pressure << 1) | ((data[4] >> 6) & 1);
- pressure += (features->pressure_max + 1) / 2;
-
- /*
- * if going from out of proximity into proximity select between the eraser
- * and the pen based on the state of the stylus2 button, choose eraser if
- * pressed else choose pen. if not a proximity change from out to in, send
- * an out of proximity for previous tool then a in for new tool.
- */
- if (!wacom->tool[0]) {
- /* Eraser bit set for DTF */
- if (data[1] & 0x10)
- wacom->tool[1] = BTN_TOOL_RUBBER;
- else
- /* Going into proximity select tool */
- wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
- } else {
- /* was entered with stylus2 pressed */
- if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
- /* report out proximity for previous tool */
- input_report_key(input, wacom->tool[1], 0);
- input_sync(input);
- wacom->tool[1] = BTN_TOOL_PEN;
- return 0;
- }
+ if (!wacom->id[0]) {
+ if ((data[0] & 0x10) || (data[4] & 0x20)) {
+ wacom->tool[0] = BTN_TOOL_RUBBER;
+ wacom->id[0] = ERASER_DEVICE_ID;
}
- if (wacom->tool[1] != BTN_TOOL_RUBBER) {
- /* Unknown tool selected default to pen tool */
- wacom->tool[1] = BTN_TOOL_PEN;
+ else {
+ wacom->tool[0] = BTN_TOOL_PEN;
wacom->id[0] = STYLUS_DEVICE_ID;
}
- input_report_key(input, wacom->tool[1], prox); /* report in proximity for tool */
- input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
- input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
- input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
- input_report_abs(input, ABS_PRESSURE, pressure);
+ }
- input_report_key(input, BTN_TOUCH, data[4] & 0x08);
- input_report_key(input, BTN_STYLUS, data[4] & 0x10);
- /* Only allow the stylus2 button to be reported for the pen tool. */
- input_report_key(input, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
- } else {
- /* report proximity-out of a (valid) tool */
- if (wacom->tool[1] != BTN_TOOL_RUBBER) {
- /* Unknown tool selected default to pen tool */
- wacom->tool[1] = BTN_TOOL_PEN;
- }
- input_report_key(input, wacom->tool[1], prox);
+ /* If the eraser is in prox, STYLUS2 is always set. If we
+ * mis-detected the type and notice that STYLUS2 isn't set
+ * then force the eraser out of prox and let the pen in.
+ */
+ if (wacom->tool[0] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
+ input_report_key(input, BTN_TOOL_RUBBER, 0);
+ input_report_abs(input, ABS_MISC, 0);
+ input_sync(input);
+ wacom->tool[0] = BTN_TOOL_PEN;
+ wacom->id[0] = STYLUS_DEVICE_ID;
}
- wacom->tool[0] = prox; /* Save proximity state */
+ pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
+ if (features->pressure_max > 255)
+ pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+ pressure += (features->pressure_max + 1) / 2;
+
+ input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
+ input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
+ input_report_abs(input, ABS_PRESSURE, pressure);
+
+ input_report_key(input, BTN_TOUCH, data[4] & 0x08);
+ input_report_key(input, BTN_STYLUS, data[4] & 0x10);
+ /* Only allow the stylus2 button to be reported for the pen tool. */
+ input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20));
+
+ if (!prox)
+ wacom->id[0] = 0;
+ input_report_key(input, wacom->tool[0], prox);
+ input_report_abs(input, ABS_MISC, wacom->id[0]);
return 1;
}
static int wacom_ptu_irq(struct wacom_wac *wacom)
{
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
if (data[0] != WACOM_REPORT_PENABLED) {
dev_dbg(input->dev.parent,
@@ -215,7 +201,7 @@ static int wacom_ptu_irq(struct wacom_wac *wacom)
static int wacom_dtu_irq(struct wacom_wac *wacom)
{
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
int prox = data[1] & 0x20;
dev_dbg(input->dev.parent,
@@ -245,7 +231,7 @@ static int wacom_dtu_irq(struct wacom_wac *wacom)
static int wacom_dtus_irq(struct wacom_wac *wacom)
{
char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
unsigned short prox, pressure = 0;
if (data[0] != WACOM_REPORT_DTUS && data[0] != WACOM_REPORT_DTUSPAD) {
@@ -297,7 +283,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
struct input_dev *pad_input = wacom->pad_input;
int battery_capacity, ps_connected;
int prox;
@@ -464,7 +450,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
int idx = 0;
/* tool number */
@@ -645,23 +631,149 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
return 0;
}
+static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len)
+{
+ unsigned char *data = wacom_wac->data;
+ struct input_dev *input = wacom_wac->pad_input;
+ struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+ struct wacom_features *features = &wacom_wac->features;
+ int bat_charging, bat_percent, touch_ring_mode;
+ __u32 serial;
+ int i;
+
+ if (data[0] != WACOM_REPORT_REMOTE) {
+ dev_dbg(input->dev.parent,
+ "%s: received unknown report #%d", __func__, data[0]);
+ return 0;
+ }
+
+ serial = data[3] + (data[4] << 8) + (data[5] << 16);
+ wacom_wac->id[0] = PAD_DEVICE_ID;
+
+ input_report_key(input, BTN_0, (data[9] & 0x01));
+ input_report_key(input, BTN_1, (data[9] & 0x02));
+ input_report_key(input, BTN_2, (data[9] & 0x04));
+ input_report_key(input, BTN_3, (data[9] & 0x08));
+ input_report_key(input, BTN_4, (data[9] & 0x10));
+ input_report_key(input, BTN_5, (data[9] & 0x20));
+ input_report_key(input, BTN_6, (data[9] & 0x40));
+ input_report_key(input, BTN_7, (data[9] & 0x80));
+
+ input_report_key(input, BTN_8, (data[10] & 0x01));
+ input_report_key(input, BTN_9, (data[10] & 0x02));
+ input_report_key(input, BTN_A, (data[10] & 0x04));
+ input_report_key(input, BTN_B, (data[10] & 0x08));
+ input_report_key(input, BTN_C, (data[10] & 0x10));
+ input_report_key(input, BTN_X, (data[10] & 0x20));
+ input_report_key(input, BTN_Y, (data[10] & 0x40));
+ input_report_key(input, BTN_Z, (data[10] & 0x80));
+
+ input_report_key(input, BTN_BASE, (data[11] & 0x01));
+ input_report_key(input, BTN_BASE2, (data[11] & 0x02));
+
+ if (data[12] & 0x80)
+ input_report_abs(input, ABS_WHEEL, (data[12] & 0x7f));
+ else
+ input_report_abs(input, ABS_WHEEL, 0);
+
+ bat_percent = data[7] & 0x7f;
+ bat_charging = !!(data[7] & 0x80);
+
+ if (data[9] | data[10] | (data[11] & 0x03) | data[12])
+ input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+ else
+ input_report_abs(input, ABS_MISC, 0);
+
+ input_event(input, EV_MSC, MSC_SERIAL, serial);
+
+ /*Which mode select (LED light) is currently on?*/
+ touch_ring_mode = (data[11] & 0xC0) >> 6;
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ if (wacom_wac->serial[i] == serial)
+ wacom->led.select[i] = touch_ring_mode;
+ }
+
+ if (!wacom->battery &&
+ !(features->quirks & WACOM_QUIRK_BATTERY)) {
+ features->quirks |= WACOM_QUIRK_BATTERY;
+ INIT_WORK(&wacom->work, wacom_battery_work);
+ wacom_schedule_work(wacom_wac);
+ }
+
+ wacom_notify_battery(wacom_wac, bat_percent, bat_charging, 1,
+ bat_charging);
+
+ return 1;
+}
+
+static int wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len)
+{
+ struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+ unsigned char *data = wacom_wac->data;
+ int i;
+
+ if (data[0] != WACOM_REPORT_DEVICE_LIST)
+ return 0;
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ int j = i * 6;
+ int serial = (data[j+6] << 16) + (data[j+5] << 8) + data[j+4];
+ bool connected = data[j+2];
+
+ if (connected) {
+ int k;
+
+ if (wacom_wac->serial[i] == serial)
+ continue;
+
+ if (wacom_wac->serial[i]) {
+ wacom_remote_destroy_attr_group(wacom,
+ wacom_wac->serial[i]);
+ }
+
+ /* A remote can pair more than once with an EKR,
+ * check to make sure this serial isn't already paired.
+ */
+ for (k = 0; k < WACOM_MAX_REMOTES; k++) {
+ if (wacom_wac->serial[k] == serial)
+ break;
+ }
+
+ if (k < WACOM_MAX_REMOTES) {
+ wacom_wac->serial[i] = serial;
+ continue;
+ }
+ wacom_remote_create_attr_group(wacom, serial, i);
+
+ } else if (wacom_wac->serial[i]) {
+ wacom_remote_destroy_attr_group(wacom,
+ wacom_wac->serial[i]);
+ }
+ }
+
+ return 0;
+}
+
static void wacom_intuos_general(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
unsigned int t;
/* 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);
@@ -681,7 +793,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
unsigned int t;
int idx = 0, result;
@@ -689,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) {
@@ -838,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;
@@ -1025,7 +1159,7 @@ static void wacom_intuos_bt_process_data(struct wacom_wac *wacom,
memcpy(wacom->data, data, 10);
wacom_intuos_irq(wacom);
- input_sync(wacom->input);
+ input_sync(wacom->pen_input);
if (wacom->pad_input)
input_sync(wacom->pad_input);
}
@@ -1057,7 +1191,7 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
ps_connected);
break;
default:
- dev_dbg(wacom->input->dev.parent,
+ dev_dbg(wacom->pen_input->dev.parent,
"Unknown report: %d,%d size:%zu\n",
data[0], data[1], len);
return 0;
@@ -1067,7 +1201,7 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
{
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
unsigned touch_max = wacom->features.touch_max;
int count = 0;
int i;
@@ -1075,9 +1209,8 @@ static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
if (!touch_max)
return 0;
- /* non-HID_GENERIC single touch input doesn't call this routine */
- if ((touch_max == 1) && (wacom->features.type == HID_GENERIC))
- return wacom->hid_data.tipswitch &&
+ if (touch_max == 1)
+ return test_bit(BTN_TOUCH, input->key) &&
!wacom->shared->stylus_in_proximity;
for (i = 0; i < input->mt->num_slots; i++) {
@@ -1092,7 +1225,7 @@ static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
static int wacom_24hdt_irq(struct wacom_wac *wacom)
{
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
unsigned char *data = wacom->data;
int i;
int current_num_contacts = data[61];
@@ -1160,7 +1293,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
static int wacom_mt_touch(struct wacom_wac *wacom)
{
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
unsigned char *data = wacom->data;
int i;
int current_num_contacts = data[2];
@@ -1211,7 +1344,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
{
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
unsigned char *data = wacom->data;
int i;
@@ -1240,7 +1373,7 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
{
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
bool prox = !wacom->shared->stylus_in_proximity;
int x = 0, y = 0;
@@ -1276,7 +1409,7 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
static int wacom_tpc_pen(struct wacom_wac *wacom)
{
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
bool prox = data[1] & 0x20;
if (!wacom->shared->stylus_in_proximity) /* first in prox */
@@ -1305,8 +1438,12 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
{
unsigned char *data = wacom->data;
- dev_dbg(wacom->input->dev.parent,
- "%s: received report #%d\n", __func__, data[0]);
+ if (wacom->pen_input)
+ dev_dbg(wacom->pen_input->dev.parent,
+ "%s: received report #%d\n", __func__, data[0]);
+ else if (wacom->touch_input)
+ dev_dbg(wacom->touch_input->dev.parent,
+ "%s: received report #%d\n", __func__, data[0]);
switch (len) {
case WACOM_PKGLEN_TPC1FG:
@@ -1338,11 +1475,9 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
return 0;
}
-static void wacom_map_usage(struct wacom *wacom, struct hid_usage *usage,
+static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
struct hid_field *field, __u8 type, __u16 code, int fuzz)
{
- struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct input_dev *input = wacom_wac->input;
int fmin = field->logical_minimum;
int fmax = field->logical_maximum;
@@ -1370,36 +1505,38 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage)
{
struct wacom *wacom = hid_get_drvdata(hdev);
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct input_dev *input = wacom_wac->pen_input;
switch (usage->hid) {
case HID_GD_X:
- wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4);
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4);
break;
case HID_GD_Y:
- wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4);
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4);
break;
case HID_DG_TIPPRESSURE:
- wacom_map_usage(wacom, usage, field, EV_ABS, ABS_PRESSURE, 0);
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_PRESSURE, 0);
break;
case HID_DG_INRANGE:
- wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOOL_PEN, 0);
+ wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_PEN, 0);
break;
case HID_DG_INVERT:
- wacom_map_usage(wacom, usage, field, EV_KEY,
+ wacom_map_usage(input, usage, field, EV_KEY,
BTN_TOOL_RUBBER, 0);
break;
case HID_DG_ERASER:
case HID_DG_TIPSWITCH:
- wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0);
+ wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0);
break;
case HID_DG_BARRELSWITCH:
- wacom_map_usage(wacom, usage, field, EV_KEY, BTN_STYLUS, 0);
+ wacom_map_usage(input, usage, field, EV_KEY, BTN_STYLUS, 0);
break;
case HID_DG_BARRELSWITCH2:
- wacom_map_usage(wacom, usage, field, EV_KEY, BTN_STYLUS2, 0);
+ wacom_map_usage(input, usage, field, EV_KEY, BTN_STYLUS2, 0);
break;
case HID_DG_TOOLSERIALNUMBER:
- wacom_map_usage(wacom, usage, field, EV_MSC, MSC_SERIAL, 0);
+ wacom_map_usage(input, usage, field, EV_MSC, MSC_SERIAL, 0);
break;
}
}
@@ -1409,7 +1546,7 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field,
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct input_dev *input = wacom_wac->input;
+ struct input_dev *input = wacom_wac->pen_input;
/* checking which Tool / tip switch to send */
switch (usage->hid) {
@@ -1434,12 +1571,18 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field,
return 0;
}
+static void wacom_wac_pen_pre_report(struct hid_device *hdev,
+ struct hid_report *report)
+{
+ return;
+}
+
static void wacom_wac_pen_report(struct hid_device *hdev,
struct hid_report *report)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct input_dev *input = wacom_wac->input;
+ struct input_dev *input = wacom_wac->pen_input;
bool prox = wacom_wac->hid_data.inrange_state;
if (!wacom_wac->shared->stylus_in_proximity) /* first in prox */
@@ -1468,25 +1611,33 @@ 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 wacom_features *features = &wacom_wac->features;
+ struct input_dev *input = wacom_wac->touch_input;
unsigned touch_max = wacom_wac->features.touch_max;
switch (usage->hid) {
case HID_GD_X:
features->last_slot_field = usage->hid;
if (touch_max == 1)
- wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4);
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4);
else
- wacom_map_usage(wacom, usage, field, EV_ABS,
+ wacom_map_usage(input, usage, field, EV_ABS,
ABS_MT_POSITION_X, 4);
break;
case HID_GD_Y:
features->last_slot_field = usage->hid;
if (touch_max == 1)
- wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4);
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4);
else
- wacom_map_usage(wacom, usage, field, EV_ABS,
+ wacom_map_usage(input, usage, field, EV_ABS,
ABS_MT_POSITION_Y, 4);
break;
+ case HID_DG_WIDTH:
+ case HID_DG_HEIGHT:
+ features->last_slot_field = usage->hid;
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_MT_TOUCH_MAJOR, 0);
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_MT_TOUCH_MINOR, 0);
+ input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+ break;
case HID_DG_CONTACTID:
features->last_slot_field = usage->hid;
break;
@@ -1498,7 +1649,12 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
break;
case HID_DG_TIPSWITCH:
features->last_slot_field = usage->hid;
- wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0);
+ 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;
}
}
@@ -1511,6 +1667,10 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
bool prox = hid_data->tipswitch &&
!wacom_wac->shared->stylus_in_proximity;
+ wacom_wac->hid_data.num_received++;
+ if (wacom_wac->hid_data.num_received > wacom_wac->hid_data.num_expected)
+ return;
+
if (mt) {
int slot;
@@ -1527,6 +1687,13 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
hid_data->x);
input_report_abs(input, mt ? ABS_MT_POSITION_Y : ABS_Y,
hid_data->y);
+
+ if (test_bit(ABS_MT_TOUCH_MAJOR, input->absbit)) {
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, max(hid_data->width, hid_data->height));
+ input_report_abs(input, ABS_MT_TOUCH_MINOR, min(hid_data->width, hid_data->height));
+ if (hid_data->width != hid_data->height)
+ input_report_abs(input, ABS_MT_ORIENTATION, hid_data->width <= hid_data->height ? 0 : 1);
+ }
}
}
@@ -1543,6 +1710,12 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
case HID_GD_Y:
wacom_wac->hid_data.y = value;
break;
+ case HID_DG_WIDTH:
+ wacom_wac->hid_data.width = value;
+ break;
+ case HID_DG_HEIGHT:
+ wacom_wac->hid_data.height = value;
+ break;
case HID_DG_CONTACTID:
wacom_wac->hid_data.id = value;
break;
@@ -1554,24 +1727,75 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
if (usage->usage_index + 1 == field->report_count) {
if (usage->hid == wacom_wac->features.last_slot_field)
- wacom_wac_finger_slot(wacom_wac, wacom_wac->input);
+ wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input);
}
return 0;
}
+static void wacom_wac_finger_pre_report(struct hid_device *hdev,
+ struct hid_report *report)
+{
+ struct wacom *wacom = hid_get_drvdata(hdev);
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct hid_data* hid_data = &wacom_wac->hid_data;
+
+ 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)
+ hid_data->num_expected = value;
+ }
+ else {
+ hid_data->num_expected = wacom_wac->features.touch_max;
+ }
+}
+
static void wacom_wac_finger_report(struct hid_device *hdev,
struct hid_report *report)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct input_dev *input = wacom_wac->input;
+ struct input_dev *input = wacom_wac->touch_input;
unsigned touch_max = wacom_wac->features.touch_max;
+ /* If more packets of data are expected, give us a chance to
+ * process them rather than immediately syncing a partial
+ * update.
+ */
+ if (wacom_wac->hid_data.num_received < wacom_wac->hid_data.num_expected)
+ return;
+
if (touch_max > 1)
input_mt_sync_frame(input);
input_sync(input);
+ wacom_wac->hid_data.num_received = 0;
/* keep touch state for pen event */
wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(wacom_wac);
@@ -1582,10 +1806,10 @@ void wacom_wac_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;
/* currently, only direct devices have proper hid report descriptors */
- __set_bit(INPUT_PROP_DIRECT, input->propbit);
+ __set_bit(INPUT_PROP_DIRECT, wacom_wac->pen_input->propbit);
+ __set_bit(INPUT_PROP_DIRECT, wacom_wac->touch_input->propbit);
if (WACOM_PEN_FIELD(field))
return wacom_wac_pen_usage_mapping(hdev, field, usage);
@@ -1611,6 +1835,25 @@ int wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
return 0;
}
+static void wacom_report_events(struct hid_device *hdev, struct hid_report *report)
+{
+ int r;
+
+ for (r = 0; r < report->maxfield; r++) {
+ struct hid_field *field;
+ unsigned count, n;
+
+ field = report->field[r];
+ count = field->report_count;
+
+ if (!(HID_MAIN_ITEM_VARIABLE & field->flags))
+ continue;
+
+ for (n = 0; n < count; n++)
+ wacom_wac_event(hdev, field, &field->usage[n], field->value[n]);
+ }
+}
+
void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
{
struct wacom *wacom = hid_get_drvdata(hdev);
@@ -1621,6 +1864,14 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
return;
if (WACOM_PEN_FIELD(field))
+ wacom_wac_pen_pre_report(hdev, report);
+
+ if (WACOM_FINGER_FIELD(field))
+ wacom_wac_finger_pre_report(hdev, report);
+
+ wacom_report_events(hdev, report);
+
+ if (WACOM_PEN_FIELD(field))
return wacom_wac_pen_report(hdev, report);
if (WACOM_FINGER_FIELD(field))
@@ -1630,7 +1881,7 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
static int wacom_bpt_touch(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
struct input_dev *pad_input = wacom->pad_input;
unsigned char *data = wacom->data;
int i;
@@ -1678,7 +1929,7 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
{
struct wacom_features *features = &wacom->features;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
bool touch = data[1] & 0x80;
int slot = input_mt_get_slot_by_key(input, data[0]);
@@ -1695,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 <= INTUOSPL) {
+ if (features->type >= INTUOSPS && features->type <= INTUOSHT2) {
width = data[5] * 100;
height = data[6] * 100;
} else {
@@ -1723,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 {
@@ -1736,10 +1987,9 @@ static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data)
static int wacom_bpt3_touch(struct wacom_wac *wacom)
{
- struct input_dev *input = wacom->input;
unsigned char *data = wacom->data;
int count = data[1] & 0x07;
- int i;
+ int touch_changed = 0, i;
if (data[0] != 0x02)
return 0;
@@ -1749,14 +1999,19 @@ 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);
}
- input_mt_sync_frame(input);
- wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
+
+ /* 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);
+ }
return 1;
}
@@ -1764,7 +2019,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
static int wacom_bpt_pen(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
unsigned char *data = wacom->data;
int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
@@ -1834,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);
@@ -1873,7 +2133,7 @@ static void wacom_bamboo_pad_pen_event(struct wacom_wac *wacom,
static int wacom_bamboo_pad_touch_event(struct wacom_wac *wacom,
unsigned char *data)
{
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
unsigned char *finger_data, prefix;
unsigned id;
int x, y;
@@ -1941,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,
@@ -1979,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,
@@ -2060,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;
@@ -2096,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
@@ -2111,34 +2377,53 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
sync = wacom_wireless_irq(wacom_wac, len);
break;
+ case REMOTE:
+ if (wacom_wac->data[0] == WACOM_REPORT_DEVICE_LIST)
+ sync = wacom_remote_status_irq(wacom_wac, len);
+ else
+ sync = wacom_remote_irq(wacom_wac, len);
+ break;
+
default:
sync = false;
break;
}
if (sync) {
- input_sync(wacom_wac->input);
+ if (wacom_wac->pen_input)
+ input_sync(wacom_wac->pen_input);
+ if (wacom_wac->touch_input)
+ input_sync(wacom_wac->touch_input);
if (wacom_wac->pad_input)
input_sync(wacom_wac->pad_input);
}
}
-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->input;
+ 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);
@@ -2148,7 +2433,7 @@ static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
{
- struct input_dev *input_dev = wacom_wac->input;
+ struct input_dev *input_dev = wacom_wac->pen_input;
input_set_capability(input_dev, EV_REL, REL_WHEEL);
@@ -2167,15 +2452,62 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
}
-void wacom_setup_device_quirks(struct wacom_features *features)
+void wacom_setup_device_quirks(struct wacom *wacom)
{
+ struct wacom_features *features = &wacom->wacom_wac.features;
+
+ /* 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 >= INTUOS3S && features->type <= WACOM_MO)) {
+ if (features->device_type & WACOM_DEVICETYPE_PEN)
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+ }
/* touch device found but size is not defined. use default */
- if (features->device_type == BTN_TOOL_FINGER && !features->x_max) {
+ if (features->device_type & WACOM_DEVICETYPE_TOUCH && !features->x_max) {
features->x_max = 1023;
features->y_max = 1023;
}
+ /*
+ * Intuos5/Pro and Bamboo 3rd gen have no useful data about its
+ * touch interface in its HID descriptor. If this is the touch
+ * interface (PacketSize of WACOM_PKGLEN_BBTOUCH3), override the
+ * tablet values.
+ */
+ 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 >= INTUOSHT && features->type <= BAMBOO_PT)
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+
+ features->x_max = 4096;
+ features->y_max = 4096;
+ }
+ else if (features->pktlen == WACOM_PKGLEN_BBTOUCH) {
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+ }
+ }
+
+ /*
+ * Raw Wacom-mode pen and touch events both come from interface
+ * 0, whose HID descriptor has an application usage of 0xFF0D
+ * (i.e., WACOM_VENDORDEFINED_PEN). We route pen packets back
+ * out through the HID_GENERIC device created for interface 1,
+ * so rewrite this one to be of type WACOM_DEVICETYPE_TOUCH.
+ */
+ if (features->type == BAMBOO_PAD)
+ features->device_type = WACOM_DEVICETYPE_TOUCH;
+
+ if (features->type == REMOTE)
+ features->device_type = WACOM_DEVICETYPE_PAD;
+
+ if (wacom->hdev->bus == BUS_BLUETOOTH)
+ features->quirks |= WACOM_QUIRK_BATTERY;
+
/* quirk for bamboo touch with 2 low res touches */
if (features->type == BAMBOO_PT &&
features->pktlen == WACOM_PKGLEN_BBTOUCH) {
@@ -2187,66 +2519,22 @@ void wacom_setup_device_quirks(struct wacom_features *features)
}
if (features->type == WIRELESS) {
-
- /* monitor never has input and pen/touch have delayed create */
- features->quirks |= WACOM_QUIRK_NO_INPUT;
-
- /* must be monitor interface if no device_type set */
- if (!features->device_type) {
- features->quirks |= WACOM_QUIRK_MONITOR;
+ if (features->device_type == WACOM_DEVICETYPE_WL_MONITOR) {
features->quirks |= WACOM_QUIRK_BATTERY;
}
}
}
-static void wacom_abs_set_axis(struct input_dev *input_dev,
- struct wacom_wac *wacom_wac)
-{
- struct wacom_features *features = &wacom_wac->features;
-
- if (features->device_type == BTN_TOOL_PEN) {
- input_set_abs_params(input_dev, ABS_X, features->x_min,
- features->x_max, features->x_fuzz, 0);
- input_set_abs_params(input_dev, ABS_Y, features->y_min,
- features->y_max, features->y_fuzz, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0,
- features->pressure_max, features->pressure_fuzz, 0);
-
- /* penabled devices have fixed resolution for each model */
- input_abs_set_res(input_dev, ABS_X, features->x_resolution);
- input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
- } else {
- if (features->touch_max == 1) {
- input_set_abs_params(input_dev, ABS_X, 0,
- features->x_max, features->x_fuzz, 0);
- input_set_abs_params(input_dev, ABS_Y, 0,
- features->y_max, features->y_fuzz, 0);
- input_abs_set_res(input_dev, ABS_X,
- features->x_resolution);
- input_abs_set_res(input_dev, ABS_Y,
- features->y_resolution);
- }
-
- if (features->touch_max > 1) {
- input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
- features->x_max, features->x_fuzz, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
- features->y_max, features->y_fuzz, 0);
- input_abs_set_res(input_dev, ABS_MT_POSITION_X,
- features->x_resolution);
- input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
- features->y_resolution);
- }
- }
-}
-
-int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
+int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac)
{
struct wacom_features *features = &wacom_wac->features;
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ if (!(features->device_type & WACOM_DEVICETYPE_PEN))
+ return -ENODEV;
+
if (features->type == HID_GENERIC)
/* setup has already been done */
return 0;
@@ -2254,7 +2542,17 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(ABS_MISC, input_dev->absbit);
- wacom_abs_set_axis(input_dev, wacom_wac);
+ input_set_abs_params(input_dev, ABS_X, features->x_min,
+ features->x_max, features->x_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_Y, features->y_min,
+ features->y_max, features->y_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0,
+ features->pressure_max, features->pressure_fuzz, 0);
+
+ /* penabled devices have fixed resolution for each model */
+ input_abs_set_res(input_dev, ABS_X, features->x_resolution);
+ input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
+
switch (features->type) {
case GRAPHIRE_BT:
@@ -2292,6 +2590,7 @@ int wacom_setup_pentouch_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);
@@ -2323,53 +2622,25 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
case INTUOSPS:
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
- if (features->device_type == BTN_TOOL_PEN) {
- input_set_abs_params(input_dev, ABS_DISTANCE, 0,
- features->distance_max,
- 0, 0);
-
- 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_DISTANCE, 0,
+ features->distance_max,
+ 0, 0);
- wacom_setup_intuos(wacom_wac);
- } else if (features->device_type == BTN_TOOL_FINGER) {
- __clear_bit(ABS_MISC, input_dev->absbit);
+ 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_MT_TOUCH_MAJOR,
- 0, features->x_max, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
- 0, features->y_max, 0, 0);
- input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
- }
+ wacom_setup_intuos(wacom_wac);
break;
case WACOM_24HDT:
- if (features->device_type == BTN_TOOL_FINGER) {
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, features->x_max, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, 0, features->y_max, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
- }
- /* fall through */
-
case WACOM_27QHDT:
case MTSCREEN:
case MTTPC:
case MTTPC_B:
case TABLETPC2FG:
- if (features->device_type == BTN_TOOL_FINGER && features->touch_max > 1)
- input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_DIRECT);
- /* fall through */
-
case TABLETPC:
case TABLETPCE:
__clear_bit(ABS_MISC, input_dev->absbit);
-
- __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
-
- if (features->device_type != BTN_TOOL_PEN)
- break; /* no need to process stylus stuff */
-
/* fall through */
case DTUS:
@@ -2397,50 +2668,122 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
break;
case INTUOSHT:
- if (features->touch_max &&
- features->device_type == BTN_TOOL_FINGER) {
- input_dev->evbit[0] |= BIT_MASK(EV_SW);
- __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
- }
- /* fall through */
-
case BAMBOO_PT:
- __clear_bit(ABS_MISC, input_dev->absbit);
-
- if (features->device_type == BTN_TOOL_FINGER) {
-
- if (features->touch_max) {
- if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
- input_set_abs_params(input_dev,
- ABS_MT_TOUCH_MAJOR,
- 0, features->x_max, 0, 0);
- input_set_abs_params(input_dev,
- ABS_MT_TOUCH_MINOR,
- 0, features->y_max, 0, 0);
- }
- input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
- } else {
- /* buttons/keys only interface */
- __clear_bit(ABS_X, input_dev->absbit);
- __clear_bit(ABS_Y, input_dev->absbit);
- __clear_bit(BTN_TOUCH, input_dev->keybit);
+ case BAMBOO_PEN:
+ case INTUOSHT2:
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
- /* 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);
- __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+ 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);
+ features->distance_max,
+ 0, 0);
}
break;
case BAMBOO_PAD:
__clear_bit(ABS_MISC, input_dev->absbit);
+ break;
+ }
+ return 0;
+}
+
+int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
+ struct wacom_wac *wacom_wac)
+{
+ struct wacom_features *features = &wacom_wac->features;
+
+ input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ if (!(features->device_type & WACOM_DEVICETYPE_TOUCH))
+ return -ENODEV;
+
+ if (features->type == HID_GENERIC)
+ /* setup has already been done */
+ return 0;
+
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ if (features->touch_max == 1) {
+ input_set_abs_params(input_dev, ABS_X, 0,
+ features->x_max, features->x_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0,
+ features->y_max, features->y_fuzz, 0);
+ input_abs_set_res(input_dev, ABS_X,
+ features->x_resolution);
+ input_abs_set_res(input_dev, ABS_Y,
+ features->y_resolution);
+ }
+ else if (features->touch_max > 1) {
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+ features->x_max, features->x_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+ features->y_max, features->y_fuzz, 0);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+ features->x_resolution);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+ features->y_resolution);
+ }
+
+ switch (features->type) {
+ case INTUOS5:
+ case INTUOS5L:
+ case INTUOSPM:
+ case INTUOSPL:
+ case INTUOS5S:
+ case INTUOSPS:
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, features->y_max, 0, 0);
+ input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
+ break;
+
+ case WACOM_24HDT:
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, features->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, 0, features->y_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+ /* fall through */
+
+ case WACOM_27QHDT:
+ case MTSCREEN:
+ case MTTPC:
+ case MTTPC_B:
+ case TABLETPC2FG:
+ input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_DIRECT);
+ /*fall through */
+
+ case TABLETPC:
+ case TABLETPCE:
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+ 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,
+ 0, features->x_max, 0, 0);
+ input_set_abs_params(input_dev,
+ ABS_MT_TOUCH_MINOR,
+ 0, features->y_max, 0, 0);
+ }
+ input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
+ break;
+
+ case BAMBOO_PAD:
input_mt_init_slots(input_dev, features->touch_max,
INPUT_MT_POINTER);
__set_bit(BTN_LEFT, input_dev->keybit);
@@ -2450,11 +2793,26 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
return 0;
}
+static void wacom_setup_numbered_buttons(struct input_dev *input_dev,
+ int button_count)
+{
+ int i;
+
+ for (i = 0; i < button_count && i < 10; i++)
+ __set_bit(BTN_0 + i, input_dev->keybit);
+ for (i = 10; i < button_count && i < 16; i++)
+ __set_bit(BTN_A + (i-10), input_dev->keybit);
+ for (i = 16; i < button_count && i < 18; i++)
+ __set_bit(BTN_BASE + (i-16), input_dev->keybit);
+}
+
int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac)
{
struct wacom_features *features = &wacom_wac->features;
- int i;
+
+ if (!(features->device_type & WACOM_DEVICETYPE_PAD))
+ return -ENODEV;
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
@@ -2468,10 +2826,15 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
/* kept for making udev and libwacom accepting the pad */
__set_bit(BTN_STYLUS, input_dev->keybit);
+ wacom_setup_numbered_buttons(input_dev, features->numbered_buttons);
+
switch (features->type) {
+
+ case CINTIQ_HYBRID:
+ case CINTIQ_COMPANION_2:
+ case DTK:
+ case DTUS:
case GRAPHIRE_BT:
- __set_bit(BTN_0, input_dev->keybit);
- __set_bit(BTN_1, input_dev->keybit);
break;
case WACOM_MO:
@@ -2489,16 +2852,6 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
break;
case WACOM_24HD:
- __set_bit(BTN_A, input_dev->keybit);
- __set_bit(BTN_B, input_dev->keybit);
- __set_bit(BTN_C, input_dev->keybit);
- __set_bit(BTN_X, input_dev->keybit);
- __set_bit(BTN_Y, input_dev->keybit);
- __set_bit(BTN_Z, input_dev->keybit);
-
- for (i = 0; i < 10; i++)
- __set_bit(BTN_0 + i, input_dev->keybit);
-
__set_bit(KEY_PROG1, input_dev->keybit);
__set_bit(KEY_PROG2, input_dev->keybit);
__set_bit(KEY_PROG3, input_dev->keybit);
@@ -2520,12 +2873,6 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
__set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit);
break;
- case DTK:
- for (i = 0; i < 6; i++)
- __set_bit(BTN_0 + i, input_dev->keybit);
-
- break;
-
case WACOM_22HD:
__set_bit(KEY_PROG1, input_dev->keybit);
__set_bit(KEY_PROG2, input_dev->keybit);
@@ -2533,52 +2880,22 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
/* fall through */
case WACOM_21UX2:
- __set_bit(BTN_A, input_dev->keybit);
- __set_bit(BTN_B, input_dev->keybit);
- __set_bit(BTN_C, input_dev->keybit);
- __set_bit(BTN_X, input_dev->keybit);
- __set_bit(BTN_Y, input_dev->keybit);
- __set_bit(BTN_Z, input_dev->keybit);
- __set_bit(BTN_BASE, input_dev->keybit);
- __set_bit(BTN_BASE2, input_dev->keybit);
- /* fall through */
-
case WACOM_BEE:
- __set_bit(BTN_8, input_dev->keybit);
- __set_bit(BTN_9, input_dev->keybit);
- /* fall through */
-
case CINTIQ:
- for (i = 0; i < 8; i++)
- __set_bit(BTN_0 + i, input_dev->keybit);
-
input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
break;
case WACOM_13HD:
- for (i = 0; i < 9; i++)
- __set_bit(BTN_0 + i, input_dev->keybit);
-
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
break;
case INTUOS3:
case INTUOS3L:
- __set_bit(BTN_4, input_dev->keybit);
- __set_bit(BTN_5, input_dev->keybit);
- __set_bit(BTN_6, input_dev->keybit);
- __set_bit(BTN_7, input_dev->keybit);
-
input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
/* fall through */
case INTUOS3S:
- __set_bit(BTN_0, input_dev->keybit);
- __set_bit(BTN_1, input_dev->keybit);
- __set_bit(BTN_2, input_dev->keybit);
- __set_bit(BTN_3, input_dev->keybit);
-
input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
break;
@@ -2586,19 +2903,8 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
case INTUOS5L:
case INTUOSPM:
case INTUOSPL:
- __set_bit(BTN_7, input_dev->keybit);
- __set_bit(BTN_8, input_dev->keybit);
- /* fall through */
-
case INTUOS5S:
case INTUOSPS:
- /* touch interface does not have the pad device */
- if (features->device_type != BTN_TOOL_PEN)
- return -ENODEV;
-
- for (i = 0; i < 7; i++)
- __set_bit(BTN_0 + i, input_dev->keybit);
-
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
break;
@@ -2613,36 +2919,14 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
case INTUOS4:
case INTUOS4L:
- __set_bit(BTN_7, input_dev->keybit);
- __set_bit(BTN_8, input_dev->keybit);
- /* fall through */
-
case INTUOS4S:
- for (i = 0; i < 7; i++)
- __set_bit(BTN_0 + i, input_dev->keybit);
-
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
break;
- case CINTIQ_HYBRID:
- for (i = 0; i < 9; i++)
- __set_bit(BTN_0 + i, input_dev->keybit);
-
- break;
-
- case DTUS:
- for (i = 0; i < 4; i++)
- __set_bit(BTN_0 + i, input_dev->keybit);
- break;
-
case INTUOSHT:
case BAMBOO_PT:
- /* pad device is on the touch interface */
- if ((features->device_type != BTN_TOOL_FINGER) ||
- /* Bamboo Pen only tablet does not have pad */
- ((features->type == BAMBOO_PT) && !features->touch_max))
- return -ENODEV;
-
+ case BAMBOO_TOUCH:
+ case INTUOSHT2:
__clear_bit(ABS_MISC, input_dev->absbit);
__set_bit(BTN_LEFT, input_dev->keybit);
@@ -2652,6 +2936,11 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
break;
+ case REMOTE:
+ input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
+ input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
+ break;
+
default:
/* no pad supported */
return -ENODEV;
@@ -2667,7 +2956,7 @@ static const struct wacom_features wacom_features_0x10 =
GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
static const struct wacom_features wacom_features_0x81 =
{ "Wacom Graphire BT", 16704, 12064, 511, 32,
- GRAPHIRE_BT, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+ GRAPHIRE_BT, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES, 2 };
static const struct wacom_features wacom_features_0x11 =
{ "Wacom Graphire2 4x5", 10206, 7422, 511, 63,
GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
@@ -2793,77 +3082,77 @@ static const struct wacom_features wacom_features_0x45 =
INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xB0 =
{ "Wacom Intuos3 4x5", 25400, 20320, 1023, 63,
- INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 4 };
static const struct wacom_features wacom_features_0xB1 =
{ "Wacom Intuos3 6x8", 40640, 30480, 1023, 63,
- INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
static const struct wacom_features wacom_features_0xB2 =
{ "Wacom Intuos3 9x12", 60960, 45720, 1023, 63,
- INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
static const struct wacom_features wacom_features_0xB3 =
{ "Wacom Intuos3 12x12", 60960, 60960, 1023, 63,
- INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
static const struct wacom_features wacom_features_0xB4 =
{ "Wacom Intuos3 12x19", 97536, 60960, 1023, 63,
- INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
static const struct wacom_features wacom_features_0xB5 =
{ "Wacom Intuos3 6x11", 54204, 31750, 1023, 63,
- INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
static const struct wacom_features wacom_features_0xB7 =
{ "Wacom Intuos3 4x6", 31496, 19685, 1023, 63,
- INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 4 };
static const struct wacom_features wacom_features_0xB8 =
{ "Wacom Intuos4 4x6", 31496, 19685, 2047, 63,
- INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 7 };
static const struct wacom_features wacom_features_0xB9 =
{ "Wacom Intuos4 6x9", 44704, 27940, 2047, 63,
- INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
static const struct wacom_features wacom_features_0xBA =
{ "Wacom Intuos4 8x13", 65024, 40640, 2047, 63,
- INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
static const struct wacom_features wacom_features_0xBB =
{ "Wacom Intuos4 12x19", 97536, 60960, 2047, 63,
- INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
static const struct wacom_features wacom_features_0xBC =
{ "Wacom Intuos4 WL", 40640, 25400, 2047, 63,
- INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
static const struct wacom_features wacom_features_0xBD =
{ "Wacom Intuos4 WL", 40640, 25400, 2047, 63,
- INTUOS4WL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS4WL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
static const struct wacom_features wacom_features_0x26 =
{ "Wacom Intuos5 touch S", 31496, 19685, 2047, 63,
- INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
+ INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 7, .touch_max = 16 };
static const struct wacom_features wacom_features_0x27 =
{ "Wacom Intuos5 touch M", 44704, 27940, 2047, 63,
- INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
+ INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 16 };
static const struct wacom_features wacom_features_0x28 =
{ "Wacom Intuos5 touch L", 65024, 40640, 2047, 63,
- INTUOS5L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
+ INTUOS5L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 16 };
static const struct wacom_features wacom_features_0x29 =
{ "Wacom Intuos5 S", 31496, 19685, 2047, 63,
- INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 7 };
static const struct wacom_features wacom_features_0x2A =
{ "Wacom Intuos5 M", 44704, 27940, 2047, 63,
- INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
static const struct wacom_features wacom_features_0x314 =
{ "Wacom Intuos Pro S", 31496, 19685, 2047, 63,
- INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
+ INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 7, .touch_max = 16,
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
static const struct wacom_features wacom_features_0x315 =
{ "Wacom Intuos Pro M", 44704, 27940, 2047, 63,
- INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
+ INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 16,
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
static const struct wacom_features wacom_features_0x317 =
{ "Wacom Intuos Pro L", 65024, 40640, 2047, 63,
- INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
+ INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 16,
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
static const struct wacom_features wacom_features_0xF4 =
{ "Wacom Cintiq 24HD", 104080, 65200, 2047, 63,
- WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 16,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
static const struct wacom_features wacom_features_0xF8 =
{ "Wacom Cintiq 24HD touch", 104080, 65200, 2047, 63, /* Pen */
- WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 16,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 };
static const struct wacom_features wacom_features_0xF6 =
@@ -2872,11 +3161,11 @@ static const struct wacom_features wacom_features_0xF6 =
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
static const struct wacom_features wacom_features_0x32A =
{ "Wacom Cintiq 27QHD", 119740, 67520, 2047, 63,
- WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 0,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
static const struct wacom_features wacom_features_0x32B =
{ "Wacom Cintiq 27QHD touch", 119740, 67520, 2047, 63,
- WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 0,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x32C };
static const struct wacom_features wacom_features_0x32C =
@@ -2884,20 +3173,20 @@ static const struct wacom_features wacom_features_0x32C =
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x32B, .touch_max = 10 };
static const struct wacom_features wacom_features_0x3F =
{ "Wacom Cintiq 21UX", 87200, 65600, 1023, 63,
- CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
static const struct wacom_features wacom_features_0xC5 =
{ "Wacom Cintiq 20WSX", 86680, 54180, 1023, 63,
- WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 10 };
static const struct wacom_features wacom_features_0xC6 =
{ "Wacom Cintiq 12WX", 53020, 33440, 1023, 63,
- WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 10 };
static const struct wacom_features wacom_features_0x304 =
{ "Wacom Cintiq 13HD", 59152, 33448, 1023, 63,
- WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
static const struct wacom_features wacom_features_0x333 =
{ "Wacom Cintiq 13HD touch", 59152, 33448, 2047, 63,
- WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x335 };
static const struct wacom_features wacom_features_0x335 =
@@ -2916,19 +3205,23 @@ 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", 21896, 13760, 511, 0,
- DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
+ DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4,
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,
+ DTUSX, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 0,
+ WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
+static const struct wacom_features wacom_features_0x336 =
+ { "Wacom DTU1141", 23472, 13203, 1023, 0,
+ DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4,
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,
+ DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 6,
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,
+ DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 6,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D };
static const struct wacom_features wacom_features_0x5D = /* Touch */
@@ -2937,15 +3230,15 @@ 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", 86800, 65200, 2047, 63,
- WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
static const struct wacom_features wacom_features_0xFA =
{ "Wacom Cintiq 22HD", 95440, 53860, 2047, 63,
- WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
static const struct wacom_features wacom_features_0x5B =
{ "Wacom Cintiq 22HDT", 95440, 53860, 2047, 63,
- WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e };
static const struct wacom_features wacom_features_0x5E =
@@ -3025,11 +3318,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 };
@@ -3041,10 +3333,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 };
@@ -3071,7 +3363,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 };
@@ -3092,7 +3384,7 @@ 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", 59152, 33448, 2047, 63,
- CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 };
static const struct wacom_features wacom_features_0x309 =
@@ -3101,7 +3393,7 @@ 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", 59152, 33448, 2047, 63,
- CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30C };
static const struct wacom_features wacom_features_0x30C =
@@ -3114,10 +3406,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", .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 };
@@ -3269,12 +3589,20 @@ 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) },
{ USB_DEVICE_WACOM(0x32F) },
+ { USB_DEVICE_WACOM(0x331) },
{ 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/kernel/drivers/hid/wacom_wac.h b/kernel/drivers/hid/wacom_wac.h
index 4700ac994..877c24a5d 100644
--- a/kernel/drivers/hid/wacom_wac.h
+++ b/kernel/drivers/hid/wacom_wac.h
@@ -16,12 +16,11 @@
#define WACOM_PKGLEN_MAX 192
#define WACOM_NAME_MAX 64
+#define WACOM_MAX_REMOTES 5
+#define WACOM_STATUS_UNKNOWN 255
/* packet length for individual models */
-#define WACOM_PKGLEN_PENPRTN 7
-#define WACOM_PKGLEN_GRAPHIRE 8
#define WACOM_PKGLEN_BBFUN 9
-#define WACOM_PKGLEN_INTUOS 10
#define WACOM_PKGLEN_TPC1FG 5
#define WACOM_PKGLEN_TPC1FG_B 10
#define WACOM_PKGLEN_TPC2FG 14
@@ -29,9 +28,6 @@
#define WACOM_PKGLEN_BBTOUCH3 64
#define WACOM_PKGLEN_BBPEN 10
#define WACOM_PKGLEN_WIRELESS 32
-#define WACOM_PKGLEN_MTOUCH 62
-#define WACOM_PKGLEN_MTTPC 40
-#define WACOM_PKGLEN_DTUS 68
#define WACOM_PKGLEN_PENABLED 8
#define WACOM_PKGLEN_BPAD_TOUCH 32
#define WACOM_PKGLEN_BPAD_TOUCH_USB 64
@@ -71,17 +67,29 @@
#define WACOM_REPORT_USB 192
#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 */
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0001
-#define WACOM_QUIRK_NO_INPUT 0x0002
-#define WACOM_QUIRK_MONITOR 0x0004
#define WACOM_QUIRK_BATTERY 0x0008
+/* device types */
+#define WACOM_DEVICETYPE_NONE 0x0000
+#define WACOM_DEVICETYPE_PEN 0x0001
+#define WACOM_DEVICETYPE_TOUCH 0x0002
+#define WACOM_DEVICETYPE_PAD 0x0004
+#define WACOM_DEVICETYPE_WL_MONITOR 0x0008
+
+#define WACOM_VENDORDEFINED_PEN 0xff0d0001
+
#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \
((f)->physical == HID_DG_STYLUS) || \
((f)->physical == HID_DG_PEN) || \
- ((f)->application == HID_DG_PEN))
+ ((f)->application == HID_DG_PEN) || \
+ ((f)->application == HID_DG_DIGITIZER) || \
+ ((f)->application == WACOM_VENDORDEFINED_PEN))
#define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \
((f)->physical == HID_DG_FINGER) || \
((f)->application == HID_DG_TOUCHSCREEN))
@@ -110,22 +118,27 @@ 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,
TABLETPC2FG,
@@ -145,6 +158,7 @@ struct wacom_features {
int type;
int x_resolution;
int y_resolution;
+ int numbered_buttons;
int x_min;
int y_min;
int device_type;
@@ -189,23 +203,31 @@ struct hid_data {
int width;
int height;
int id;
+ int cc_report;
+ int cc_index;
+ int cc_value_index;
+ int num_expected;
+ int num_received;
};
struct wacom_wac {
- char name[WACOM_NAME_MAX];
+ char pen_name[WACOM_NAME_MAX];
+ char touch_name[WACOM_NAME_MAX];
char pad_name[WACOM_NAME_MAX];
char bat_name[WACOM_NAME_MAX];
char ac_name[WACOM_NAME_MAX];
unsigned char data[WACOM_PKGLEN_MAX];
int tool[2];
int id[2];
- __u32 serial[2];
+ __u32 serial[5];
bool reporting_data;
struct wacom_features features;
struct wacom_shared *shared;
- struct input_dev *input;
+ struct input_dev *pen_input;
+ struct input_dev *touch_input;
struct input_dev *pad_input;
- bool input_registered;
+ bool pen_registered;
+ bool touch_registered;
bool pad_registered;
int pid;
int battery_capacity;