summaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/Kconfig24
-rw-r--r--drivers/hid/hid-alps.c26
-rw-r--r--drivers/hid/hid-core.c12
-rw-r--r--drivers/hid/hid-ids.h12
-rw-r--r--drivers/hid/hid-input.c10
-rw-r--r--drivers/hid/hid-kye.c123
-rw-r--r--drivers/hid/hid-lg.c86
-rw-r--r--drivers/hid/hid-lg4ff.c109
-rw-r--r--drivers/hid/hid-lg4ff.h4
-rw-r--r--drivers/hid/hid-microsoft.c12
-rw-r--r--drivers/hid/hid-saitek.c2
-rw-r--r--drivers/hid/hid-sony.c88
-rw-r--r--drivers/hid/hid-uclogic.c187
-rw-r--r--drivers/hid/hid-waltop.c36
-rw-r--r--drivers/hid/usbhid/hid-quirks.c4
-rw-r--r--drivers/hid/wacom.h96
-rw-r--r--drivers/hid/wacom_sys.c1187
-rw-r--r--drivers/hid/wacom_wac.c435
-rw-r--r--drivers/hid/wacom_wac.h22
19 files changed, 1805 insertions, 670 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 5f590e16e41b..cd4599c0523b 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -457,8 +457,6 @@ config LOGITECH_FF
- Logitech WingMan Cordless RumblePad
- Logitech WingMan Cordless RumblePad 2
- Logitech WingMan Force 3D
- - Logitech Formula Force EX
- - Logitech WingMan Formula Force GP
and if you want to enable force feedback for them.
Note: if you say N here, this device will still be supported, but without
@@ -488,15 +486,22 @@ config LOGIWHEELS_FF
select INPUT_FF_MEMLESS
default LOGITECH_FF
help
- Say Y here if you want to enable force feedback and range setting
+ Say Y here if you want to enable force feedback and range setting(*)
support for following Logitech wheels:
+ - Logitech G25 (*)
+ - Logitech G27 (*)
+ - Logitech G29 (*)
- Logitech Driving Force
- - Logitech Driving Force Pro
- - Logitech Driving Force GT
- - Logitech G25
- - Logitech G27
- - Logitech MOMO/MOMO 2
- - Logitech Formula Force EX
+ - Logitech Driving Force Pro (*)
+ - Logitech Driving Force GT (*)
+ - Logitech Driving Force EX/RX
+ - Logitech Driving Force Wireless
+ - Logitech Speed Force Wireless
+ - Logitech MOMO Force
+ - Logitech MOMO Racing Force
+ - Logitech Formula Force GP
+ - Logitech Formula Force EX/RX
+ - Logitech Wingman Formula Force GP
config HID_MAGICMOUSE
tristate "Apple Magic Mouse/Trackpad multi-touch support"
@@ -862,6 +867,7 @@ config HID_WACOM
select POWER_SUPPLY
select NEW_LEDS
select LEDS_CLASS
+ select LEDS_TRIGGERS
help
Say Y here if you want to use the USB or BT version of the Wacom Intuos
or Graphire tablet.
diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c
index 048befde295a..ed9c0ea5b026 100644
--- a/drivers/hid/hid-alps.c
+++ b/drivers/hid/hid-alps.c
@@ -139,8 +139,8 @@ static int u1_read_write_register(struct hid_device *hdev, u32 address,
if (read_flag) {
readbuf = kzalloc(U1_FEATURE_REPORT_LEN, GFP_KERNEL);
if (!readbuf) {
- kfree(input);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto exit;
}
ret = hid_hw_raw_request(hdev, U1_FEATURE_REPORT_ID, readbuf,
@@ -149,6 +149,7 @@ static int u1_read_write_register(struct hid_device *hdev, u32 address,
if (ret < 0) {
dev_err(&hdev->dev, "failed read register (%d)\n", ret);
+ kfree(readbuf);
goto exit;
}
@@ -190,16 +191,16 @@ static int alps_raw_event(struct hid_device *hdev,
if (z != 0) {
input_mt_report_slot_state(hdata->input,
MT_TOOL_FINGER, 1);
+ input_report_abs(hdata->input,
+ ABS_MT_POSITION_X, x);
+ input_report_abs(hdata->input,
+ ABS_MT_POSITION_Y, y);
+ input_report_abs(hdata->input,
+ ABS_MT_PRESSURE, z);
} else {
input_mt_report_slot_state(hdata->input,
MT_TOOL_FINGER, 0);
- break;
}
-
- input_report_abs(hdata->input, ABS_MT_POSITION_X, x);
- input_report_abs(hdata->input, ABS_MT_POSITION_Y, y);
- input_report_abs(hdata->input, ABS_MT_PRESSURE, z);
-
}
input_mt_sync_frame(hdata->input);
@@ -244,13 +245,13 @@ static int alps_raw_event(struct hid_device *hdev,
static int alps_post_reset(struct hid_device *hdev)
{
return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1,
- NULL, U1_TP_ABS_MODE, false);
+ NULL, U1_TP_ABS_MODE | U1_SP_ABS_MODE, false);
}
static int alps_post_resume(struct hid_device *hdev)
{
return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1,
- NULL, U1_TP_ABS_MODE, false);
+ NULL, U1_TP_ABS_MODE | U1_SP_ABS_MODE, false);
}
#endif /* CONFIG_PM */
@@ -383,7 +384,7 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi)
input2 = input_allocate_device();
if (!input2) {
- input_free_device(input2);
+ ret = -ENOMEM;
goto exit;
}
@@ -425,7 +426,8 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi)
__set_bit(INPUT_PROP_POINTER, input2->propbit);
__set_bit(INPUT_PROP_POINTING_STICK, input2->propbit);
- if (input_register_device(data->input2)) {
+ ret = input_register_device(data->input2);
+ if (ret) {
input_free_device(input2);
goto exit;
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 08f53c7fd513..2b89c701076f 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -727,6 +727,7 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
(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_PRO_4_JP ||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 ||
hid->product == USB_DEVICE_ID_MS_POWER_COVER) &&
hid->group == HID_GROUP_MULTITOUCH)
@@ -1916,7 +1917,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) },
- { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
@@ -1982,6 +1983,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ 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_PRO_4_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_DIGITAL_MEDIA_7K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600) },
@@ -2037,6 +2039,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT9) },
{ 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) },
@@ -2083,6 +2086,11 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) },
@@ -2480,7 +2488,7 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) },
-#if defined(CONFIG_MOUSE_SYNAPTICS_USB) || defined(CONFIG_MOUSE_SYNAPTICS_USB_MODULE)
+#if IS_ENABLED(CONFIG_MOUSE_SYNAPTICS_USB)
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 4ed9a4fdfea7..cd59c79eebdd 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -268,6 +268,7 @@
#define USB_DEVICE_ID_CORSAIR_K95RGB 0x1b11
#define USB_DEVICE_ID_CORSAIR_M65RGB 0x1b12
#define USB_DEVICE_ID_CORSAIR_K70RGB 0x1b13
+#define USB_DEVICE_ID_CORSAIR_STRAFE 0x1b15
#define USB_DEVICE_ID_CORSAIR_K65RGB 0x1b17
#define USB_VENDOR_ID_CREATIVELABS 0x041e
@@ -565,7 +566,7 @@
#define USB_DEVICE_ID_KYE_GPEN_560 0x5003
#define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010
#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011
-#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2 0x501a
+#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2 0x501a
#define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013
#define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015
@@ -713,6 +714,7 @@
#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_PRO_4_JP 0x07e9
#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de
#define USB_DEVICE_ID_MS_POWER_COVER 0x07da
@@ -859,6 +861,7 @@
#define USB_DEVICE_ID_SAITEK_PS1000 0x0621
#define USB_DEVICE_ID_SAITEK_RAT7_OLD 0x0ccb
#define USB_DEVICE_ID_SAITEK_RAT7 0x0cd7
+#define USB_DEVICE_ID_SAITEK_RAT9 0x0cfa
#define USB_DEVICE_ID_SAITEK_MMO7 0x0cd0
#define USB_VENDOR_ID_SAMSUNG 0x0419
@@ -996,6 +999,10 @@
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064
#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522
#define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781
+#define USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3 0x3031
+#define USB_DEVICE_ID_UGEE_TABLET_81 0x0081
+#define USB_DEVICE_ID_UGEE_TABLET_45 0x0045
+#define USB_DEVICE_ID_YIYNOVA_TABLET 0x004d
#define USB_VENDOR_ID_UNITEC 0x227d
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709
@@ -1085,4 +1092,7 @@
#define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002
#define USB_DEVICE_ID_RAPHNET_4NES4SNES 0x0003
+#define USB_VENDOR_ID_UGTIZER 0x2179
+#define USB_DEVICE_ID_UGTIZER_TABLET_GP0610 0x0053
+
#endif
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index bcfaf32d9e5e..fb9ace1cef8b 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -604,6 +604,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break;
}
+ /*
+ * Some lazy vendors declare 255 usages for System Control,
+ * leading to the creation of ABS_X|Y axis and too many others.
+ * It wouldn't be a problem if joydev doesn't consider the
+ * device as a joystick then.
+ */
+ if (field->application == HID_GD_SYSTEM_CONTROL)
+ goto ignore;
+
if ((usage->hid & 0xf0) == 0x90) { /* D-pad */
switch (usage->hid) {
case HID_GD_UP: usage->hat_dir = 1; break;
@@ -953,6 +962,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case HID_UP_HPVENDOR2:
set_bit(EV_REP, input->evbit);
switch (usage->hid & HID_USAGE) {
+ case 0x001: map_key_clear(KEY_MICMUTE); break;
case 0x003: map_key_clear(KEY_BRIGHTNESSDOWN); break;
case 0x004: map_key_clear(KEY_BRIGHTNESSUP); break;
default: goto ignore;
diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c
index 32e6d8d9ded0..0dd1167b2c9b 100644
--- a/drivers/hid/hid-kye.c
+++ b/drivers/hid/hid-kye.c
@@ -19,11 +19,6 @@
#include "hid-ids.h"
-/*
- * See EasyPen i405X description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=KYE_EasyPen_i405X
- */
-
/* Original EasyPen i405X report descriptor size */
#define EASYPEN_I405X_RDESC_ORIG_SIZE 476
@@ -82,11 +77,6 @@ static __u8 easypen_i405x_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-/*
- * See MousePen i608X description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=KYE_MousePen_i608X
- */
-
/* Original MousePen i608X report descriptor size */
#define MOUSEPEN_I608X_RDESC_ORIG_SIZE 476
@@ -186,10 +176,104 @@ static __u8 mousepen_i608x_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-/*
- * See EasyPen M610X description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=KYE_EasyPen_M610X
- */
+/* Original MousePen i608X v2 report descriptor size */
+#define MOUSEPEN_I608X_V2_RDESC_ORIG_SIZE 482
+
+/* Fixed MousePen i608X v2 report descriptor */
+static __u8 mousepen_i608x_v2_rdesc_fixed[] = {
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x09, 0x01, /* Usage (01h), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0x05, /* Report ID (5), */
+ 0x09, 0x01, /* Usage (01h), */
+ 0x15, 0x80, /* Logical Minimum (-128), */
+ 0x25, 0x7F, /* Logical Maximum (127), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x07, /* Report Count (7), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0x05, 0x0D, /* Usage Page (Digitizer), */
+ 0x09, 0x02, /* Usage (Pen), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0x10, /* Report ID (16), */
+ 0x09, 0x20, /* Usage (Stylus), */
+ 0xA0, /* Collection (Physical), */
+ 0x14, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x09, 0x42, /* Usage (Tip Switch), */
+ 0x09, 0x44, /* Usage (Barrel Switch), */
+ 0x09, 0x46, /* Usage (Tablet Pick), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x04, /* Report Count (4), */
+ 0x81, 0x03, /* Input (Constant, Variable), */
+ 0x09, 0x32, /* Usage (In Range), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0xA4, /* Push, */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x55, 0xFD, /* Unit Exponent (-3), */
+ 0x65, 0x13, /* Unit (Inch), */
+ 0x34, /* Physical Minimum (0), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
+ 0x27, 0x00, 0xA0, 0x00, 0x00, /* Logical Maximum (40960), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x31, /* Usage (Y), */
+ 0x46, 0x70, 0x17, /* Physical Maximum (6000), */
+ 0x26, 0x00, 0x78, /* Logical Maximum (30720), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xB4, /* Pop, */
+ 0x09, 0x30, /* Usage (Tip Pressure), */
+ 0x26, 0xFF, 0x07, /* Logical Maximum (2047), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xC0, /* End Collection, */
+ 0xC0, /* End Collection, */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x02, /* Usage (Mouse), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0x11, /* Report ID (17), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xA0, /* Collection (Physical), */
+ 0x14, /* Logical Minimum (0), */
+ 0xA4, /* Push, */
+ 0x05, 0x09, /* Usage Page (Button), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x19, 0x01, /* Usage Minimum (01h), */
+ 0x29, 0x03, /* Usage Maximum (03h), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x05, /* Report Count (5), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0xB4, /* Pop, */
+ 0x95, 0x01, /* Report Count (1), */
+ 0xA4, /* Push, */
+ 0x55, 0xFD, /* Unit Exponent (-3), */
+ 0x65, 0x13, /* Unit (Inch), */
+ 0x34, /* Physical Minimum (0), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
+ 0x27, 0x00, 0xA0, 0x00, 0x00, /* Logical Maximum (40960), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x31, /* Usage (Y), */
+ 0x46, 0x70, 0x17, /* Physical Maximum (6000), */
+ 0x26, 0x00, 0x78, /* Logical Maximum (30720), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xB4, /* Pop, */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x09, 0x38, /* Usage (Wheel), */
+ 0x15, 0xFF, /* Logical Minimum (-1), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x81, 0x06, /* Input (Variable, Relative), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0xC0, /* End Collection, */
+ 0xC0 /* End Collection */
+};
/* Original EasyPen M610X report descriptor size */
#define EASYPEN_M610X_RDESC_ORIG_SIZE 476
@@ -454,12 +538,17 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
}
break;
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X:
- case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2:
if (*rsize == MOUSEPEN_I608X_RDESC_ORIG_SIZE) {
rdesc = mousepen_i608x_rdesc_fixed;
*rsize = sizeof(mousepen_i608x_rdesc_fixed);
}
break;
+ case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2:
+ if (*rsize == MOUSEPEN_I608X_V2_RDESC_ORIG_SIZE) {
+ rdesc = mousepen_i608x_v2_rdesc_fixed;
+ *rsize = sizeof(mousepen_i608x_v2_rdesc_fixed);
+ }
+ break;
case USB_DEVICE_ID_KYE_EASYPEN_M610X:
if (*rsize == EASYPEN_M610X_RDESC_ORIG_SIZE) {
rdesc = easypen_m610x_rdesc_fixed;
@@ -553,7 +642,7 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id)
switch (id->product) {
case USB_DEVICE_ID_KYE_EASYPEN_I405X:
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X:
- case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2:
+ case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2:
case USB_DEVICE_ID_KYE_EASYPEN_M610X:
case USB_DEVICE_ID_KYE_PENSKETCH_M912:
ret = kye_tablet_enable(hdev);
@@ -586,7 +675,7 @@ static const struct hid_device_id kye_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_MOUSEPEN_I608X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
- USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) },
+ USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_EASYPEN_M610X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index feb2be71f77c..76f644deb0a7 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -49,6 +49,7 @@
#define FV_RDESC_ORIG_SIZE 130
#define MOMO_RDESC_ORIG_SIZE 87
#define MOMO2_RDESC_ORIG_SIZE 87
+#define FFG_RDESC_ORIG_SIZE 85
/* Fixed report descriptors for Logitech Driving Force (and Pro)
* wheel controllers
@@ -334,6 +335,52 @@ static __u8 momo2_rdesc_fixed[] = {
0xC0 /* End Collection */
};
+static __u8 ffg_rdesc_fixed[] = {
+0x05, 0x01, /* Usage Page (Desktop), */
+0x09, 0x04, /* Usage (Joystik), */
+0xA1, 0x01, /* Collection (Application), */
+0xA1, 0x02, /* Collection (Logical), */
+0x95, 0x01, /* Report Count (1), */
+0x75, 0x0A, /* Report Size (10), */
+0x15, 0x00, /* Logical Minimum (0), */
+0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
+0x35, 0x00, /* Physical Minimum (0), */
+0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
+0x09, 0x30, /* Usage (X), */
+0x81, 0x02, /* Input (Variable), */
+0x95, 0x06, /* Report Count (6), */
+0x75, 0x01, /* Report Size (1), */
+0x25, 0x01, /* Logical Maximum (1), */
+0x45, 0x01, /* Physical Maximum (1), */
+0x05, 0x09, /* Usage Page (Button), */
+0x19, 0x01, /* Usage Minimum (01h), */
+0x29, 0x06, /* Usage Maximum (06h), */
+0x81, 0x02, /* Input (Variable), */
+0x95, 0x01, /* Report Count (1), */
+0x75, 0x08, /* Report Size (8), */
+0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+0x46, 0xFF, 0x00, /* Physical Maximum (255), */
+0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+0x09, 0x01, /* Usage (01h), */
+0x81, 0x02, /* Input (Variable), */
+0x05, 0x01, /* Usage Page (Desktop), */
+0x81, 0x01, /* Input (Constant), */
+0x09, 0x31, /* Usage (Y), */
+0x81, 0x02, /* Input (Variable), */
+0x09, 0x32, /* Usage (Z), */
+0x81, 0x02, /* Input (Variable), */
+0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+0x09, 0x01, /* Usage (01h), */
+0x81, 0x02, /* Input (Variable), */
+0xC0, /* End Collection, */
+0xA1, 0x02, /* Collection (Logical), */
+0x09, 0x02, /* Usage (02h), */
+0x95, 0x07, /* Report Count (7), */
+0x91, 0x02, /* Output (Variable), */
+0xC0, /* End Collection, */
+0xC0 /* End Collection */
+};
+
/*
* Certain Logitech keyboards send in report #3 keys which are far
* above the logical maximum described in descriptor. This extends
@@ -343,8 +390,6 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
- struct usb_device_descriptor *udesc;
- __u16 bcdDevice, rev_maj, rev_min;
if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 &&
rdesc[84] == 0x8c && rdesc[85] == 0x02) {
@@ -363,20 +408,18 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
switch (hdev->product) {
- /* Several wheels report as this id when operating in emulation mode. */
- case USB_DEVICE_ID_LOGITECH_WHEEL:
- udesc = &(hid_to_usb_dev(hdev)->descriptor);
- if (!udesc) {
- hid_err(hdev, "NULL USB device descriptor\n");
- break;
+ case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
+ if (*rsize == FFG_RDESC_ORIG_SIZE) {
+ hid_info(hdev,
+ "fixing up Logitech Wingman Formula Force GP report descriptor\n");
+ rdesc = ffg_rdesc_fixed;
+ *rsize = sizeof(ffg_rdesc_fixed);
}
- bcdDevice = le16_to_cpu(udesc->bcdDevice);
- rev_maj = bcdDevice >> 8;
- rev_min = bcdDevice & 0xff;
+ break;
- /* Update the report descriptor for only the Driving Force wheel */
- if (rev_maj == 1 && rev_min == 2 &&
- *rsize == DF_RDESC_ORIG_SIZE) {
+ /* Several wheels report as this id when operating in emulation mode. */
+ case USB_DEVICE_ID_LOGITECH_WHEEL:
+ if (*rsize == DF_RDESC_ORIG_SIZE) {
hid_info(hdev,
"fixing up Logitech Driving Force report descriptor\n");
rdesc = df_rdesc_fixed;
@@ -621,6 +664,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
usage->code == ABS_RZ)) {
switch (hdev->product) {
case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
+ case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
case USB_DEVICE_ID_LOGITECH_WHEEL:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
@@ -657,6 +701,17 @@ static int lg_event(struct hid_device *hdev, struct hid_field *field,
return 0;
}
+static int lg_raw_event(struct hid_device *hdev, struct hid_report *report,
+ u8 *rd, int size)
+{
+ struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
+
+ if (drv_data->quirks & LG_FF4)
+ return lg4ff_raw_event(hdev, report, rd, size, drv_data);
+
+ return 0;
+}
+
static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
@@ -809,7 +864,7 @@ static const struct hid_device_id lg_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
.driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
- .driver_data = LG_FF },
+ .driver_data = LG_NOGET | LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
.driver_data = LG_FF2 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
@@ -830,6 +885,7 @@ static struct hid_driver lg_driver = {
.input_mapping = lg_input_mapping,
.input_mapped = lg_input_mapped,
.event = lg_event,
+ .raw_event = lg_raw_event,
.probe = lg_probe,
.remove = lg_remove,
};
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index af3a8ec8a746..1fc12e357035 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -75,6 +75,7 @@ static void lg4ff_set_range_g25(struct hid_device *hid, u16 range);
struct lg4ff_wheel_data {
const u32 product_id;
+ u16 combine;
u16 range;
const u16 min_range;
const u16 max_range;
@@ -136,6 +137,7 @@ struct lg4ff_alternate_mode {
};
static const struct lg4ff_wheel lg4ff_devices[] = {
+ {USB_DEVICE_ID_LOGITECH_WINGMAN_FFG, lg4ff_wheel_effects, 40, 180, NULL},
{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, lg4ff_set_range_dfp},
@@ -328,6 +330,56 @@ int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
}
}
+int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
+ u8 *rd, int size, struct lg_drv_data *drv_data)
+{
+ int offset;
+ struct lg4ff_device_entry *entry = drv_data->device_props;
+
+ if (!entry)
+ return 0;
+
+ /* adjust HID report present combined pedals data */
+ if (entry->wdata.combine) {
+ switch (entry->wdata.product_id) {
+ case USB_DEVICE_ID_LOGITECH_WHEEL:
+ rd[5] = rd[3];
+ rd[6] = 0x7F;
+ return 1;
+ case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
+ case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
+ case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
+ rd[4] = rd[3];
+ rd[5] = 0x7F;
+ return 1;
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
+ rd[5] = rd[4];
+ rd[6] = 0x7F;
+ return 1;
+ case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
+ case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
+ offset = 5;
+ break;
+ case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
+ case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
+ offset = 6;
+ break;
+ case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
+ offset = 3;
+ break;
+ default:
+ return 0;
+ }
+
+ /* Compute a combined axis when wheel does not supply it */
+ rd[offset] = (0xFF + rd[offset] - rd[offset+1]) >> 1;
+ rd[offset+1] = 0x7F;
+ return 1;
+ }
+
+ return 0;
+}
+
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)
@@ -345,6 +397,7 @@ static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const s
{
struct lg4ff_wheel_data t_wdata = { .product_id = wheel->product_id,
.real_product_id = real_product_id,
+ .combine = 0,
.min_range = wheel->min_range,
.max_range = wheel->max_range,
.set_range = wheel->set_range,
@@ -885,6 +938,58 @@ 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);
+static ssize_t lg4ff_combine_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hid_device *hid = to_hid_device(dev);
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ size_t count;
+
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return 0;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return 0;
+ }
+
+ count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.combine);
+ return count;
+}
+
+static ssize_t lg4ff_combine_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 combine = simple_strtoul(buf, NULL, 10);
+
+ 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;
+ }
+
+ if (combine > 1)
+ combine = 1;
+
+ entry->wdata.combine = combine;
+ return count;
+}
+static DEVICE_ATTR(combine_pedals, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_combine_show, lg4ff_combine_store);
+
/* Export the currently set range of the wheel */
static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -1259,6 +1364,9 @@ int lg4ff_init(struct hid_device *hid)
}
/* Create sysfs interface */
+ error = device_create_file(&hid->dev, &dev_attr_combine_pedals);
+ if (error)
+ hid_warn(hid, "Unable to create sysfs interface for \"combine\", errno %d\n", error);
error = device_create_file(&hid->dev, &dev_attr_range);
if (error)
hid_warn(hid, "Unable to create sysfs interface for \"range\", errno %d\n", error);
@@ -1358,6 +1466,7 @@ int lg4ff_deinit(struct hid_device *hid)
device_remove_file(&hid->dev, &dev_attr_alternate_modes);
}
+ device_remove_file(&hid->dev, &dev_attr_combine_pedals);
device_remove_file(&hid->dev, &dev_attr_range);
#ifdef CONFIG_LEDS_CLASS
{
diff --git a/drivers/hid/hid-lg4ff.h b/drivers/hid/hid-lg4ff.h
index 66201af44da3..de1f350e0bd3 100644
--- a/drivers/hid/hid-lg4ff.h
+++ b/drivers/hid/hid-lg4ff.h
@@ -6,11 +6,15 @@ 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);
+int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
+ u8 *rd, int size, 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; }
+static inline int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
+ u8 *rd, int size, 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/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index e924d555536c..c6cd392e9f99 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -28,7 +28,6 @@
#define MS_RDESC 0x08
#define MS_NOGET 0x10
#define MS_DUPLICATE_USAGES 0x20
-#define MS_RDESC_3K 0x40
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
@@ -45,13 +44,6 @@ static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
rdesc[557] = 0x35;
rdesc[559] = 0x45;
}
- /* the same as above (s/usage/physical/) */
- if ((quirks & MS_RDESC_3K) && *rsize == 106 && rdesc[94] == 0x19 &&
- rdesc[95] == 0x00 && rdesc[96] == 0x29 &&
- rdesc[97] == 0xff) {
- rdesc[94] = 0x35;
- rdesc[96] = 0x45;
- }
return rdesc;
}
@@ -271,7 +263,7 @@ static const struct hid_device_id ms_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB),
.driver_data = MS_PRESENTER },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K),
- .driver_data = MS_ERGONOMY | MS_RDESC_3K },
+ .driver_data = MS_ERGONOMY },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K),
.driver_data = MS_ERGONOMY },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600),
@@ -288,6 +280,8 @@ static const struct hid_device_id ms_devices[] = {
.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_PRO_4_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_POWER_COVER),
diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c
index 2f84b26f1167..39e642686ff0 100644
--- a/drivers/hid/hid-saitek.c
+++ b/drivers/hid/hid-saitek.c
@@ -183,6 +183,8 @@ static const struct hid_device_id saitek_devices[] = {
.driver_data = SAITEK_RELEASE_MODE_RAT7 },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7),
.driver_data = SAITEK_RELEASE_MODE_RAT7 },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT9),
+ .driver_data = SAITEK_RELEASE_MODE_RAT7 },
{ HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9),
.driver_data = SAITEK_RELEASE_MODE_RAT7 },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7),
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 310436a54a3f..b0bb99a821bd 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -8,7 +8,7 @@
* Copyright (c) 2012 David Dillow <dave@thedillows.org>
* Copyright (c) 2006-2013 Jiri Kosina
* Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
- * Copyright (c) 2014 Frank Praznik <frank.praznik@gmail.com>
+ * Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
*/
/*
@@ -51,6 +51,7 @@
#define NAVIGATION_CONTROLLER_USB BIT(9)
#define NAVIGATION_CONTROLLER_BT BIT(10)
#define SINO_LITE_CONTROLLER BIT(11)
+#define FUTUREMAX_DANCE_MAT BIT(12)
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
@@ -65,6 +66,8 @@
MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER)
#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
MOTION_CONTROLLER)
+#define SONY_BT_DEVICE (SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_BT |\
+ MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER_BT)
#define MAX_LEDS 4
@@ -1048,6 +1051,7 @@ struct sony_sc {
u8 mac_address[6];
u8 worker_initialized;
+ u8 defer_initialization;
u8 cable_state;
u8 battery_charging;
u8 battery_capacity;
@@ -1058,6 +1062,12 @@ struct sony_sc {
u8 led_count;
};
+static inline void sony_schedule_work(struct sony_sc *sc)
+{
+ if (!sc->defer_initialization)
+ schedule_work(&sc->state_worker);
+}
+
static u8 *sixaxis_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize)
{
@@ -1125,7 +1135,7 @@ static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc,
{
struct sony_sc *sc = hid_get_drvdata(hdev);
- if (sc->quirks & SINO_LITE_CONTROLLER)
+ if (sc->quirks & (SINO_LITE_CONTROLLER | FUTUREMAX_DANCE_MAT))
return rdesc;
/*
@@ -1317,6 +1327,11 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
dualshock4_parse_report(sc, rd, size);
}
+ if (sc->defer_initialization) {
+ sc->defer_initialization = 0;
+ sony_schedule_work(sc);
+ }
+
return 0;
}
@@ -1554,7 +1569,7 @@ static void buzz_set_leds(struct sony_sc *sc)
static void sony_set_leds(struct sony_sc *sc)
{
if (!(sc->quirks & BUZZ_CONTROLLER))
- schedule_work(&sc->state_worker);
+ sony_schedule_work(sc);
else
buzz_set_leds(sc);
}
@@ -1665,7 +1680,7 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
new_off != drv_data->led_delay_off[n]) {
drv_data->led_delay_on[n] = new_on;
drv_data->led_delay_off[n] = new_off;
- schedule_work(&drv_data->state_worker);
+ sony_schedule_work(drv_data);
}
return 0;
@@ -1865,6 +1880,17 @@ static void dualshock4_send_output_report(struct sony_sc *sc)
u8 *buf = sc->output_report_dmabuf;
int offset;
+ /*
+ * NOTE: The buf[1] field of the Bluetooth report controls
+ * the Dualshock 4 reporting rate.
+ *
+ * Known values include:
+ *
+ * 0x80 - 1000hz (full speed)
+ * 0xA0 - 31hz
+ * 0xB0 - 20hz
+ * 0xD0 - 66hz
+ */
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
memset(buf, 0, DS4_REPORT_0x05_SIZE);
buf[0] = 0x05;
@@ -1976,7 +2002,7 @@ static int sony_play_effect(struct input_dev *dev, void *data,
sc->left = effect->u.rumble.strong_magnitude / 256;
sc->right = effect->u.rumble.weak_magnitude / 256;
- schedule_work(&sc->state_worker);
+ sony_schedule_work(sc);
return 0;
}
@@ -2039,8 +2065,11 @@ static int sony_battery_get_property(struct power_supply *psy,
return ret;
}
-static int sony_battery_probe(struct sony_sc *sc)
+static int sony_battery_probe(struct sony_sc *sc, int append_dev_id)
{
+ const char *battery_str_fmt = append_dev_id ?
+ "sony_controller_battery_%pMR_%i" :
+ "sony_controller_battery_%pMR";
struct power_supply_config psy_cfg = { .drv_data = sc, };
struct hid_device *hdev = sc->hdev;
int ret;
@@ -2056,9 +2085,8 @@ static int sony_battery_probe(struct sony_sc *sc)
sc->battery_desc.get_property = sony_battery_get_property;
sc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
sc->battery_desc.use_for_apm = 0;
- sc->battery_desc.name = kasprintf(GFP_KERNEL,
- "sony_controller_battery_%pMR",
- sc->mac_address);
+ sc->battery_desc.name = kasprintf(GFP_KERNEL, battery_str_fmt,
+ sc->mac_address, sc->device_id);
if (!sc->battery_desc.name)
return -ENOMEM;
@@ -2094,7 +2122,21 @@ static void sony_battery_remove(struct sony_sc *sc)
* it will show up as two devices. A global list of connected controllers and
* their MAC addresses is maintained to ensure that a device is only connected
* once.
+ *
+ * Some USB-only devices masquerade as Sixaxis controllers and all have the
+ * same dummy Bluetooth address, so a comparison of the connection type is
+ * required. Devices are only rejected in the case where two devices have
+ * matching Bluetooth addresses on different bus types.
*/
+static inline int sony_compare_connection_type(struct sony_sc *sc0,
+ struct sony_sc *sc1)
+{
+ const int sc0_not_bt = !(sc0->quirks & SONY_BT_DEVICE);
+ const int sc1_not_bt = !(sc1->quirks & SONY_BT_DEVICE);
+
+ return sc0_not_bt == sc1_not_bt;
+}
+
static int sony_check_add_dev_list(struct sony_sc *sc)
{
struct sony_sc *entry;
@@ -2107,9 +2149,14 @@ static int sony_check_add_dev_list(struct sony_sc *sc)
ret = memcmp(sc->mac_address, entry->mac_address,
sizeof(sc->mac_address));
if (!ret) {
- ret = -EEXIST;
- hid_info(sc->hdev, "controller with MAC address %pMR already connected\n",
+ if (sony_compare_connection_type(sc, entry)) {
+ ret = 1;
+ } else {
+ ret = -EEXIST;
+ hid_info(sc->hdev,
+ "controller with MAC address %pMR already connected\n",
sc->mac_address);
+ }
goto unlock;
}
}
@@ -2285,10 +2332,14 @@ static inline void sony_cancel_work_sync(struct sony_sc *sc)
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
+ int append_dev_id;
unsigned long quirks = id->driver_data;
struct sony_sc *sc;
unsigned int connect_mask = HID_CONNECT_DEFAULT;
+ if (!strcmp(hdev->name, "FutureMax Dance Mat"))
+ quirks |= FUTUREMAX_DANCE_MAT;
+
sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL);
if (sc == NULL) {
hid_err(hdev, "can't alloc sony descriptor\n");
@@ -2341,9 +2392,16 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
* the Sixaxis does not want the report_id as part of the data
* packet, so we have to discard buf[0] when sending the actual
* control message, even for numbered reports, humpf!
+ *
+ * Additionally, the Sixaxis on USB isn't properly initialized
+ * until the PS logo button is pressed and as such won't retain
+ * any state set by an output report, so the initial
+ * configuration report is deferred until the first input
+ * report arrives.
*/
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
+ sc->defer_initialization = 1;
ret = sixaxis_set_operational_usb(hdev);
sony_init_output_report(sc, sixaxis_send_output_report);
} else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) ||
@@ -2379,7 +2437,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret < 0)
goto err_stop;
- ret = sony_check_add(sc);
+ ret = append_dev_id = sony_check_add(sc);
if (ret < 0)
goto err_stop;
@@ -2390,7 +2448,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
if (sc->quirks & SONY_BATTERY_SUPPORT) {
- ret = sony_battery_probe(sc);
+ ret = sony_battery_probe(sc, append_dev_id);
if (ret < 0)
goto err_stop;
@@ -2486,8 +2544,10 @@ static int sony_resume(struct hid_device *hdev)
* reinitialized on resume or they won't behave properly.
*/
if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
- (sc->quirks & NAVIGATION_CONTROLLER_USB))
+ (sc->quirks & NAVIGATION_CONTROLLER_USB)) {
sixaxis_set_operational_usb(sc->hdev);
+ sc->defer_initialization = 1;
+ }
sony_set_leds(sc);
}
diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c
index 85ac43517e3f..1509d7287ff3 100644
--- a/drivers/hid/hid-uclogic.c
+++ b/drivers/hid/hid-uclogic.c
@@ -21,13 +21,6 @@
#include "hid-ids.h"
-/*
- * See WPXXXXU model descriptions, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP4030U
- * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP5540U
- * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP8060U
- */
-
/* Size of the original descriptor of WPXXXXU tablets */
#define WPXXXXU_RDESC_ORIG_SIZE 212
@@ -221,11 +214,6 @@ static __u8 wp8060u_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-/*
- * See WP1062 description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP1062
- */
-
/* Size of the original descriptor of WP1062 tablet */
#define WP1062_RDESC_ORIG_SIZE 254
@@ -274,11 +262,6 @@ static __u8 wp1062_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-/*
- * See PF1209 description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_PF1209
- */
-
/* Size of the original descriptor of PF1209 tablet */
#define PF1209_RDESC_ORIG_SIZE 234
@@ -356,11 +339,6 @@ static __u8 pf1209_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-/*
- * See TWHL850 description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Wireless_Tablet_TWHL850
- */
-
/* Size of the original descriptors of TWHL850 tablet */
#define TWHL850_RDESC_ORIG_SIZE0 182
#define TWHL850_RDESC_ORIG_SIZE1 161
@@ -469,11 +447,6 @@ static __u8 twhl850_rdesc_fixed2[] = {
0xC0 /* End Collection */
};
-/*
- * See TWHA60 description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_TWHA60
- */
-
/* Size of the original descriptors of TWHA60 tablet */
#define TWHA60_RDESC_ORIG_SIZE0 254
#define TWHA60_RDESC_ORIG_SIZE1 139
@@ -613,6 +586,27 @@ static const __u8 uclogic_tablet_rdesc_template[] = {
0xC0 /* End Collection */
};
+/* Fixed virtual pad report descriptor */
+static const __u8 uclogic_buttonpad_rdesc[] = {
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x07, /* Usage (Keypad), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0xF7, /* Report ID (247), */
+ 0x05, 0x0D, /* Usage Page (Digitizers), */
+ 0x09, 0x39, /* Usage (Tablet Function Keys), */
+ 0xA0, /* Collection (Physical), */
+ 0x05, 0x09, /* Usage Page (Button), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x18, /* Report Count (24), */
+ 0x81, 0x03, /* Input (Constant, Variable), */
+ 0x19, 0x01, /* Usage Minimum (01h), */
+ 0x29, 0x08, /* Usage Maximum (08h), */
+ 0x95, 0x08, /* Report Count (8), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xC0, /* End Collection */
+ 0xC0 /* End Collection */
+};
+
/* Parameter indices */
enum uclogic_prm {
UCLOGIC_PRM_X_LM = 1,
@@ -628,6 +622,7 @@ struct uclogic_drvdata {
unsigned int rsize;
bool invert_pen_inrange;
bool ignore_pen_usage;
+ bool has_virtual_pad_interface;
};
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
@@ -637,6 +632,12 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
__u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
+ if (drvdata->rdesc != NULL) {
+ rdesc = drvdata->rdesc;
+ *rsize = drvdata->rsize;
+ return rdesc;
+ }
+
switch (hdev->product) {
case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209:
if (*rsize == PF1209_RDESC_ORIG_SIZE) {
@@ -706,11 +707,6 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
break;
}
break;
- default:
- if (drvdata->rdesc != NULL) {
- rdesc = drvdata->rdesc;
- *rsize = drvdata->rsize;
- }
}
return rdesc;
@@ -804,7 +800,6 @@ static int uclogic_tablet_enable(struct hid_device *hdev)
len = UCLOGIC_PRM_NUM * sizeof(*buf);
buf = kmalloc(len, GFP_KERNEL);
if (buf == NULL) {
- hid_err(hdev, "failed to allocate parameter buffer\n");
rc = -ENOMEM;
goto cleanup;
}
@@ -848,7 +843,6 @@ static int uclogic_tablet_enable(struct hid_device *hdev)
sizeof(uclogic_tablet_rdesc_template),
GFP_KERNEL);
if (drvdata->rdesc == NULL) {
- hid_err(hdev, "failed to allocate fixed rdesc\n");
rc = -ENOMEM;
goto cleanup;
}
@@ -876,11 +870,75 @@ cleanup:
return rc;
}
+/**
+ * Enable actual button mode.
+ *
+ * @hdev: HID device
+ */
+static int uclogic_button_enable(struct hid_device *hdev)
+{
+ int rc;
+ struct usb_device *usb_dev = hid_to_usb_dev(hdev);
+ struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
+ char *str_buf;
+ size_t str_len = 16;
+ unsigned char *rdesc;
+ size_t rdesc_len;
+
+ str_buf = kzalloc(str_len, GFP_KERNEL);
+ if (str_buf == NULL) {
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+
+ /* Enable abstract keyboard mode */
+ rc = usb_string(usb_dev, 0x7b, str_buf, str_len);
+ if (rc == -EPIPE) {
+ hid_info(hdev, "button mode setting not found\n");
+ rc = 0;
+ goto cleanup;
+ } else if (rc < 0) {
+ hid_err(hdev, "failed to enable abstract keyboard\n");
+ goto cleanup;
+ } else if (strncmp(str_buf, "HK On", rc)) {
+ hid_info(hdev, "invalid answer when requesting buttons: '%s'\n",
+ str_buf);
+ rc = -EINVAL;
+ goto cleanup;
+ }
+
+ /* Re-allocate fixed report descriptor */
+ rdesc_len = drvdata->rsize + sizeof(uclogic_buttonpad_rdesc);
+ rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL);
+ if (!rdesc) {
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+
+ memcpy(rdesc, drvdata->rdesc, drvdata->rsize);
+
+ /* Append the buttonpad descriptor */
+ memcpy(rdesc + drvdata->rsize, uclogic_buttonpad_rdesc,
+ sizeof(uclogic_buttonpad_rdesc));
+
+ /* clean up old rdesc and use the new one */
+ drvdata->rsize = rdesc_len;
+ devm_kfree(&hdev->dev, drvdata->rdesc);
+ drvdata->rdesc = rdesc;
+
+ rc = 0;
+
+cleanup:
+ kfree(str_buf);
+ return rc;
+}
+
static int uclogic_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int rc;
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+ struct usb_device *udev = hid_to_usb_dev(hdev);
struct uclogic_drvdata *drvdata;
/*
@@ -899,6 +957,10 @@ static int uclogic_probe(struct hid_device *hdev,
switch (id->product) {
case USB_DEVICE_ID_HUION_TABLET:
+ case USB_DEVICE_ID_YIYNOVA_TABLET:
+ case USB_DEVICE_ID_UGEE_TABLET_81:
+ case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3:
+ case USB_DEVICE_ID_UGEE_TABLET_45:
/* If this is the pen interface */
if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
rc = uclogic_tablet_enable(hdev);
@@ -907,10 +969,48 @@ static int uclogic_probe(struct hid_device *hdev,
return rc;
}
drvdata->invert_pen_inrange = true;
+
+ rc = uclogic_button_enable(hdev);
+ drvdata->has_virtual_pad_interface = !rc;
} else {
drvdata->ignore_pen_usage = true;
}
break;
+ case USB_DEVICE_ID_UGTIZER_TABLET_GP0610:
+ /* If this is the pen interface */
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
+ rc = uclogic_tablet_enable(hdev);
+ if (rc) {
+ hid_err(hdev, "tablet enabling failed\n");
+ return rc;
+ }
+ drvdata->invert_pen_inrange = true;
+ } else {
+ drvdata->ignore_pen_usage = true;
+ }
+ break;
+ case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60:
+ /*
+ * If it is the three-interface version, which is known to
+ * respond to initialization.
+ */
+ if (udev->config->desc.bNumInterfaces == 3) {
+ /* If it is the pen interface */
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+ rc = uclogic_tablet_enable(hdev);
+ if (rc) {
+ hid_err(hdev, "tablet enabling failed\n");
+ return rc;
+ }
+ drvdata->invert_pen_inrange = true;
+
+ rc = uclogic_button_enable(hdev);
+ drvdata->has_virtual_pad_interface = !rc;
+ } else {
+ drvdata->ignore_pen_usage = true;
+ }
+ }
+ break;
}
rc = hid_parse(hdev);
@@ -933,12 +1033,16 @@ static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report,
{
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
- if ((drvdata->invert_pen_inrange) &&
- (report->type == HID_INPUT_REPORT) &&
+ if ((report->type == HID_INPUT_REPORT) &&
(report->id == UCLOGIC_PEN_REPORT_ID) &&
- (size >= 2))
- /* Invert the in-range bit */
- data[1] ^= 0x40;
+ (size >= 2)) {
+ if (drvdata->has_virtual_pad_interface && (data[1] & 0x20))
+ /* Change to virtual frame button report ID */
+ data[0] = 0xf7;
+ else if (drvdata->invert_pen_inrange)
+ /* Invert the in-range bit */
+ data[1] ^= 0x40;
+ }
return 0;
}
@@ -960,6 +1064,11 @@ static const struct hid_device_id uclogic_devices[] = {
USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
{ }
};
MODULE_DEVICE_TABLE(hid, uclogic_devices);
diff --git a/drivers/hid/hid-waltop.c b/drivers/hid/hid-waltop.c
index 059931d7b392..a91aabe4a70a 100644
--- a/drivers/hid/hid-waltop.c
+++ b/drivers/hid/hid-waltop.c
@@ -42,11 +42,6 @@
* 02 16 02 ink
*/
-/*
- * See Slim Tablet 5.8 inch description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Slim_Tablet_5.8%22
- */
-
/* Size of the original report descriptor of Slim Tablet 5.8 inch */
#define SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE 222
@@ -98,11 +93,6 @@ static __u8 slim_tablet_5_8_inch_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-/*
- * See Slim Tablet 12.1 inch description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Slim_Tablet_12.1%22
- */
-
/* Size of the original report descriptor of Slim Tablet 12.1 inch */
#define SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE 269
@@ -154,11 +144,6 @@ static __u8 slim_tablet_12_1_inch_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-/*
- * See Q Pad description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Q_Pad
- */
-
/* Size of the original report descriptor of Q Pad */
#define Q_PAD_RDESC_ORIG_SIZE 241
@@ -210,11 +195,6 @@ static __u8 q_pad_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-/*
- * See description, device and HID report descriptors of tablet with PID 0038 at
- * http://sf.net/apps/mediawiki/digimend/?title=Waltop_PID_0038
- */
-
/* Size of the original report descriptor of tablet with PID 0038 */
#define PID_0038_RDESC_ORIG_SIZE 241
@@ -268,11 +248,6 @@ static __u8 pid_0038_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-/*
- * See Media Tablet 10.6 inch description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Media_Tablet_10.6%22
- */
-
/* Size of the original report descriptor of Media Tablet 10.6 inch */
#define MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE 300
@@ -386,11 +361,6 @@ static __u8 media_tablet_10_6_inch_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-/*
- * See Media Tablet 14.1 inch description, device and HID report descriptors at
- * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Media_Tablet_14.1%22
- */
-
/* Size of the original report descriptor of Media Tablet 14.1 inch */
#define MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE 309
@@ -502,12 +472,6 @@ static __u8 media_tablet_14_1_inch_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-/*
- * See Sirius Battery Free Tablet description, device and HID report descriptors
- * at
- * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Sirius_Battery_Free_Tablet
- */
-
/* Size of the original report descriptor of Sirius Battery Free Tablet */
#define SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE 335
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index b4b8c6abb03e..0a0eca5da47d 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -76,6 +76,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K95RGB, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_STRAFE, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT },
@@ -98,6 +99,7 @@ static const struct hid_blacklist {
{ 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_PRO_4_JP, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
@@ -143,7 +145,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
- { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h
index 4681a65a4579..b4800ea891cb 100644
--- a/drivers/hid/wacom.h
+++ b/drivers/hid/wacom.h
@@ -90,6 +90,8 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/hid.h>
+#include <linux/kfifo.h>
+#include <linux/leds.h>
#include <linux/usb/input.h>
#include <linux/power_supply.h>
#include <asm/unaligned.h>
@@ -105,32 +107,95 @@
#define USB_VENDOR_ID_WACOM 0x056a
#define USB_VENDOR_ID_LENOVO 0x17ef
+enum wacom_worker {
+ WACOM_WORKER_WIRELESS,
+ WACOM_WORKER_BATTERY,
+ WACOM_WORKER_REMOTE,
+};
+
+struct wacom;
+
+struct wacom_led {
+ struct led_classdev cdev;
+ struct led_trigger trigger;
+ struct wacom *wacom;
+ unsigned int group;
+ unsigned int id;
+ u8 llv;
+ u8 hlv;
+ bool held;
+};
+
+struct wacom_group_leds {
+ u8 select; /* status led selector (0..3) */
+ struct wacom_led *leds;
+ unsigned int count;
+ struct device *dev;
+};
+
+struct wacom_battery {
+ struct wacom *wacom;
+ struct power_supply_desc bat_desc;
+ struct power_supply *battery;
+ char bat_name[WACOM_NAME_MAX];
+ int battery_capacity;
+ int bat_charging;
+ int bat_connected;
+ int ps_connected;
+};
+
+struct wacom_remote {
+ spinlock_t remote_lock;
+ struct kfifo remote_fifo;
+ struct kobject *remote_dir;
+ struct {
+ struct attribute_group group;
+ u32 serial;
+ struct input_dev *input;
+ bool registered;
+ struct wacom_battery battery;
+ } remotes[WACOM_MAX_REMOTES];
+};
+
struct wacom {
struct usb_device *usbdev;
struct usb_interface *intf;
struct wacom_wac wacom_wac;
struct hid_device *hdev;
struct mutex lock;
- struct work_struct work;
- struct wacom_led {
- u8 select[5]; /* status led selector (0..3) */
+ struct work_struct wireless_work;
+ struct work_struct battery_work;
+ struct work_struct remote_work;
+ struct wacom_remote *remote;
+ struct wacom_leds {
+ struct wacom_group_leds *groups;
+ unsigned int count;
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 */
+ u8 max_llv; /* maximum brightness of LED (llv) */
+ u8 max_hlv; /* maximum brightness of LED (hlv) */
} led;
- bool led_initialized;
- struct power_supply *battery;
- 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];
+ struct wacom_battery battery;
+ bool resources;
};
-static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
+static inline void wacom_schedule_work(struct wacom_wac *wacom_wac,
+ enum wacom_worker which)
{
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
- schedule_work(&wacom->work);
+
+ switch (which) {
+ case WACOM_WORKER_WIRELESS:
+ schedule_work(&wacom->wireless_work);
+ break;
+ case WACOM_WORKER_BATTERY:
+ schedule_work(&wacom->battery_work);
+ break;
+ case WACOM_WORKER_REMOTE:
+ schedule_work(&wacom->remote_work);
+ break;
+ }
}
extern const struct hid_device_id wacom_ids[];
@@ -149,7 +214,8 @@ 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);
+enum led_brightness wacom_leds_brightness_get(struct wacom_led *led);
+struct wacom_led *wacom_led_find(struct wacom *wacom, unsigned int group,
+ unsigned int id);
+struct wacom_led *wacom_led_next(struct wacom *wacom, struct wacom_led *cur);
#endif
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 499cc8213cfe..5e7a5648e708 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -25,7 +25,6 @@
#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)
@@ -91,7 +90,12 @@ static void wacom_close(struct input_dev *dev)
{
struct wacom *wacom = input_get_drvdata(dev);
- hid_hw_close(wacom->hdev);
+ /*
+ * wacom->hdev should never be null, but surprisingly, I had the case
+ * once while unplugging the Wacom Wireless Receiver.
+ */
+ if (wacom->hdev)
+ hid_hw_close(wacom->hdev);
}
/*
@@ -523,36 +527,95 @@ struct wacom_hdev_data {
static LIST_HEAD(wacom_udev_list);
static DEFINE_MUTEX(wacom_udev_list_lock);
+static bool compare_device_paths(struct hid_device *hdev_a,
+ struct hid_device *hdev_b, char separator)
+{
+ int n1 = strrchr(hdev_a->phys, separator) - hdev_a->phys;
+ int n2 = strrchr(hdev_b->phys, separator) - hdev_b->phys;
+
+ if (n1 != n2 || n1 <= 0 || n2 <= 0)
+ return false;
+
+ return !strncmp(hdev_a->phys, hdev_b->phys, n1);
+}
+
static bool wacom_are_sibling(struct hid_device *hdev,
struct hid_device *sibling)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_features *features = &wacom->wacom_wac.features;
- int vid = features->oVid;
- int pid = features->oPid;
- int n1,n2;
+ struct wacom *sibling_wacom = hid_get_drvdata(sibling);
+ struct wacom_features *sibling_features = &sibling_wacom->wacom_wac.features;
+ __u32 oVid = features->oVid ? features->oVid : hdev->vendor;
+ __u32 oPid = features->oPid ? features->oPid : hdev->product;
+
+ /* The defined oVid/oPid must match that of the sibling */
+ if (features->oVid != HID_ANY_ID && sibling->vendor != oVid)
+ return false;
+ if (features->oPid != HID_ANY_ID && sibling->product != oPid)
+ return false;
- if (vid == 0 && pid == 0) {
- vid = hdev->vendor;
- pid = hdev->product;
+ /*
+ * Devices with the same VID/PID must share the same physical
+ * device path, while those with different VID/PID must share
+ * the same physical parent device path.
+ */
+ if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) {
+ if (!compare_device_paths(hdev, sibling, '/'))
+ return false;
+ } else {
+ if (!compare_device_paths(hdev, sibling, '.'))
+ return false;
}
- if (vid != sibling->vendor || pid != sibling->product)
+ /* Skip the remaining heuristics unless you are a HID_GENERIC device */
+ if (features->type != HID_GENERIC)
+ return true;
+
+ /*
+ * Direct-input devices may not be siblings of indirect-input
+ * devices.
+ */
+ if ((features->device_type & WACOM_DEVICETYPE_DIRECT) &&
+ !(sibling_features->device_type & WACOM_DEVICETYPE_DIRECT))
return false;
- /* Compare the physical path. */
- n1 = strrchr(hdev->phys, '.') - hdev->phys;
- n2 = strrchr(sibling->phys, '.') - sibling->phys;
- if (n1 != n2 || n1 <= 0 || n2 <= 0)
+ /*
+ * Indirect-input devices may not be siblings of direct-input
+ * devices.
+ */
+ if (!(features->device_type & WACOM_DEVICETYPE_DIRECT) &&
+ (sibling_features->device_type & WACOM_DEVICETYPE_DIRECT))
+ return false;
+
+ /* Pen devices may only be siblings of touch devices */
+ if ((features->device_type & WACOM_DEVICETYPE_PEN) &&
+ !(sibling_features->device_type & WACOM_DEVICETYPE_TOUCH))
return false;
- return !strncmp(hdev->phys, sibling->phys, n1);
+ /* Touch devices may only be siblings of pen devices */
+ if ((features->device_type & WACOM_DEVICETYPE_TOUCH) &&
+ !(sibling_features->device_type & WACOM_DEVICETYPE_PEN))
+ return false;
+
+ /*
+ * No reason could be found for these two devices to NOT be
+ * siblings, so there's a good chance they ARE siblings
+ */
+ return true;
}
static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev)
{
struct wacom_hdev_data *data;
+ /* Try to find an already-probed interface from the same device */
+ list_for_each_entry(data, &wacom_udev_list, list) {
+ if (compare_device_paths(hdev, data->dev, '/'))
+ return data;
+ }
+
+ /* Fallback to finding devices that appear to be "siblings" */
list_for_each_entry(data, &wacom_udev_list, list) {
if (wacom_are_sibling(hdev, data->dev)) {
kref_get(&data->kref);
@@ -563,6 +626,38 @@ static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev)
return NULL;
}
+static void wacom_release_shared_data(struct kref *kref)
+{
+ struct wacom_hdev_data *data =
+ container_of(kref, struct wacom_hdev_data, kref);
+
+ mutex_lock(&wacom_udev_list_lock);
+ list_del(&data->list);
+ mutex_unlock(&wacom_udev_list_lock);
+
+ kfree(data);
+}
+
+static void wacom_remove_shared_data(void *res)
+{
+ struct wacom *wacom = res;
+ struct wacom_hdev_data *data;
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
+ if (wacom_wac->shared) {
+ data = container_of(wacom_wac->shared, struct wacom_hdev_data,
+ shared);
+
+ if (wacom_wac->shared->touch == wacom->hdev)
+ wacom_wac->shared->touch = NULL;
+ else if (wacom_wac->shared->pen == wacom->hdev)
+ wacom_wac->shared->pen = NULL;
+
+ kref_put(&data->kref, wacom_release_shared_data);
+ wacom_wac->shared = NULL;
+ }
+}
+
static int wacom_add_shared_data(struct hid_device *hdev)
{
struct wacom *wacom = hid_get_drvdata(hdev);
@@ -587,6 +682,13 @@ static int wacom_add_shared_data(struct hid_device *hdev)
wacom_wac->shared = &data->shared;
+ retval = devm_add_action(&hdev->dev, wacom_remove_shared_data, wacom);
+ if (retval) {
+ mutex_unlock(&wacom_udev_list_lock);
+ wacom_remove_shared_data(wacom);
+ return retval;
+ }
+
if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)
wacom_wac->shared->touch = hdev;
else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN)
@@ -597,37 +699,6 @@ out:
return retval;
}
-static void wacom_release_shared_data(struct kref *kref)
-{
- struct wacom_hdev_data *data =
- container_of(kref, struct wacom_hdev_data, kref);
-
- mutex_lock(&wacom_udev_list_lock);
- list_del(&data->list);
- mutex_unlock(&wacom_udev_list_lock);
-
- kfree(data);
-}
-
-static void wacom_remove_shared_data(struct wacom *wacom)
-{
- struct wacom_hdev_data *data;
- struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-
- if (wacom_wac->shared) {
- data = container_of(wacom_wac->shared, struct wacom_hdev_data,
- shared);
-
- if (wacom_wac->shared->touch == wacom->hdev)
- wacom_wac->shared->touch = NULL;
- else if (wacom_wac->shared->pen == wacom->hdev)
- wacom_wac->shared->pen = NULL;
-
- kref_put(&data->kref, wacom_release_shared_data);
- wacom_wac->shared = NULL;
- }
-}
-
static int wacom_led_control(struct wacom *wacom)
{
unsigned char *buf;
@@ -635,6 +706,12 @@ static int wacom_led_control(struct wacom *wacom)
unsigned char report_id = WAC_CMD_LED_CONTROL;
int buf_size = 9;
+ if (!hid_get_drvdata(wacom->hdev))
+ return -ENODEV;
+
+ if (!wacom->led.groups)
+ return -ENOTSUPP;
+
if (wacom->wacom_wac.pid) { /* wireless connected */
report_id = WAC_CMD_WL_LED_CONTROL;
buf_size = 13;
@@ -650,7 +727,7 @@ static int wacom_led_control(struct wacom *wacom)
* one of four values:
* 0 = Low; 1 = Medium; 2 = High; 3 = Off
*/
- int ring_led = wacom->led.select[0] & 0x03;
+ int ring_led = wacom->led.groups[0].select & 0x03;
int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03;
int crop_lum = 0;
unsigned char led_bits = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
@@ -665,11 +742,11 @@ static int wacom_led_control(struct wacom *wacom)
buf[1] = led_bits;
}
else {
- int led = wacom->led.select[0] | 0x4;
+ int led = wacom->led.groups[0].select | 0x4;
if (wacom->wacom_wac.features.type == WACOM_21UX2 ||
wacom->wacom_wac.features.type == WACOM_24HD)
- led |= (wacom->led.select[1] << 4) | 0x40;
+ led |= (wacom->led.groups[1].select << 4) | 0x40;
buf[0] = report_id;
buf[1] = led;
@@ -741,7 +818,7 @@ static ssize_t wacom_led_select_store(struct device *dev, int set_id,
mutex_lock(&wacom->lock);
- wacom->led.select[set_id] = id & 0x3;
+ wacom->led.groups[set_id].select = id & 0x3;
err = wacom_led_control(wacom);
mutex_unlock(&wacom->lock);
@@ -761,7 +838,7 @@ static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \
struct hid_device *hdev = to_hid_device(dev);\
struct wacom *wacom = hid_get_drvdata(hdev); \
return scnprintf(buf, PAGE_SIZE, "%d\n", \
- wacom->led.select[SET_ID]); \
+ wacom->led.groups[SET_ID].select); \
} \
static DEVICE_ATTR(status_led##SET_ID##_select, DEV_ATTR_RW_PERM, \
wacom_led##SET_ID##_select_show, \
@@ -904,6 +981,327 @@ static struct attribute_group intuos5_led_attr_group = {
.attrs = intuos5_led_attrs,
};
+struct wacom_sysfs_group_devres {
+ struct attribute_group *group;
+ struct kobject *root;
+};
+
+static void wacom_devm_sysfs_group_release(struct device *dev, void *res)
+{
+ struct wacom_sysfs_group_devres *devres = res;
+ struct kobject *kobj = devres->root;
+
+ dev_dbg(dev, "%s: dropping reference to %s\n",
+ __func__, devres->group->name);
+ sysfs_remove_group(kobj, devres->group);
+}
+
+static int __wacom_devm_sysfs_create_group(struct wacom *wacom,
+ struct kobject *root,
+ struct attribute_group *group)
+{
+ struct wacom_sysfs_group_devres *devres;
+ int error;
+
+ devres = devres_alloc(wacom_devm_sysfs_group_release,
+ sizeof(struct wacom_sysfs_group_devres),
+ GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
+
+ devres->group = group;
+ devres->root = root;
+
+ error = sysfs_create_group(devres->root, group);
+ if (error)
+ return error;
+
+ devres_add(&wacom->hdev->dev, devres);
+
+ return 0;
+}
+
+static int wacom_devm_sysfs_create_group(struct wacom *wacom,
+ struct attribute_group *group)
+{
+ return __wacom_devm_sysfs_create_group(wacom, &wacom->hdev->dev.kobj,
+ group);
+}
+
+enum led_brightness wacom_leds_brightness_get(struct wacom_led *led)
+{
+ struct wacom *wacom = led->wacom;
+
+ if (wacom->led.max_hlv)
+ return led->hlv * LED_FULL / wacom->led.max_hlv;
+
+ if (wacom->led.max_llv)
+ return led->llv * LED_FULL / wacom->led.max_llv;
+
+ /* device doesn't support brightness tuning */
+ return LED_FULL;
+}
+
+static enum led_brightness __wacom_led_brightness_get(struct led_classdev *cdev)
+{
+ struct wacom_led *led = container_of(cdev, struct wacom_led, cdev);
+ struct wacom *wacom = led->wacom;
+
+ if (wacom->led.groups[led->group].select != led->id)
+ return LED_OFF;
+
+ return wacom_leds_brightness_get(led);
+}
+
+static int wacom_led_brightness_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct wacom_led *led = container_of(cdev, struct wacom_led, cdev);
+ struct wacom *wacom = led->wacom;
+ int error;
+
+ mutex_lock(&wacom->lock);
+
+ if (!wacom->led.groups || (brightness == LED_OFF &&
+ wacom->led.groups[led->group].select != led->id)) {
+ error = 0;
+ goto out;
+ }
+
+ led->llv = wacom->led.llv = wacom->led.max_llv * brightness / LED_FULL;
+ led->hlv = wacom->led.hlv = wacom->led.max_hlv * brightness / LED_FULL;
+
+ wacom->led.groups[led->group].select = led->id;
+
+ error = wacom_led_control(wacom);
+
+out:
+ mutex_unlock(&wacom->lock);
+
+ return error;
+}
+
+static void wacom_led_readonly_brightness_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+}
+
+static int wacom_led_register_one(struct device *dev, struct wacom *wacom,
+ struct wacom_led *led, unsigned int group,
+ unsigned int id, bool read_only)
+{
+ int error;
+ char *name;
+
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ "%s::wacom-%d.%d",
+ dev_name(dev),
+ group,
+ id);
+ if (!name)
+ return -ENOMEM;
+
+ if (!read_only) {
+ led->trigger.name = name;
+ error = devm_led_trigger_register(dev, &led->trigger);
+ if (error) {
+ hid_err(wacom->hdev,
+ "failed to register LED trigger %s: %d\n",
+ led->cdev.name, error);
+ return error;
+ }
+ }
+
+ led->group = group;
+ led->id = id;
+ led->wacom = wacom;
+ led->llv = wacom->led.llv;
+ led->hlv = wacom->led.hlv;
+ led->cdev.name = name;
+ led->cdev.max_brightness = LED_FULL;
+ led->cdev.flags = LED_HW_PLUGGABLE;
+ led->cdev.brightness_get = __wacom_led_brightness_get;
+ if (!read_only) {
+ led->cdev.brightness_set_blocking = wacom_led_brightness_set;
+ led->cdev.default_trigger = led->cdev.name;
+ } else {
+ led->cdev.brightness_set = wacom_led_readonly_brightness_set;
+ }
+
+ error = devm_led_classdev_register(dev, &led->cdev);
+ if (error) {
+ hid_err(wacom->hdev,
+ "failed to register LED %s: %d\n",
+ led->cdev.name, error);
+ led->cdev.name = NULL;
+ return error;
+ }
+
+ return 0;
+}
+
+static void wacom_led_groups_release_one(void *data)
+{
+ struct wacom_group_leds *group = data;
+
+ devres_release_group(group->dev, group);
+}
+
+static int wacom_led_groups_alloc_and_register_one(struct device *dev,
+ struct wacom *wacom,
+ int group_id, int count,
+ bool read_only)
+{
+ struct wacom_led *leds;
+ int i, error;
+
+ if (group_id >= wacom->led.count || count <= 0)
+ return -EINVAL;
+
+ if (!devres_open_group(dev, &wacom->led.groups[group_id], GFP_KERNEL))
+ return -ENOMEM;
+
+ leds = devm_kzalloc(dev, sizeof(struct wacom_led) * count, GFP_KERNEL);
+ if (!leds) {
+ error = -ENOMEM;
+ goto err;
+ }
+
+ wacom->led.groups[group_id].leds = leds;
+ wacom->led.groups[group_id].count = count;
+
+ for (i = 0; i < count; i++) {
+ error = wacom_led_register_one(dev, wacom, &leds[i],
+ group_id, i, read_only);
+ if (error)
+ goto err;
+ }
+
+ wacom->led.groups[group_id].dev = dev;
+
+ devres_close_group(dev, &wacom->led.groups[group_id]);
+
+ /*
+ * There is a bug (?) in devm_led_classdev_register() in which its
+ * increments the refcount of the parent. If the parent is an input
+ * device, that means the ref count never reaches 0 when
+ * devm_input_device_release() gets called.
+ * This means that the LEDs are still there after disconnect.
+ * Manually force the release of the group so that the leds are released
+ * once we are done using them.
+ */
+ error = devm_add_action_or_reset(&wacom->hdev->dev,
+ wacom_led_groups_release_one,
+ &wacom->led.groups[group_id]);
+ if (error)
+ return error;
+
+ return 0;
+
+err:
+ devres_release_group(dev, &wacom->led.groups[group_id]);
+ return error;
+}
+
+struct wacom_led *wacom_led_find(struct wacom *wacom, unsigned int group_id,
+ unsigned int id)
+{
+ struct wacom_group_leds *group;
+
+ if (group_id >= wacom->led.count)
+ return NULL;
+
+ group = &wacom->led.groups[group_id];
+
+ if (!group->leds)
+ return NULL;
+
+ id %= group->count;
+
+ return &group->leds[id];
+}
+
+/**
+ * wacom_led_next: gives the next available led with a wacom trigger.
+ *
+ * returns the next available struct wacom_led which has its default trigger
+ * or the current one if none is available.
+ */
+struct wacom_led *wacom_led_next(struct wacom *wacom, struct wacom_led *cur)
+{
+ struct wacom_led *next_led;
+ int group, next;
+
+ if (!wacom || !cur)
+ return NULL;
+
+ group = cur->group;
+ next = cur->id;
+
+ do {
+ next_led = wacom_led_find(wacom, group, ++next);
+ if (!next_led || next_led == cur)
+ return next_led;
+ } while (next_led->cdev.trigger != &next_led->trigger);
+
+ return next_led;
+}
+
+static void wacom_led_groups_release(void *data)
+{
+ struct wacom *wacom = data;
+
+ wacom->led.groups = NULL;
+ wacom->led.count = 0;
+}
+
+static int wacom_led_groups_allocate(struct wacom *wacom, int count)
+{
+ struct device *dev = &wacom->hdev->dev;
+ struct wacom_group_leds *groups;
+ int error;
+
+ groups = devm_kzalloc(dev, sizeof(struct wacom_group_leds) * count,
+ GFP_KERNEL);
+ if (!groups)
+ return -ENOMEM;
+
+ error = devm_add_action_or_reset(dev, wacom_led_groups_release, wacom);
+ if (error)
+ return error;
+
+ wacom->led.groups = groups;
+ wacom->led.count = count;
+
+ return 0;
+}
+
+static int wacom_leds_alloc_and_register(struct wacom *wacom, int group_count,
+ int led_per_group, bool read_only)
+{
+ struct device *dev;
+ int i, error;
+
+ if (!wacom->wacom_wac.pad_input)
+ return -EINVAL;
+
+ dev = &wacom->wacom_wac.pad_input->dev;
+
+ error = wacom_led_groups_allocate(wacom, group_count);
+ if (error)
+ return error;
+
+ for (i = 0; i < group_count; i++) {
+ error = wacom_led_groups_alloc_and_register_one(dev, wacom, i,
+ led_per_group,
+ read_only);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
static int wacom_initialize_leds(struct wacom *wacom)
{
int error;
@@ -917,25 +1315,38 @@ static int wacom_initialize_leds(struct wacom *wacom)
case INTUOS4:
case INTUOS4WL:
case INTUOS4L:
- wacom->led.select[0] = 0;
- wacom->led.select[1] = 0;
wacom->led.llv = 10;
wacom->led.hlv = 20;
+ wacom->led.max_llv = 127;
+ wacom->led.max_hlv = 127;
wacom->led.img_lum = 10;
- error = sysfs_create_group(&wacom->hdev->dev.kobj,
- &intuos4_led_attr_group);
+
+ error = wacom_leds_alloc_and_register(wacom, 1, 4, false);
+ if (error) {
+ hid_err(wacom->hdev,
+ "cannot create leds err: %d\n", error);
+ return error;
+ }
+
+ error = wacom_devm_sysfs_create_group(wacom,
+ &intuos4_led_attr_group);
break;
case WACOM_24HD:
case WACOM_21UX2:
- wacom->led.select[0] = 0;
- wacom->led.select[1] = 0;
wacom->led.llv = 0;
wacom->led.hlv = 0;
wacom->led.img_lum = 0;
- error = sysfs_create_group(&wacom->hdev->dev.kobj,
- &cintiq_led_attr_group);
+ error = wacom_leds_alloc_and_register(wacom, 2, 4, false);
+ if (error) {
+ hid_err(wacom->hdev,
+ "cannot create leds err: %d\n", error);
+ return error;
+ }
+
+ error = wacom_devm_sysfs_create_group(wacom,
+ &cintiq_led_attr_group);
break;
case INTUOS5S:
@@ -944,16 +1355,31 @@ static int wacom_initialize_leds(struct wacom *wacom)
case INTUOSPS:
case INTUOSPM:
case INTUOSPL:
- wacom->led.select[0] = 0;
- wacom->led.select[1] = 0;
wacom->led.llv = 32;
- wacom->led.hlv = 0;
- wacom->led.img_lum = 0;
+ wacom->led.max_llv = 96;
+
+ error = wacom_leds_alloc_and_register(wacom, 1, 4, false);
+ if (error) {
+ hid_err(wacom->hdev,
+ "cannot create leds err: %d\n", error);
+ return error;
+ }
- error = sysfs_create_group(&wacom->hdev->dev.kobj,
- &intuos5_led_attr_group);
+ error = wacom_devm_sysfs_create_group(wacom,
+ &intuos5_led_attr_group);
break;
+ case REMOTE:
+ wacom->led.llv = 255;
+ wacom->led.max_llv = 255;
+ error = wacom_led_groups_allocate(wacom, 5);
+ if (error) {
+ hid_err(wacom->hdev,
+ "cannot create leds err: %d\n", error);
+ return error;
+ }
+ return 0;
+
default:
return 0;
}
@@ -964,86 +1390,45 @@ static int wacom_initialize_leds(struct wacom *wacom)
return error;
}
wacom_led_control(wacom);
- wacom->led_initialized = true;
return 0;
}
-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) {
- case INTUOS4S:
- case INTUOS4:
- case INTUOS4WL:
- case INTUOS4L:
- sysfs_remove_group(&wacom->hdev->dev.kobj,
- &intuos4_led_attr_group);
- break;
-
- case WACOM_24HD:
- case WACOM_21UX2:
- sysfs_remove_group(&wacom->hdev->dev.kobj,
- &cintiq_led_attr_group);
- break;
-
- case INTUOS5S:
- case INTUOS5:
- case INTUOS5L:
- case INTUOSPS:
- case INTUOSPM:
- case INTUOSPL:
- sysfs_remove_group(&wacom->hdev->dev.kobj,
- &intuos5_led_attr_group);
- break;
- }
-}
-
static enum power_supply_property wacom_battery_props[] = {
+ POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_CAPACITY
};
-static enum power_supply_property wacom_ac_props[] = {
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_SCOPE,
-};
-
static int wacom_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
- struct wacom *wacom = power_supply_get_drvdata(psy);
+ struct wacom_battery *battery = power_supply_get_drvdata(psy);
int ret = 0;
switch (psp) {
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = battery->wacom->wacom_wac.name;
+ break;
case POWER_SUPPLY_PROP_PRESENT:
- val->intval = wacom->wacom_wac.bat_connected;
+ val->intval = battery->bat_connected;
break;
case POWER_SUPPLY_PROP_SCOPE:
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
break;
case POWER_SUPPLY_PROP_CAPACITY:
- val->intval =
- wacom->wacom_wac.battery_capacity;
+ val->intval = battery->battery_capacity;
break;
case POWER_SUPPLY_PROP_STATUS:
- if (wacom->wacom_wac.bat_charging)
+ if (battery->bat_charging)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
- else if (wacom->wacom_wac.battery_capacity == 100 &&
- wacom->wacom_wac.ps_connected)
+ else if (battery->battery_capacity == 100 &&
+ battery->ps_connected)
val->intval = POWER_SUPPLY_STATUS_FULL;
- else if (wacom->wacom_wac.ps_connected)
+ else if (battery->ps_connected)
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
else
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
@@ -1056,84 +1441,64 @@ static int wacom_battery_get_property(struct power_supply *psy,
return ret;
}
-static int wacom_ac_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
+static int __wacom_initialize_battery(struct wacom *wacom,
+ struct wacom_battery *battery)
{
- struct wacom *wacom = power_supply_get_drvdata(psy);
- int ret = 0;
+ static atomic_t battery_no = ATOMIC_INIT(0);
+ struct device *dev = &wacom->hdev->dev;
+ struct power_supply_config psy_cfg = { .drv_data = battery, };
+ struct power_supply *ps_bat;
+ struct power_supply_desc *bat_desc = &battery->bat_desc;
+ unsigned long n;
+ int error;
- switch (psp) {
- case POWER_SUPPLY_PROP_PRESENT:
- /* fall through */
- case POWER_SUPPLY_PROP_ONLINE:
- val->intval = wacom->wacom_wac.ps_connected;
- break;
- case POWER_SUPPLY_PROP_SCOPE:
- val->intval = POWER_SUPPLY_SCOPE_DEVICE;
- break;
- default:
- ret = -EINVAL;
- break;
+ if (!devres_open_group(dev, bat_desc, GFP_KERNEL))
+ return -ENOMEM;
+
+ battery->wacom = wacom;
+
+ n = atomic_inc_return(&battery_no) - 1;
+
+ bat_desc->properties = wacom_battery_props;
+ bat_desc->num_properties = ARRAY_SIZE(wacom_battery_props);
+ bat_desc->get_property = wacom_battery_get_property;
+ sprintf(battery->bat_name, "wacom_battery_%ld", n);
+ bat_desc->name = battery->bat_name;
+ bat_desc->type = POWER_SUPPLY_TYPE_USB;
+ bat_desc->use_for_apm = 0;
+
+ ps_bat = devm_power_supply_register(dev, bat_desc, &psy_cfg);
+ if (IS_ERR(ps_bat)) {
+ error = PTR_ERR(ps_bat);
+ goto err;
}
- return ret;
+
+ power_supply_powers(ps_bat, &wacom->hdev->dev);
+
+ battery->battery = ps_bat;
+
+ devres_close_group(dev, bat_desc);
+ return 0;
+
+err:
+ devres_release_group(dev, bat_desc);
+ return error;
}
static int wacom_initialize_battery(struct wacom *wacom)
{
- static atomic_t battery_no = ATOMIC_INIT(0);
- struct power_supply_config psy_cfg = { .drv_data = wacom, };
- unsigned long n;
-
- if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) {
- struct power_supply_desc *bat_desc = &wacom->battery_desc;
- struct power_supply_desc *ac_desc = &wacom->ac_desc;
- n = atomic_inc_return(&battery_no) - 1;
-
- bat_desc->properties = wacom_battery_props;
- bat_desc->num_properties = ARRAY_SIZE(wacom_battery_props);
- bat_desc->get_property = wacom_battery_get_property;
- sprintf(wacom->wacom_wac.bat_name, "wacom_battery_%ld", n);
- bat_desc->name = wacom->wacom_wac.bat_name;
- bat_desc->type = POWER_SUPPLY_TYPE_BATTERY;
- bat_desc->use_for_apm = 0;
-
- ac_desc->properties = wacom_ac_props;
- ac_desc->num_properties = ARRAY_SIZE(wacom_ac_props);
- ac_desc->get_property = wacom_ac_get_property;
- sprintf(wacom->wacom_wac.ac_name, "wacom_ac_%ld", n);
- ac_desc->name = wacom->wacom_wac.ac_name;
- ac_desc->type = POWER_SUPPLY_TYPE_MAINS;
- ac_desc->use_for_apm = 0;
-
- wacom->battery = power_supply_register(&wacom->hdev->dev,
- &wacom->battery_desc, &psy_cfg);
- if (IS_ERR(wacom->battery))
- return PTR_ERR(wacom->battery);
-
- power_supply_powers(wacom->battery, &wacom->hdev->dev);
-
- wacom->ac = power_supply_register(&wacom->hdev->dev,
- &wacom->ac_desc,
- &psy_cfg);
- if (IS_ERR(wacom->ac)) {
- power_supply_unregister(wacom->battery);
- return PTR_ERR(wacom->ac);
- }
-
- power_supply_powers(wacom->ac, &wacom->hdev->dev);
- }
+ if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY)
+ return __wacom_initialize_battery(wacom, &wacom->battery);
return 0;
}
static void wacom_destroy_battery(struct wacom *wacom)
{
- if (wacom->battery) {
- power_supply_unregister(wacom->battery);
- wacom->battery = NULL;
- power_supply_unregister(wacom->ac);
- wacom->ac = NULL;
+ if (wacom->battery.battery) {
+ devres_release_group(&wacom->hdev->dev,
+ &wacom->battery.bat_desc);
+ wacom->battery.battery = NULL;
}
}
@@ -1179,7 +1544,7 @@ static ssize_t wacom_show_remote_mode(struct kobject *kobj,
struct wacom *wacom = hid_get_drvdata(hdev);
u8 mode;
- mode = wacom->led.select[index];
+ mode = wacom->led.groups[index].select;
if (mode >= 0 && mode < 3)
return snprintf(buf, PAGE_SIZE, "%d\n", mode);
else
@@ -1212,54 +1577,30 @@ 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)
+static 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;
+ struct wacom_remote *remote = wacom->remote;
- wacom_wac->serial[index] = serial;
-
- buf = kzalloc(WAC_REMOTE_SERIAL_MAX_STRLEN, GFP_KERNEL);
- if (!buf)
+ remote->remotes[index].group.name = devm_kasprintf(&wacom->hdev->dev,
+ GFP_KERNEL,
+ "%d", serial);
+ if (!remote->remotes[index].group.name)
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]);
+ error = __wacom_devm_sysfs_create_group(wacom, remote->remote_dir,
+ &remote->remotes[index].group);
if (error) {
+ remote->remotes[index].group.name = NULL;
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;
@@ -1316,27 +1657,57 @@ static const struct attribute *remote_unpair_attrs[] = {
NULL
};
-static int wacom_initialize_remote(struct wacom *wacom)
+static void wacom_remotes_destroy(void *data)
+{
+ struct wacom *wacom = data;
+ struct wacom_remote *remote = wacom->remote;
+
+ if (!remote)
+ return;
+
+ kobject_put(remote->remote_dir);
+ kfifo_free(&remote->remote_fifo);
+ wacom->remote = NULL;
+}
+
+static int wacom_initialize_remotes(struct wacom *wacom)
{
int error = 0;
- struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
+ struct wacom_remote *remote;
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;
+ remote = devm_kzalloc(&wacom->hdev->dev, sizeof(*wacom->remote),
+ GFP_KERNEL);
+ if (!remote)
+ return -ENOMEM;
+
+ wacom->remote = remote;
- wacom->remote_dir = kobject_create_and_add("wacom_remote",
- &wacom->hdev->dev.kobj);
- if (!wacom->remote_dir)
+ spin_lock_init(&remote->remote_lock);
+
+ error = kfifo_alloc(&remote->remote_fifo,
+ 5 * sizeof(struct wacom_remote_data),
+ GFP_KERNEL);
+ if (error) {
+ hid_err(wacom->hdev, "failed allocating remote_fifo\n");
return -ENOMEM;
+ }
- error = sysfs_create_files(wacom->remote_dir, remote_unpair_attrs);
+ remote->remotes[0].group = remote0_serial_group;
+ remote->remotes[1].group = remote1_serial_group;
+ remote->remotes[2].group = remote2_serial_group;
+ remote->remotes[3].group = remote3_serial_group;
+ remote->remotes[4].group = remote4_serial_group;
+
+ remote->remote_dir = kobject_create_and_add("wacom_remote",
+ &wacom->hdev->dev.kobj);
+ if (!remote->remote_dir)
+ return -ENOMEM;
+
+ error = sysfs_create_files(remote->remote_dir, remote_unpair_attrs);
if (error) {
hid_err(wacom->hdev,
@@ -1345,10 +1716,15 @@ static int wacom_initialize_remote(struct wacom *wacom)
}
for (i = 0; i < WACOM_MAX_REMOTES; i++) {
- wacom->led.select[i] = WACOM_STATUS_UNKNOWN;
- wacom_wac->serial[i] = 0;
+ wacom->led.groups[i].select = WACOM_STATUS_UNKNOWN;
+ remote->remotes[i].serial = 0;
}
+ error = devm_add_action_or_reset(&wacom->hdev->dev,
+ wacom_remotes_destroy, wacom);
+ if (error)
+ return error;
+
return 0;
}
@@ -1358,7 +1734,7 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom)
struct hid_device *hdev = wacom->hdev;
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
- input_dev = input_allocate_device();
+ input_dev = devm_input_allocate_device(&hdev->dev);
if (!input_dev)
return NULL;
@@ -1377,36 +1753,6 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom)
return input_dev;
}
-static void wacom_clean_inputs(struct wacom *wacom)
-{
- 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);
- }
- kobject_put(wacom->remote_dir);
- wacom->wacom_wac.pen_input = NULL;
- wacom->wacom_wac.touch_input = NULL;
- wacom->wacom_wac.pad_input = NULL;
- wacom->wacom_wac.pen_registered = false;
- wacom->wacom_wac.touch_registered = false;
- wacom->wacom_wac.pad_registered = false;
- wacom_destroy_leds(wacom);
-}
-
static int wacom_allocate_inputs(struct wacom *wacom)
{
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
@@ -1414,10 +1760,10 @@ static int wacom_allocate_inputs(struct wacom *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);
+ if (!wacom_wac->pen_input ||
+ !wacom_wac->touch_input ||
+ !wacom_wac->pad_input)
return -ENOMEM;
- }
wacom_wac->pen_input->name = wacom_wac->pen_name;
wacom_wac->touch_input->name = wacom_wac->touch_name;
@@ -1448,8 +1794,7 @@ static int wacom_register_inputs(struct wacom *wacom)
} else {
error = input_register_device(pen_input_dev);
if (error)
- goto fail_register_pen_input;
- wacom_wac->pen_registered = true;
+ goto fail;
}
error = wacom_setup_touch_input_capabilities(touch_input_dev, wacom_wac);
@@ -1461,8 +1806,7 @@ static int wacom_register_inputs(struct wacom *wacom)
} else {
error = input_register_device(touch_input_dev);
if (error)
- goto fail_register_touch_input;
- wacom_wac->touch_registered = true;
+ goto fail;
}
error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac);
@@ -1474,37 +1818,15 @@ static int wacom_register_inputs(struct wacom *wacom)
} else {
error = input_register_device(pad_input_dev);
if (error)
- goto fail_register_pad_input;
- wacom_wac->pad_registered = true;
-
- error = wacom_initialize_leds(wacom);
- if (error)
- goto fail_leds;
-
- error = wacom_initialize_remote(wacom);
- if (error)
- goto fail_remote;
+ goto fail;
}
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:
- if (touch_input_dev)
- input_unregister_device(touch_input_dev);
+fail:
+ wacom_wac->pad_input = NULL;
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;
}
@@ -1543,14 +1865,14 @@ static void wacom_calculate_res(struct wacom_features *features)
void wacom_battery_work(struct work_struct *work)
{
- struct wacom *wacom = container_of(work, struct wacom, work);
+ struct wacom *wacom = container_of(work, struct wacom, battery_work);
if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) &&
- !wacom->battery) {
+ !wacom->battery.battery) {
wacom_initialize_battery(wacom);
}
else if (!(wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) &&
- wacom->battery) {
+ wacom->battery.battery) {
wacom_destroy_battery(wacom);
}
}
@@ -1606,6 +1928,9 @@ static void wacom_update_name(struct wacom *wacom, const char *suffix)
strlcpy(name, features->name, sizeof(name));
}
+ snprintf(wacom_wac->name, sizeof(wacom_wac->name), "%s%s",
+ name, suffix);
+
/* Append the device type to the name */
snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
"%s%s Pen", name, suffix);
@@ -1615,6 +1940,22 @@ static void wacom_update_name(struct wacom *wacom, const char *suffix)
"%s%s Pad", name, suffix);
}
+static void wacom_release_resources(struct wacom *wacom)
+{
+ struct hid_device *hdev = wacom->hdev;
+
+ if (!wacom->resources)
+ return;
+
+ devres_release_group(&hdev->dev, wacom);
+
+ wacom->resources = false;
+
+ wacom->wacom_wac.pen_input = NULL;
+ wacom->wacom_wac.touch_input = NULL;
+ wacom->wacom_wac.pad_input = NULL;
+}
+
static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
{
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
@@ -1627,9 +1968,14 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
if (features->pktlen > WACOM_PKGLEN_MAX)
return -EINVAL;
+ if (!devres_open_group(&hdev->dev, wacom, GFP_KERNEL))
+ return -ENOMEM;
+
+ wacom->resources = true;
+
error = wacom_allocate_inputs(wacom);
if (error)
- return error;
+ goto fail;
/*
* Bamboo Pad has a generic hid handling for the Pen, and we switch it
@@ -1642,7 +1988,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
} else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) &&
(features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) {
error = -ENODEV;
- goto fail_allocate_inputs;
+ goto fail;
}
}
@@ -1662,7 +2008,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
error ? "Ignoring" : "Assuming pen");
if (error)
- goto fail_parsed;
+ goto fail;
features->device_type |= WACOM_DEVICETYPE_PEN;
}
@@ -1673,18 +2019,28 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
error = wacom_add_shared_data(hdev);
if (error)
- goto fail_shared_data;
+ goto fail;
if (!(features->device_type & WACOM_DEVICETYPE_WL_MONITOR) &&
(features->quirks & WACOM_QUIRK_BATTERY)) {
error = wacom_initialize_battery(wacom);
if (error)
- goto fail_battery;
+ goto fail;
}
error = wacom_register_inputs(wacom);
if (error)
- goto fail_register_inputs;
+ goto fail;
+
+ if (wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD) {
+ error = wacom_initialize_leds(wacom);
+ if (error)
+ goto fail;
+
+ error = wacom_initialize_remotes(wacom);
+ if (error)
+ goto fail;
+ }
if (features->type == HID_GENERIC)
connect_mask |= HID_CONNECT_DRIVER;
@@ -1693,7 +2049,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
error = hid_hw_start(hdev, connect_mask);
if (error) {
hid_err(hdev, "hw start failed\n");
- goto fail_hw_start;
+ goto fail;
}
if (!wireless) {
@@ -1705,7 +2061,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
if ((features->type == BAMBOO_TOUCH) &&
(features->device_type & WACOM_DEVICETYPE_PEN)) {
error = -ENODEV;
- goto fail_hw_start;
+ goto fail_quirks;
}
/* pen only Bamboo neither support touch nor pad */
@@ -1713,37 +2069,33 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
((features->device_type & WACOM_DEVICETYPE_TOUCH) ||
(features->device_type & WACOM_DEVICETYPE_PAD))) {
error = -ENODEV;
- goto fail_hw_start;
+ goto fail_quirks;
}
if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR)
error = hid_hw_open(hdev);
if ((wacom_wac->features.type == INTUOSHT ||
- wacom_wac->features.type == INTUOSHT2) &&
+ wacom_wac->features.type == INTUOSHT2) &&
(wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)) {
- wacom_wac->shared->touch_input = wacom_wac->touch_input;
+ wacom_wac->shared->type = wacom_wac->features.type;
+ wacom_wac->shared->touch_input = wacom_wac->touch_input;
}
+ devres_close_group(&hdev->dev, wacom);
+
return 0;
-fail_hw_start:
+fail_quirks:
hid_hw_stop(hdev);
-fail_register_inputs:
- wacom_clean_inputs(wacom);
- wacom_destroy_battery(wacom);
-fail_battery:
- wacom_remove_shared_data(wacom);
-fail_shared_data:
-fail_parsed:
-fail_allocate_inputs:
- wacom_clean_inputs(wacom);
+fail:
+ wacom_release_resources(wacom);
return error;
}
static void wacom_wireless_work(struct work_struct *work)
{
- struct wacom *wacom = container_of(work, struct wacom, work);
+ struct wacom *wacom = container_of(work, struct wacom, wireless_work);
struct usb_device *usbdev = wacom->usbdev;
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct hid_device *hdev1, *hdev2;
@@ -1762,17 +2114,16 @@ static void wacom_wireless_work(struct work_struct *work)
hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
wacom1 = hid_get_drvdata(hdev1);
wacom_wac1 = &(wacom1->wacom_wac);
- wacom_clean_inputs(wacom1);
+ wacom_release_resources(wacom1);
/* Touch interface */
hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
wacom2 = hid_get_drvdata(hdev2);
wacom_wac2 = &(wacom2->wacom_wac);
- wacom_clean_inputs(wacom2);
+ wacom_release_resources(wacom2);
if (wacom_wac->pid == 0) {
hid_info(wacom->hdev, "wireless tablet disconnected\n");
- wacom_wac1->shared->type = 0;
} else {
const struct hid_device_id *id = wacom_ids;
@@ -1814,6 +2165,8 @@ static void wacom_wireless_work(struct work_struct *work)
goto fail;
}
+ strlcpy(wacom_wac->name, wacom_wac1->name,
+ sizeof(wacom_wac->name));
error = wacom_initialize_battery(wacom);
if (error)
goto fail;
@@ -1822,11 +2175,177 @@ static void wacom_wireless_work(struct work_struct *work)
return;
fail:
- wacom_clean_inputs(wacom1);
- wacom_clean_inputs(wacom2);
+ wacom_release_resources(wacom1);
+ wacom_release_resources(wacom2);
return;
}
+static void wacom_remote_destroy_one(struct wacom *wacom, unsigned int index)
+{
+ struct wacom_remote *remote = wacom->remote;
+ u32 serial = remote->remotes[index].serial;
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&remote->remote_lock, flags);
+ remote->remotes[index].registered = false;
+ spin_unlock_irqrestore(&remote->remote_lock, flags);
+
+ if (remote->remotes[index].battery.battery)
+ devres_release_group(&wacom->hdev->dev,
+ &remote->remotes[index].battery.bat_desc);
+
+ if (remote->remotes[index].group.name)
+ devres_release_group(&wacom->hdev->dev,
+ &remote->remotes[index]);
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ if (remote->remotes[i].serial == serial) {
+ remote->remotes[i].serial = 0;
+ remote->remotes[i].group.name = NULL;
+ remote->remotes[i].registered = false;
+ remote->remotes[i].battery.battery = NULL;
+ wacom->led.groups[i].select = WACOM_STATUS_UNKNOWN;
+ }
+ }
+}
+
+static int wacom_remote_create_one(struct wacom *wacom, u32 serial,
+ unsigned int index)
+{
+ struct wacom_remote *remote = wacom->remote;
+ struct device *dev = &wacom->hdev->dev;
+ int error, k;
+
+ /* 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 (remote->remotes[k].serial == serial)
+ break;
+ }
+
+ if (k < WACOM_MAX_REMOTES) {
+ remote->remotes[index].serial = serial;
+ return 0;
+ }
+
+ if (!devres_open_group(dev, &remote->remotes[index], GFP_KERNEL))
+ return -ENOMEM;
+
+ error = wacom_remote_create_attr_group(wacom, serial, index);
+ if (error)
+ goto fail;
+
+ remote->remotes[index].input = wacom_allocate_input(wacom);
+ if (!remote->remotes[index].input) {
+ error = -ENOMEM;
+ goto fail;
+ }
+ remote->remotes[index].input->uniq = remote->remotes[index].group.name;
+ remote->remotes[index].input->name = wacom->wacom_wac.pad_name;
+
+ if (!remote->remotes[index].input->name) {
+ error = -EINVAL;
+ goto fail;
+ }
+
+ error = wacom_setup_pad_input_capabilities(remote->remotes[index].input,
+ &wacom->wacom_wac);
+ if (error)
+ goto fail;
+
+ remote->remotes[index].serial = serial;
+
+ error = input_register_device(remote->remotes[index].input);
+ if (error)
+ goto fail;
+
+ error = wacom_led_groups_alloc_and_register_one(
+ &remote->remotes[index].input->dev,
+ wacom, index, 3, true);
+ if (error)
+ goto fail;
+
+ remote->remotes[index].registered = true;
+
+ devres_close_group(dev, &remote->remotes[index]);
+ return 0;
+
+fail:
+ devres_release_group(dev, &remote->remotes[index]);
+ remote->remotes[index].serial = 0;
+ return error;
+}
+
+static int wacom_remote_attach_battery(struct wacom *wacom, int index)
+{
+ struct wacom_remote *remote = wacom->remote;
+ int error;
+
+ if (!remote->remotes[index].registered)
+ return 0;
+
+ if (remote->remotes[index].battery.battery)
+ return 0;
+
+ if (wacom->led.groups[index].select == WACOM_STATUS_UNKNOWN)
+ return 0;
+
+ error = __wacom_initialize_battery(wacom,
+ &wacom->remote->remotes[index].battery);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static void wacom_remote_work(struct work_struct *work)
+{
+ struct wacom *wacom = container_of(work, struct wacom, remote_work);
+ struct wacom_remote *remote = wacom->remote;
+ struct wacom_remote_data data;
+ unsigned long flags;
+ unsigned int count;
+ u32 serial;
+ int i;
+
+ spin_lock_irqsave(&remote->remote_lock, flags);
+
+ count = kfifo_out(&remote->remote_fifo, &data, sizeof(data));
+
+ if (count != sizeof(data)) {
+ hid_err(wacom->hdev,
+ "workitem triggered without status available\n");
+ spin_unlock_irqrestore(&remote->remote_lock, flags);
+ return;
+ }
+
+ if (!kfifo_is_empty(&remote->remote_fifo))
+ wacom_schedule_work(&wacom->wacom_wac, WACOM_WORKER_REMOTE);
+
+ spin_unlock_irqrestore(&remote->remote_lock, flags);
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ serial = data.remote[i].serial;
+ if (data.remote[i].connected) {
+
+ if (remote->remotes[i].serial == serial) {
+ wacom_remote_attach_battery(wacom, i);
+ continue;
+ }
+
+ if (remote->remotes[i].serial)
+ wacom_remote_destroy_one(wacom, i);
+
+ wacom_remote_create_one(wacom, serial, i);
+
+ } else if (remote->remotes[i].serial) {
+ wacom_remote_destroy_one(wacom, i);
+ }
+ }
+}
+
static int wacom_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
@@ -1845,7 +2364,7 @@ static int wacom_probe(struct hid_device *hdev,
/* hid-core sets this quirk for the boot interface */
hdev->quirks &= ~HID_QUIRK_NOGET;
- wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
+ wacom = devm_kzalloc(&hdev->dev, sizeof(struct wacom), GFP_KERNEL);
if (!wacom)
return -ENOMEM;
@@ -1858,7 +2377,7 @@ static int wacom_probe(struct hid_device *hdev,
if (features->check_for_hid_type && features->hid_type != hdev->type) {
error = -ENODEV;
- goto fail_type;
+ goto fail;
}
wacom_wac->hid_data.inputmode = -1;
@@ -1867,18 +2386,20 @@ static int wacom_probe(struct hid_device *hdev,
wacom->usbdev = dev;
wacom->intf = intf;
mutex_init(&wacom->lock);
- INIT_WORK(&wacom->work, wacom_wireless_work);
+ INIT_WORK(&wacom->wireless_work, wacom_wireless_work);
+ INIT_WORK(&wacom->battery_work, wacom_battery_work);
+ INIT_WORK(&wacom->remote_work, wacom_remote_work);
/* ask for the report descriptor to be loaded by HID */
error = hid_parse(hdev);
if (error) {
hid_err(hdev, "parse failed\n");
- goto fail_parse;
+ goto fail;
}
error = wacom_parse_and_register(wacom, false);
if (error)
- goto fail_parse;
+ goto fail;
if (hdev->bus == BUS_BLUETOOTH) {
error = device_create_file(&hdev->dev, &dev_attr_speed);
@@ -1890,9 +2411,7 @@ static int wacom_probe(struct hid_device *hdev,
return 0;
-fail_type:
-fail_parse:
- kfree(wacom);
+fail:
hid_set_drvdata(hdev, NULL);
return error;
}
@@ -1908,15 +2427,13 @@ static void wacom_remove(struct hid_device *hdev)
hid_hw_stop(hdev);
- cancel_work_sync(&wacom->work);
- wacom_clean_inputs(wacom);
+ cancel_work_sync(&wacom->wireless_work);
+ cancel_work_sync(&wacom->battery_work);
+ cancel_work_sync(&wacom->remote_work);
if (hdev->bus == BUS_BLUETOOTH)
device_remove_file(&hdev->dev, &dev_attr_speed);
- wacom_destroy_battery(wacom);
- wacom_remove_shared_data(wacom);
hid_set_drvdata(hdev, NULL);
- kfree(wacom);
}
#ifdef CONFIG_PM
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 1eae13cdc502..1cb79925730d 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -34,6 +34,10 @@
*/
#define WACOM_CONTACT_AREA_SCALE 2607
+static bool touch_arbitration = 1;
+module_param(touch_arbitration, bool, 0644);
+MODULE_PARM_DESC(touch_arbitration, " on (Y) off (N)");
+
static void wacom_report_numbered_buttons(struct input_dev *input_dev,
int button_count, int mask);
@@ -48,25 +52,34 @@ static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 };
*/
static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 };
+static void __wacom_notify_battery(struct wacom_battery *battery,
+ int bat_capacity, bool bat_charging,
+ bool bat_connected, bool ps_connected)
+{
+ bool changed = battery->battery_capacity != bat_capacity ||
+ battery->bat_charging != bat_charging ||
+ battery->bat_connected != bat_connected ||
+ battery->ps_connected != ps_connected;
+
+ if (changed) {
+ battery->battery_capacity = bat_capacity;
+ battery->bat_charging = bat_charging;
+ battery->bat_connected = bat_connected;
+ battery->ps_connected = ps_connected;
+
+ if (battery->battery)
+ power_supply_changed(battery->battery);
+ }
+}
+
static void wacom_notify_battery(struct wacom_wac *wacom_wac,
int bat_capacity, bool bat_charging, bool bat_connected,
bool ps_connected)
{
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
- bool changed = wacom_wac->battery_capacity != bat_capacity ||
- wacom_wac->bat_charging != bat_charging ||
- wacom_wac->bat_connected != bat_connected ||
- wacom_wac->ps_connected != ps_connected;
- if (changed) {
- wacom_wac->battery_capacity = bat_capacity;
- wacom_wac->bat_charging = bat_charging;
- wacom_wac->bat_connected = bat_connected;
- wacom_wac->ps_connected = ps_connected;
-
- if (wacom->battery)
- power_supply_changed(wacom->battery);
- }
+ __wacom_notify_battery(&wacom->battery, bat_capacity, bat_charging,
+ bat_connected, ps_connected);
}
static int wacom_penpartner_irq(struct wacom_wac *wacom)
@@ -751,22 +764,37 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
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 input_dev *input;
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
- struct wacom_features *features = &wacom_wac->features;
+ struct wacom_remote *remote = wacom->remote;
int bat_charging, bat_percent, touch_ring_mode;
__u32 serial;
- int i;
+ int i, index = -1;
+ unsigned long flags;
if (data[0] != WACOM_REPORT_REMOTE) {
- dev_dbg(input->dev.parent,
- "%s: received unknown report #%d", __func__, data[0]);
+ hid_dbg(wacom->hdev, "%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;
+ spin_lock_irqsave(&remote->remote_lock, flags);
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ if (remote->remotes[i].serial == serial) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index < 0 || !remote->remotes[index].registered)
+ goto out;
+
+ input = remote->remotes[index].input;
+
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));
@@ -803,73 +831,69 @@ static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len)
input_event(input, EV_MSC, MSC_SERIAL, serial);
+ input_sync(input);
+
/*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);
+ if (remote->remotes[i].serial == serial)
+ wacom->led.groups[i].select = touch_ring_mode;
}
- wacom_notify_battery(wacom_wac, bat_percent, bat_charging, 1,
- bat_charging);
+ __wacom_notify_battery(&remote->remotes[index].battery, bat_percent,
+ bat_charging, 1, bat_charging);
- return 1;
+out:
+ spin_unlock_irqrestore(&remote->remote_lock, flags);
+ return 0;
}
-static int wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len)
+static void 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;
+ struct wacom_remote *remote = wacom->remote;
+ struct wacom_remote_data remote_data;
+ unsigned long flags;
+ int i, ret;
if (data[0] != WACOM_REPORT_DEVICE_LIST)
- return 0;
+ return;
+
+ memset(&remote_data, 0, sizeof(struct wacom_remote_data));
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;
+ remote_data.remote[i].serial = serial;
+ remote_data.remote[i].connected = connected;
+ }
- if (wacom_wac->serial[i] == serial)
- continue;
+ spin_lock_irqsave(&remote->remote_lock, flags);
- if (wacom_wac->serial[i]) {
- wacom_remote_destroy_attr_group(wacom,
- wacom_wac->serial[i]);
- }
+ ret = kfifo_in(&remote->remote_fifo, &remote_data, sizeof(remote_data));
+ if (ret != sizeof(remote_data)) {
+ spin_unlock_irqrestore(&remote->remote_lock, flags);
+ hid_err(wacom->hdev, "Can't queue Remote status event.\n");
+ return;
+ }
- /* 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;
- }
+ spin_unlock_irqrestore(&remote->remote_lock, flags);
- if (k < WACOM_MAX_REMOTES) {
- wacom_wac->serial[i] = serial;
- continue;
- }
- wacom_remote_create_attr_group(wacom, serial, i);
+ wacom_schedule_work(wacom_wac, WACOM_WORKER_REMOTE);
+}
- } else if (wacom_wac->serial[i]) {
- wacom_remote_destroy_attr_group(wacom,
- wacom_wac->serial[i]);
- }
- }
+static inline bool report_touch_events(struct wacom_wac *wacom)
+{
+ return (touch_arbitration ? !wacom->shared->stylus_in_proximity : 1);
+}
- return 0;
+static inline bool delay_pen_events(struct wacom_wac *wacom)
+{
+ return (wacom->shared->touch_down && touch_arbitration);
}
static int wacom_intuos_general(struct wacom_wac *wacom)
@@ -885,7 +909,7 @@ static int wacom_intuos_general(struct wacom_wac *wacom)
data[0] != WACOM_REPORT_INTUOS_PEN)
return 0;
- if (wacom->shared->touch_down)
+ if (delay_pen_events(wacom))
return 1;
/* don't report events if we don't know the tool ID */
@@ -1145,7 +1169,7 @@ static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
if (touch_max == 1)
return test_bit(BTN_TOUCH, input->key) &&
- !wacom->shared->stylus_in_proximity;
+ report_touch_events(wacom);
for (i = 0; i < input->mt->num_slots; i++) {
struct input_mt_slot *ps = &input->mt->slots[i];
@@ -1186,7 +1210,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
for (i = 0; i < contacts_to_send; i++) {
int offset = (byte_per_packet * i) + 1;
- bool touch = (data[offset] & 0x1) && !wacom->shared->stylus_in_proximity;
+ bool touch = (data[offset] & 0x1) && report_touch_events(wacom);
int slot = input_mt_get_slot_by_key(input, data[offset + 1]);
if (slot < 0)
@@ -1250,7 +1274,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
for (i = 0; i < contacts_to_send; i++) {
int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3;
- bool touch = (data[offset] & 0x1) && !wacom->shared->stylus_in_proximity;
+ bool touch = (data[offset] & 0x1) && report_touch_events(wacom);
int id = get_unaligned_le16(&data[offset + 1]);
int slot = input_mt_get_slot_by_key(input, id);
@@ -1284,7 +1308,7 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
for (i = 0; i < 2; i++) {
int p = data[1] & (1 << i);
- bool touch = p && !wacom->shared->stylus_in_proximity;
+ bool touch = p && report_touch_events(wacom);
input_mt_slot(input, i);
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
@@ -1308,7 +1332,7 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
{
unsigned char *data = wacom->data;
struct input_dev *input = wacom->touch_input;
- bool prox = !wacom->shared->stylus_in_proximity;
+ bool prox = report_touch_events(wacom);
int x = 0, y = 0;
if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG)
@@ -1353,8 +1377,10 @@ static int wacom_tpc_pen(struct wacom_wac *wacom)
/* keep pen state for touch events */
wacom->shared->stylus_in_proximity = prox;
- /* send pen events only when touch is up or forced out */
- if (!wacom->shared->touch_down) {
+ /* send pen events only when touch is up or forced out
+ * or touch arbitration is off
+ */
+ if (!delay_pen_events(wacom)) {
input_report_key(input, BTN_STYLUS, data[1] & 0x02);
input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
@@ -1496,8 +1522,10 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field,
return 0;
}
- /* send pen events only when touch is up or forced out */
- if (!usage->type || wacom_wac->shared->touch_down)
+ /* send pen events only when touch is up or forced out
+ * or touch arbitration is off
+ */
+ if (!usage->type || delay_pen_events(wacom_wac))
return 0;
input_event(input, usage->type, usage->code, value);
@@ -1527,8 +1555,7 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
/* keep pen state for touch events */
wacom_wac->shared->stylus_in_proximity = prox;
- /* send pen events only when touch is up or forced out */
- if (!wacom_wac->shared->touch_down) {
+ if (!delay_pen_events(wacom_wac)) {
input_report_key(input, BTN_TOUCH,
wacom_wac->hid_data.tipswitch);
input_report_key(input, wacom_wac->tool[0], prox);
@@ -1544,13 +1571,11 @@ 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(input, usage, field, EV_ABS, ABS_X, 4);
else
@@ -1558,7 +1583,6 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
ABS_MT_POSITION_X, 4);
break;
case HID_GD_Y:
- features->last_slot_field = usage->hid;
if (touch_max == 1)
wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4);
else
@@ -1567,22 +1591,11 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
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;
- case HID_DG_INRANGE:
- features->last_slot_field = usage->hid;
- break;
- case HID_DG_INVERT:
- features->last_slot_field = usage->hid;
- break;
case HID_DG_TIPSWITCH:
- features->last_slot_field = usage->hid;
wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0);
break;
case HID_DG_CONTACTCOUNT:
@@ -1599,7 +1612,7 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
struct hid_data *hid_data = &wacom_wac->hid_data;
bool mt = wacom_wac->features.touch_max > 1;
bool prox = hid_data->tipswitch &&
- !wacom_wac->shared->stylus_in_proximity;
+ report_touch_events(wacom_wac);
wacom_wac->hid_data.num_received++;
if (wacom_wac->hid_data.num_received > wacom_wac->hid_data.num_expected)
@@ -1660,7 +1673,7 @@ 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)
+ if (usage->hid == wacom_wac->hid_data.last_slot_field)
wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input);
}
@@ -1673,31 +1686,35 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev,
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct hid_data* hid_data = &wacom_wac->hid_data;
+ int i;
- 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;
- }
+ for (i = 0; i < report->maxfield; i++) {
+ struct hid_field *field = report->field[i];
+ int j;
+
+ for (j = 0; j < field->maxusage; j++) {
+ struct hid_usage *usage = &field->usage[j];
+
+ switch (usage->hid) {
+ case HID_GD_X:
+ case HID_GD_Y:
+ case HID_DG_WIDTH:
+ case HID_DG_HEIGHT:
+ case HID_DG_CONTACTID:
+ case HID_DG_INRANGE:
+ case HID_DG_INVERT:
+ case HID_DG_TIPSWITCH:
+ hid_data->last_slot_field = usage->hid;
+ break;
+ case HID_DG_CONTACTCOUNT:
+ hid_data->cc_report = report->id;
+ hid_data->cc_index = i;
+ hid_data->cc_value_index = j;
+ break;
}
}
}
+
if (hid_data->cc_report != 0 &&
hid_data->cc_index >= 0) {
struct hid_field *field = report->field[hid_data->cc_index];
@@ -1740,10 +1757,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 wacom_features *features = &wacom_wac->features;
/* currently, only direct devices have proper hid report descriptors */
- __set_bit(INPUT_PROP_DIRECT, wacom_wac->pen_input->propbit);
- __set_bit(INPUT_PROP_DIRECT, wacom_wac->touch_input->propbit);
+ features->device_type |= WACOM_DEVICETYPE_DIRECT;
if (WACOM_PEN_FIELD(field))
return wacom_wac_pen_usage_mapping(hdev, field, usage);
@@ -1825,15 +1842,8 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
for (i = 0; i < 2; i++) {
int offset = (data[1] & 0x80) ? (8 * i) : (9 * i);
- bool touch = data[offset + 3] & 0x80;
-
- /*
- * Touch events need to be disabled while stylus is
- * in proximity because user's hand is resting on touchpad
- * and sending unwanted events. User expects tablet buttons
- * to continue working though.
- */
- touch = touch && !wacom->shared->stylus_in_proximity;
+ bool touch = report_touch_events(wacom)
+ && (data[offset + 3] & 0x80);
input_mt_slot(input, i);
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
@@ -1870,7 +1880,7 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
if (slot < 0)
return;
- touch = touch && !wacom->shared->stylus_in_proximity;
+ touch = touch && report_touch_events(wacom);
input_mt_slot(input, slot);
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
@@ -1942,7 +1952,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
}
/* only update touch if we actually have a touchpad and touch data changed */
- if (wacom->touch_registered && touch_changed) {
+ if (wacom->touch_input && touch_changed) {
input_mt_sync_frame(wacom->touch_input);
wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
}
@@ -1983,7 +1993,7 @@ static int wacom_bpt_pen(struct wacom_wac *wacom)
}
wacom->shared->stylus_in_proximity = prox;
- if (wacom->shared->touch_down)
+ if (delay_pen_events(wacom))
return 0;
if (prox) {
@@ -2077,7 +2087,7 @@ static int wacom_bamboo_pad_touch_event(struct wacom_wac *wacom,
for (id = 0; id < wacom->features.touch_max; id++) {
valid = !!(prefix & BIT(id)) &&
- !wacom->shared->stylus_in_proximity;
+ report_touch_events(wacom);
input_mt_slot(input, id);
input_mt_report_slot_state(input, MT_TOOL_FINGER, valid);
@@ -2099,8 +2109,7 @@ static int wacom_bamboo_pad_touch_event(struct wacom_wac *wacom,
input_report_key(input, BTN_RIGHT, prefix & 0x80);
/* keep touch state for pen event */
- wacom->shared->touch_down = !!prefix &&
- !wacom->shared->stylus_in_proximity;
+ wacom->shared->touch_down = !!prefix && report_touch_events(wacom);
return 1;
}
@@ -2149,16 +2158,15 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
charging = !!(data[5] & 0x80);
if (wacom->pid != pid) {
wacom->pid = pid;
- wacom_schedule_work(wacom);
+ wacom_schedule_work(wacom, WACOM_WORKER_WIRELESS);
}
- if (wacom->shared->type)
- wacom_notify_battery(wacom, battery, charging, 1, 0);
+ wacom_notify_battery(wacom, battery, charging, 1, 0);
} else if (wacom->pid != 0) {
/* disconnected while previously connected */
wacom->pid = 0;
- wacom_schedule_work(wacom);
+ wacom_schedule_work(wacom, WACOM_WORKER_WIRELESS);
wacom_notify_battery(wacom, 0, 0, 0, 0);
}
@@ -2190,18 +2198,16 @@ static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len)
wacom_notify_battery(wacom_wac, battery, charging,
battery || charging, 1);
- if (!wacom->battery &&
+ if (!wacom->battery.battery &&
!(features->quirks & WACOM_QUIRK_BATTERY)) {
features->quirks |= WACOM_QUIRK_BATTERY;
- INIT_WORK(&wacom->work, wacom_battery_work);
- wacom_schedule_work(wacom_wac);
+ wacom_schedule_work(wacom_wac, WACOM_WORKER_BATTERY);
}
}
else if ((features->quirks & WACOM_QUIRK_BATTERY) &&
- wacom->battery) {
+ wacom->battery.battery) {
features->quirks &= ~WACOM_QUIRK_BATTERY;
- INIT_WORK(&wacom->work, wacom_battery_work);
- wacom_schedule_work(wacom_wac);
+ wacom_schedule_work(wacom_wac, WACOM_WORKER_BATTERY);
wacom_notify_battery(wacom_wac, 0, 0, 0, 0);
}
return 0;
@@ -2312,8 +2318,9 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
break;
case REMOTE:
+ sync = false;
if (wacom_wac->data[0] == WACOM_REPORT_DEVICE_LIST)
- sync = wacom_remote_status_irq(wacom_wac, len);
+ wacom_remote_status_irq(wacom_wac, len);
else
sync = wacom_remote_irq(wacom_wac, len);
break;
@@ -2451,6 +2458,33 @@ void wacom_setup_device_quirks(struct wacom *wacom)
if (features->type == REMOTE)
features->device_type = WACOM_DEVICETYPE_PAD;
+ switch (features->type) {
+ case PL:
+ case DTU:
+ case DTUS:
+ case DTUSX:
+ case WACOM_21UX2:
+ case WACOM_22HD:
+ case DTK:
+ case WACOM_24HD:
+ case WACOM_27QHD:
+ case CINTIQ_HYBRID:
+ case CINTIQ_COMPANION_2:
+ case CINTIQ:
+ case WACOM_BEE:
+ case WACOM_13HD:
+ case WACOM_24HDT:
+ case WACOM_27QHDT:
+ case TABLETPC:
+ case TABLETPCE:
+ case TABLETPC2FG:
+ case MTSCREEN:
+ case MTTPC:
+ case MTTPC_B:
+ features->device_type |= WACOM_DEVICETYPE_DIRECT;
+ break;
+ }
+
if (wacom->hdev->bus == BUS_BLUETOOTH)
features->quirks |= WACOM_QUIRK_BATTERY;
@@ -2469,6 +2503,9 @@ void wacom_setup_device_quirks(struct wacom *wacom)
features->quirks |= WACOM_QUIRK_BATTERY;
}
}
+
+ if (features->type == REMOTE)
+ features->device_type |= WACOM_DEVICETYPE_WL_MONITOR;
}
int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
@@ -2481,6 +2518,11 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
if (!(features->device_type & WACOM_DEVICETYPE_PEN))
return -ENODEV;
+ if (features->device_type & WACOM_DEVICETYPE_DIRECT)
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+ else
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
if (features->type == HID_GENERIC)
/* setup has already been done */
return 0;
@@ -2499,7 +2541,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
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:
__clear_bit(ABS_MISC, input_dev->absbit);
@@ -2523,8 +2564,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
__set_bit(BTN_STYLUS, input_dev->keybit);
__set_bit(BTN_STYLUS2, input_dev->keybit);
-
- __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
break;
case WACOM_27QHD:
@@ -2539,7 +2578,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
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);
wacom_setup_cintiq(wacom_wac);
break;
@@ -2555,8 +2593,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
/* fall through */
case INTUOS:
- __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-
wacom_setup_intuos(wacom_wac);
break;
@@ -2566,8 +2602,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
case INTUOSPL:
case INTUOS5S:
case INTUOSPS:
- __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-
input_set_abs_params(input_dev, ABS_DISTANCE, 0,
features->distance_max,
features->distance_fuzz, 0);
@@ -2597,8 +2631,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
__set_bit(BTN_STYLUS, input_dev->keybit);
__set_bit(BTN_STYLUS2, input_dev->keybit);
-
- __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
break;
case PTU:
@@ -2609,16 +2641,12 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
__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(INPUT_PROP_POINTER, input_dev->propbit);
break;
case INTUOSHT:
case BAMBOO_PT:
case BAMBOO_PEN:
case INTUOSHT2:
- __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-
if (features->type == INTUOSHT2) {
wacom_setup_basic_pro_pen(wacom_wac);
} else {
@@ -2649,6 +2677,11 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
if (!(features->device_type & WACOM_DEVICETYPE_TOUCH))
return -ENODEV;
+ if (features->device_type & WACOM_DEVICETYPE_DIRECT)
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+ else
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
if (features->type == HID_GENERIC)
/* setup has already been done */
return 0;
@@ -2683,8 +2716,6 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
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);
@@ -2707,7 +2738,6 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
case TABLETPC:
case TABLETPCE:
- __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
break;
case INTUOSHT:
@@ -2752,11 +2782,105 @@ static void wacom_setup_numbered_buttons(struct input_dev *input_dev,
__set_bit(BTN_BASE + (i-16), input_dev->keybit);
}
+static void wacom_24hd_update_leds(struct wacom *wacom, int mask, int group)
+{
+ struct wacom_led *led;
+ int i;
+ bool updated = false;
+
+ /*
+ * 24HD has LED group 1 to the left and LED group 0 to the right.
+ * So group 0 matches the second half of the buttons and thus the mask
+ * needs to be shifted.
+ */
+ if (group == 0)
+ mask >>= 8;
+
+ for (i = 0; i < 3; i++) {
+ led = wacom_led_find(wacom, group, i);
+ if (!led) {
+ hid_err(wacom->hdev, "can't find LED %d in group %d\n",
+ i, group);
+ continue;
+ }
+ if (!updated && mask & BIT(i)) {
+ led->held = true;
+ led_trigger_event(&led->trigger, LED_FULL);
+ } else {
+ led->held = false;
+ }
+ }
+}
+
+static bool wacom_is_led_toggled(struct wacom *wacom, int button_count,
+ int mask, int group)
+{
+ int button_per_group;
+
+ /*
+ * 21UX2 has LED group 1 to the left and LED group 0
+ * to the right. We need to reverse the group to match this
+ * historical behavior.
+ */
+ if (wacom->wacom_wac.features.type == WACOM_21UX2)
+ group = 1 - group;
+
+ button_per_group = button_count/wacom->led.count;
+
+ return mask & (1 << (group * button_per_group));
+}
+
+static void wacom_update_led(struct wacom *wacom, int button_count, int mask,
+ int group)
+{
+ struct wacom_led *led, *next_led;
+ int cur;
+ bool pressed;
+
+ if (wacom->wacom_wac.features.type == WACOM_24HD)
+ return wacom_24hd_update_leds(wacom, mask, group);
+
+ pressed = wacom_is_led_toggled(wacom, button_count, mask, group);
+ cur = wacom->led.groups[group].select;
+
+ led = wacom_led_find(wacom, group, cur);
+ if (!led) {
+ hid_err(wacom->hdev, "can't find current LED %d in group %d\n",
+ cur, group);
+ return;
+ }
+
+ if (!pressed) {
+ led->held = false;
+ return;
+ }
+
+ if (led->held && pressed)
+ return;
+
+ next_led = wacom_led_next(wacom, led);
+ if (!next_led) {
+ hid_err(wacom->hdev, "can't find next LED in group %d\n",
+ group);
+ return;
+ }
+ if (next_led == led)
+ return;
+
+ next_led->held = true;
+ led_trigger_event(&next_led->trigger,
+ wacom_leds_brightness_get(next_led));
+}
+
static void wacom_report_numbered_buttons(struct input_dev *input_dev,
int button_count, int mask)
{
+ struct wacom *wacom = input_get_drvdata(input_dev);
int i;
+ for (i = 0; i < wacom->led.count; i++)
+ wacom_update_led(wacom, button_count, mask, i);
+
for (i = 0; i < button_count && i < 10; i++)
input_report_key(input_dev, BTN_0 + i, mask & (1 << i));
for (i = 10; i < button_count && i < 16; i++)
@@ -2773,6 +2897,9 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
if (!(features->device_type & WACOM_DEVICETYPE_PAD))
return -ENODEV;
+ if (features->type == REMOTE && input_dev == wacom_wac->pad_input)
+ return -ENODEV;
+
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
/* kept for making legacy xf86-input-wacom working with the wheels */
@@ -3403,7 +3530,7 @@ static const struct wacom_features wacom_features_0x343 =
WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
static const struct wacom_features wacom_features_HID_ANY_ID =
- { "Wacom HID", .type = HID_GENERIC };
+ { "Wacom HID", .type = HID_GENERIC, .oVid = HID_ANY_ID, .oPid = HID_ANY_ID };
#define USB_DEVICE_WACOM(prod) \
HID_DEVICE(BUS_USB, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index 53d16537fd2a..324c40b0c119 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -82,6 +82,7 @@
#define WACOM_DEVICETYPE_TOUCH 0x0002
#define WACOM_DEVICETYPE_PAD 0x0004
#define WACOM_DEVICETYPE_WL_MONITOR 0x0008
+#define WACOM_DEVICETYPE_DIRECT 0x0010
#define WACOM_VENDORDEFINED_PEN 0xff0d0001
#define WACOM_G9_PAGE 0xff090000
@@ -185,7 +186,6 @@ struct wacom_features {
int pktlen;
bool check_for_hid_type;
int hid_type;
- int last_slot_field;
};
struct wacom_shared {
@@ -214,35 +214,35 @@ struct hid_data {
int cc_report;
int cc_index;
int cc_value_index;
+ int last_slot_field;
int num_expected;
int num_received;
};
+struct wacom_remote_data {
+ struct {
+ u32 serial;
+ bool connected;
+ } remote[WACOM_MAX_REMOTES];
+};
+
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[5];
+ __u32 serial[2];
bool reporting_data;
struct wacom_features features;
struct wacom_shared *shared;
struct input_dev *pen_input;
struct input_dev *touch_input;
struct input_dev *pad_input;
- bool pen_registered;
- bool touch_registered;
- bool pad_registered;
int pid;
- int battery_capacity;
int num_contacts_left;
- int bat_charging;
- int bat_connected;
- int ps_connected;
u8 bt_features;
u8 bt_high_speed;
int mode_report;