From 8ffd341c4b66f4a2c0dea93dac9b7141870ea63c Mon Sep 17 00:00:00 2001 From: Harald Brinkmann Date: Thu, 3 Sep 2015 17:49:26 +0200 Subject: [PATCH 01/30] HID: saitek: mode button quirk for Mad Catz R.A.T.5 This patch enables the Saitek HID quirk for the mode button of the Mad Catz R.A.T.5 gaming mouse by adding the same quirk that's been neeeded for preivous versions. [jikos@kernel.org: update changelog a little bit] Signed-off-by: Harald Brinkmann Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 5 ++--- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-saitek.c | 2 ++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 6ab51ae3c39d..434ffa566340 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -672,9 +672,8 @@ config HID_SAITEK Supported devices: - PS1000 Dual Analog Pad - - R.A.T.9 Gaming Mouse - - R.A.T.7 Gaming Mouse - - M.M.O.7 Gaming Mouse + - Saitek R.A.T.7, R.A.T.9, M.M.O.7 Gaming Mice + - Mad Catz R.A.T.5, R.A.T.9 Gaming Mice config HID_SAMSUNG tristate "Samsung InfraRed remote control or keyboards" diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bcd914a63af2..31fc6427cac3 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1981,6 +1981,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5) }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9) }, #endif { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index f769208276ae..99c13cd2bf07 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -646,6 +646,7 @@ #define USB_VENDOR_ID_MADCATZ 0x0738 #define USB_DEVICE_ID_MADCATZ_BEATPAD 0x4540 +#define USB_DEVICE_ID_MADCATZ_RAT5 0x1705 #define USB_DEVICE_ID_MADCATZ_RAT9 0x1709 #define USB_VENDOR_ID_MCC 0x09db diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c index a014f21275d8..2f84b26f1167 100644 --- a/drivers/hid/hid-saitek.c +++ b/drivers/hid/hid-saitek.c @@ -177,6 +177,8 @@ static int saitek_event(struct hid_device *hdev, struct hid_field *field, static const struct hid_device_id saitek_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000), .driver_data = SAITEK_FIX_PS1000 }, + { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5), + .driver_data = SAITEK_RELEASE_MODE_RAT7 }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD), .driver_data = SAITEK_RELEASE_MODE_RAT7 }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7), From 9fe8eccad4425fd024516fac2ebc0a39610441a0 Mon Sep 17 00:00:00 2001 From: Fernando D S Lima Date: Thu, 3 Sep 2015 12:56:31 -0300 Subject: [PATCH 02/30] HID: sensor-hub: Fixup for Lenovo ThinkPad Helix 2 sensor hub report There is an error in the report descriptor of the Thinkpad Helix 2 where logical minimum value (557376) is greater than logical maximum (491200) for all of the magnetic flux axis data fields. This error results in a report descriptor parsing failure that causes the sensors attached to the hub not to be detected. dmesg excerpt: [ 19.866905] drivers/hid/hid-core.c: logical range invalid 0x88140 0x77ec0 [ 19.866914] hid-sensor-hub 0018:2047:0855.0007: item 0 1 0 8 parsing failed [ 19.866926] hid-sensor-hub 0018:2047:0855.0007: parse failed [ 19.866933] hid-sensor-hub: probe of 0018:2047:0855.0007 failed with error -22 Add a report fixup to change magnetic flux logical minimums to -557376 for the parsing to succeed and the sensors to get detected. After applying the fix the sensors get detected, with corresponding drivers (hid-accel-3d,hid-gyro-3d,etc) loaded, and its possible to read their values. Signed-off-by: Fernando D S Lima Reviewed-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/hid-sensor-hub.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index a76eb2a0a987..51d51d7b2dad 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -593,6 +593,20 @@ static __u8 *sensor_hub_report_fixup(struct hid_device *hdev, __u8 *rdesc, } } + /* Checks if the report descriptor of Thinkpad Helix 2 has a logical + * minimum for magnetic flux axis greater than the maximum */ + if (hdev->product == USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA && + *rsize == 2558 && rdesc[913] == 0x17 && rdesc[914] == 0x40 && + rdesc[915] == 0x81 && rdesc[916] == 0x08 && + rdesc[917] == 0x00 && rdesc[918] == 0x27 && + rdesc[921] == 0x07 && rdesc[922] == 0x00) { + /* Sets negative logical minimum for mag x, y and z */ + rdesc[914] = rdesc[935] = rdesc[956] = 0xc0; + rdesc[915] = rdesc[936] = rdesc[957] = 0x7e; + rdesc[916] = rdesc[937] = rdesc[958] = 0xf7; + rdesc[917] = rdesc[938] = rdesc[959] = 0xff; + } + return rdesc; } From 580a7e82f08c38837dbe08374d649329d3de698b Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 3 Sep 2015 09:08:29 -0400 Subject: [PATCH 03/30] HID: logitech-hidpp: split HIDPP_QUIRK_DELAYED_INIT in two HIDPP_QUIRK_DELAYED_INIT means two things currently: - we want to delay the initialization process - we do not want hid to create an input device based on the report descriptor. This should actually be 2 different quirks so we can have special connect events while still having HID creating the input for us. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 484196459305..81d1bc0feebb 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -43,8 +43,12 @@ MODULE_PARM_DESC(disable_raw_mode, #define HIDPP_QUIRK_CLASS_M560 BIT(1) /* bits 2..20 are reserved for classes */ -#define HIDPP_QUIRK_DELAYED_INIT BIT(21) +#define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22) +#define HIDPP_QUIRK_NO_HIDINPUT BIT(23) + +#define HIDPP_QUIRK_DELAYED_INIT (HIDPP_QUIRK_NO_HIDINPUT | \ + HIDPP_QUIRK_CONNECT_EVENTS) /* * There are two hidpp protocols in use, the first version hidpp10 is known @@ -1203,7 +1207,7 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, if (unlikely(hidpp_report_is_connect_event(report))) { atomic_set(&hidpp->connected, !(report->rap.params[0] & (1 << 6))); - if ((hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) && + if ((hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) && (schedule_work(&hidpp->work) == 0)) dbg_hid("%s: connect event already queued\n", __func__); return 1; @@ -1333,18 +1337,21 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) if (!connected || hidpp->delayed_input) return; + /* the device is already connected, we can ask for its name and + * protocol */ if (!hidpp->protocol_major) { ret = !hidpp_is_connected(hidpp); if (ret) { hid_err(hdev, "Can not get the protocol version.\n"); return; } + hid_info(hdev, "HID++ %u.%u device connected.\n", + hidpp->protocol_major, hidpp->protocol_minor); } - /* the device is already connected, we can ask for its name and - * protocol */ - hid_info(hdev, "HID++ %u.%u device connected.\n", - hidpp->protocol_major, hidpp->protocol_minor); + if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)) + /* if HID created the input nodes for us, we can stop now */ + return; if (!hidpp->name || hidpp->name == hdev->name) { name = hidpp_get_device_name(hidpp); @@ -1397,7 +1404,8 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) if (disable_raw_mode) { hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP; - hidpp->quirks &= ~HIDPP_QUIRK_DELAYED_INIT; + hidpp->quirks &= ~HIDPP_QUIRK_CONNECT_EVENTS; + hidpp->quirks &= ~HIDPP_QUIRK_NO_HIDINPUT; } if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { @@ -1448,7 +1456,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) /* Block incoming packets */ hid_device_io_stop(hdev); - if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) + if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) connect_mask &= ~HID_CONNECT_HIDINPUT; ret = hid_hw_start(hdev, connect_mask); @@ -1457,7 +1465,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) goto hid_hw_start_fail; } - if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) { + if (hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) { /* Allow incoming packets */ hid_device_io_start(hdev); From 90cdd986335eddb7cac6e456f7c48c1ce76fd095 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 3 Sep 2015 09:08:30 -0400 Subject: [PATCH 04/30] HID: logitech-hidpp: add support to disable tap-to-click on the K400 The Logitech K400 keyboard has an embedded touchpad which is seen as a mouse from the OS point of view. There is a hardware shortcut to disable tap-to-click but the setting is not remembered accross reset, annoying some users. We can toggle this feature from the host by using the feature 0x6010: Touchpad FW items Reported-by: BALATON Zoltan Tested-by: BALATON Zoltan Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 133 +++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 81d1bc0feebb..452e5d5d6be7 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -33,6 +33,11 @@ module_param(disable_raw_mode, bool, 0644); MODULE_PARM_DESC(disable_raw_mode, "Disable Raw mode reporting for touchpads and keep firmware gestures."); +static bool disable_tap_to_click; +module_param(disable_tap_to_click, bool, 0644); +MODULE_PARM_DESC(disable_tap_to_click, + "Disable Tap-To-Click mode reporting for touchpads (only on the K400 currently)."); + #define REPORT_ID_HIDPP_SHORT 0x10 #define REPORT_ID_HIDPP_LONG 0x11 @@ -41,6 +46,7 @@ MODULE_PARM_DESC(disable_raw_mode, #define HIDPP_QUIRK_CLASS_WTP BIT(0) #define HIDPP_QUIRK_CLASS_M560 BIT(1) +#define HIDPP_QUIRK_CLASS_K400 BIT(2) /* bits 2..20 are reserved for classes */ #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) @@ -556,6 +562,52 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp) return name; } +/* -------------------------------------------------------------------------- */ +/* 0x6010: Touchpad FW items */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_TOUCHPAD_FW_ITEMS 0x6010 + +#define CMD_TOUCHPAD_FW_ITEMS_SET 0x10 + +struct hidpp_touchpad_fw_items { + uint8_t presence; + uint8_t desired_state; + uint8_t state; + uint8_t persistent; +}; + +/** + * send a set state command to the device by reading the current items->state + * field. items is then filled with the current state. + */ +static int hidpp_touchpad_fw_items_set(struct hidpp_device *hidpp, + u8 feature_index, + struct hidpp_touchpad_fw_items *items) +{ + struct hidpp_report response; + int ret; + u8 *params = (u8 *)response.fap.params; + + ret = hidpp_send_fap_command_sync(hidpp, feature_index, + CMD_TOUCHPAD_FW_ITEMS_SET, &items->state, 1, &response); + + if (ret > 0) { + hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", + __func__, ret); + return -EPROTO; + } + if (ret) + return ret; + + items->presence = params[0]; + items->desired_state = params[1]; + items->state = params[2]; + items->persistent = params[3]; + + return 0; +} + /* -------------------------------------------------------------------------- */ /* 0x6100: TouchPadRawXY */ /* -------------------------------------------------------------------------- */ @@ -1136,6 +1188,75 @@ static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi, return -1; } +/* ------------------------------------------------------------------------- */ +/* Logitech K400 devices */ +/* ------------------------------------------------------------------------- */ + +/* + * The Logitech K400 keyboard has an embedded touchpad which is seen + * as a mouse from the OS point of view. There is a hardware shortcut to disable + * tap-to-click but the setting is not remembered accross reset, annoying some + * users. + * + * We can toggle this feature from the host by using the feature 0x6010: + * Touchpad FW items + */ + +struct k400_private_data { + u8 feature_index; +}; + +static int k400_disable_tap_to_click(struct hidpp_device *hidpp) +{ + struct k400_private_data *k400 = hidpp->private_data; + struct hidpp_touchpad_fw_items items = {}; + int ret; + u8 feature_type; + + if (!k400->feature_index) { + ret = hidpp_root_get_feature(hidpp, + HIDPP_PAGE_TOUCHPAD_FW_ITEMS, + &k400->feature_index, &feature_type); + if (ret) + /* means that the device is not powered up */ + return ret; + } + + ret = hidpp_touchpad_fw_items_set(hidpp, k400->feature_index, &items); + if (ret) + return ret; + + return 0; +} + +static int k400_allocate(struct hid_device *hdev) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + struct k400_private_data *k400; + + k400 = devm_kzalloc(&hdev->dev, sizeof(struct k400_private_data), + GFP_KERNEL); + if (!k400) + return -ENOMEM; + + hidpp->private_data = k400; + + return 0; +}; + +static int k400_connect(struct hid_device *hdev, bool connected) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + + if (!connected) + return 0; + + if (!disable_tap_to_click) + return 0; + + return k400_disable_tap_to_click(hidpp); +} + /* -------------------------------------------------------------------------- */ /* Generic HID++ devices */ /* -------------------------------------------------------------------------- */ @@ -1332,6 +1453,10 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) ret = m560_send_config_command(hdev, connected); if (ret) return; + } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) { + ret = k400_connect(hdev, connected); + if (ret) + return; } if (!connected || hidpp->delayed_input) @@ -1416,6 +1541,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = m560_allocate(hdev); if (ret) goto allocate_fail; + } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) { + ret = k400_allocate(hdev); + if (ret) + goto allocate_fail; } INIT_WORK(&hidpp->work, delayed_work_cb); @@ -1510,6 +1639,10 @@ static const struct hid_device_id hidpp_devices[] = { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, USB_VENDOR_ID_LOGITECH, 0x402d), .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 }, + { /* Keyboard logitech K400 */ + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, 0x4024), + .driver_data = HIDPP_QUIRK_CONNECT_EVENTS | HIDPP_QUIRK_CLASS_K400 }, { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, From 18339f59c3a6698ee17d32970c9e1e450b16e7c3 Mon Sep 17 00:00:00 2001 From: Maciej Zuk Date: Thu, 3 Sep 2015 21:46:39 +0200 Subject: [PATCH 05/30] HID: dragonrise: fix HID Descriptor for 0x0006 PID Fixed HID descriptor for DragonRise Joystick. Replaced default descriptor which doubles Z axis and causes mixing values of X and Z axes. Signed-off-by: Maciej Zuk Signed-off-by: Jiri Kosina --- drivers/hid/hid-dr.c | 58 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c index ce0644424f58..1d78ba3b799e 100644 --- a/drivers/hid/hid-dr.c +++ b/drivers/hid/hid-dr.c @@ -234,6 +234,58 @@ static __u8 pid0011_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +static __u8 pid0006_rdesc_fixed[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x04, /* Usage (Joystick) */ + 0xA1, 0x01, /* Collection (Application) */ + 0xA1, 0x02, /* Collection (Logical) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x05, /* Report Count (5) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x35, 0x00, /* Physical Minimum (0) */ + 0x46, 0xFF, 0x00, /* Physical Maximum (255) */ + 0x09, 0x30, /* Usage (X) */ + 0x09, 0x33, /* Usage (Ry) */ + 0x09, 0x32, /* Usage (Z) */ + 0x09, 0x31, /* Usage (Y) */ + 0x09, 0x34, /* Usage (Ry) */ + 0x81, 0x02, /* Input (Variable) */ + 0x75, 0x04, /* Report Size (4) */ + 0x95, 0x01, /* Report Count (1) */ + 0x25, 0x07, /* Logical Maximum (7) */ + 0x46, 0x3B, 0x01, /* Physical Maximum (315) */ + 0x65, 0x14, /* Unit (Centimeter) */ + 0x09, 0x39, /* Usage (Hat switch) */ + 0x81, 0x42, /* Input (Variable) */ + 0x65, 0x00, /* Unit (None) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x0C, /* Report Count (12) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x45, 0x01, /* Physical Maximum (1) */ + 0x05, 0x09, /* Usage Page (Button) */ + 0x19, 0x01, /* Usage Minimum (0x01) */ + 0x29, 0x0C, /* Usage Maximum (0x0C) */ + 0x81, 0x02, /* Input (Variable) */ + 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x08, /* Report Count (8) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x45, 0x01, /* Physical Maximum (1) */ + 0x09, 0x01, /* Usage (0x01) */ + 0x81, 0x02, /* Input (Variable) */ + 0xC0, /* End Collection */ + 0xA1, 0x02, /* Collection (Logical) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x07, /* Report Count (7) */ + 0x46, 0xFF, 0x00, /* Physical Maximum (255) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x09, 0x02, /* Usage (0x02) */ + 0x91, 0x02, /* Output (Variable) */ + 0xC0, /* End Collection */ + 0xC0 /* End Collection */ +}; + static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { @@ -244,6 +296,12 @@ static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(pid0011_rdesc_fixed); } break; + case 0x0006: + if (*rsize == sizeof(pid0006_rdesc_fixed)) { + rdesc = pid0006_rdesc_fixed; + *rsize = sizeof(pid0006_rdesc_fixed); + } + break; } return rdesc; } From c6956eb70e2549a3c2fa6ee525e02776d293caf4 Mon Sep 17 00:00:00 2001 From: Donavan Lance Date: Tue, 15 Sep 2015 12:44:54 -0400 Subject: [PATCH 06/30] HID: Add new Microsoft Type Cover 3 product ID Adds support for Microsoft Type Cover 3 with 0x07e2 product ID. Signed-off-by: Donavan Lance Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 ++ drivers/hid/hid-ids.h | 1 + drivers/hid/hid-microsoft.c | 2 ++ drivers/hid/usbhid/hid-quirks.c | 1 + 4 files changed, 6 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bcd914a63af2..6ae2dddaab0c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -725,6 +725,7 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) if (hid->vendor == USB_VENDOR_ID_MICROSOFT && (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 || + hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 || hid->product == USB_DEVICE_ID_MS_POWER_COVER) && @@ -1928,6 +1929,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index f769208276ae..01bb2fae6925 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -679,6 +679,7 @@ #define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 #define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc +#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd #define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de #define USB_DEVICE_ID_MS_POWER_COVER 0x07da diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 9aa3515090a7..77a2cf3e4afe 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -278,6 +278,8 @@ static const struct hid_device_id ms_devices[] = { .driver_data = MS_DUPLICATE_USAGES }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3), .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2), + .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 1dff8f0015ba..5ef80c8d5bcf 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -91,6 +91,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS }, From 0678072755b6672b19ee0fd42748a003912fca09 Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Thu, 17 Sep 2015 17:02:22 +0200 Subject: [PATCH 07/30] HID: Make I2C a known bus in hid_connect() Just to prettify the log message. Otherwise it would be . Signed-off-by: Daniel Martin Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 31fc6427cac3..71a1286c4c35 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1678,6 +1678,9 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) case BUS_BLUETOOTH: bus = "BLUETOOTH"; break; + case BUS_I2C: + bus = "I2C"; + break; default: bus = ""; } From 79b568b9d0c7c5d81932f4486d50b38efdd6da6d Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Fri, 18 Sep 2015 16:31:33 -0700 Subject: [PATCH 08/30] HID: core: Avoid uninitialized buffer access hid_connect adds various strings to the buffer but they're all conditional. You can find circumstances where nothing would be written to it but the kernel will still print the supposedly empty buffer with printk. This leads to corruption on the console/in the logs. Ensure buf is initialized to an empty string. Signed-off-by: Richard Purdie [dvhart: Initialize string to "" rather than assign buf[0] = NULL;] Cc: Jiri Kosina Cc: linux-input@vger.kernel.org Cc: stable@vger.kernel.org Signed-off-by: Darren Hart Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bcd914a63af2..4130685fca7c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1611,7 +1611,7 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) "Multi-Axis Controller" }; const char *type, *bus; - char buf[64]; + char buf[64] = ""; unsigned int i; int len; int ret; From 3b164a00a9fc0e8df88291a671bb3de37aa3340f Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Wed, 23 Sep 2015 09:59:10 -0700 Subject: [PATCH 09/30] HID: wacom: Cleanup unsupported device_type for BAMBOO_PT Not all Bamboo support both pen and touch. Make sure we deal with pen only and touch only devices properly. Signed-off-by: Ping Cheng Tested-By: Aaron Skomra Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 49 ++++++++++++++++++++++++++++------------- drivers/hid/wacom_wac.c | 31 ++++++++++++++------------ drivers/hid/wacom_wac.h | 4 +++- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 9a4912c1828d..f4ba6a0ca4cb 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -211,7 +211,7 @@ static void wacom_usage_mapping(struct hid_device *hdev, * Bamboo models do not support HID_DG_CONTACTMAX. * And, Bamboo Pen only descriptor contains touch. */ - if (features->type != BAMBOO_PT) { + if (features->type > BAMBOO_PT) { /* ISDv4 touch devices at least supports one touch point */ if (finger && !features->touch_max) features->touch_max = 1; @@ -222,7 +222,8 @@ static void wacom_usage_mapping(struct hid_device *hdev, features->x_max = field->logical_maximum; if (finger) { features->x_phy = field->physical_maximum; - if (features->type != BAMBOO_PT) { + if ((features->type != BAMBOO_PT) && + (features->type != BAMBOO_TOUCH)) { features->unit = field->unit; features->unitExpo = field->unit_exponent; } @@ -232,7 +233,8 @@ static void wacom_usage_mapping(struct hid_device *hdev, features->y_max = field->logical_maximum; if (finger) { features->y_phy = field->physical_maximum; - if (features->type != BAMBOO_PT) { + if ((features->type != BAMBOO_PT) && + (features->type != BAMBOO_TOUCH)) { features->unit = field->unit; features->unitExpo = field->unit_exponent; } @@ -1547,15 +1549,16 @@ static void wacom_wireless_work(struct work_struct *work) wacom_wac1->features = *((struct wacom_features *)id->driver_data); wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PEN; - if (wacom_wac1->features.type != INTUOSHT && - wacom_wac1->features.type != BAMBOO_PT) - wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD; wacom_set_default_phy(&wacom_wac1->features); wacom_calculate_res(&wacom_wac1->features); snprintf(wacom_wac1->pen_name, WACOM_NAME_MAX, "%s (WL) Pen", wacom_wac1->features.name); - snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad", - wacom_wac1->features.name); + if (wacom_wac1->features.type < BAMBOO_PEN || + wacom_wac1->features.type > BAMBOO_PT) { + snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad", + wacom_wac1->features.name); + wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD; + } wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max; wacom_wac1->shared->type = wacom_wac1->features.type; wacom_wac1->pid = wacom_wac->pid; @@ -1575,13 +1578,14 @@ static void wacom_wireless_work(struct work_struct *work) wacom_calculate_res(&wacom_wac2->features); snprintf(wacom_wac2->touch_name, WACOM_NAME_MAX, "%s (WL) Finger",wacom_wac2->features.name); - snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX, - "%s (WL) Pad",wacom_wac2->features.name); if (wacom_wac1->features.touch_max) wacom_wac2->features.device_type |= WACOM_DEVICETYPE_TOUCH; - if (wacom_wac1->features.type == INTUOSHT || - wacom_wac1->features.type == BAMBOO_PT) + if (wacom_wac1->features.type >= INTUOSHT && + wacom_wac1->features.type <= BAMBOO_PT) { + snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX, + "%s (WL) Pad",wacom_wac2->features.name); wacom_wac2->features.device_type |= WACOM_DEVICETYPE_PAD; + } wacom_wac2->pid = wacom_wac->pid; error = wacom_allocate_inputs(wacom2) || wacom_register_inputs(wacom2); @@ -1772,6 +1776,24 @@ static int wacom_probe(struct hid_device *hdev, features->device_type |= WACOM_DEVICETYPE_PEN; } + /* Note that if query fails it is not a hard failure */ + wacom_query_tablet_data(hdev, features); + + /* touch only Bamboo doesn't support pen */ + if ((features->type == BAMBOO_TOUCH) && + (features->device_type & WACOM_DEVICETYPE_PEN)) { + error = -ENODEV; + goto fail_shared_data; + } + + /* pen only Bamboo neither support touch nor pad */ + if ((features->type == BAMBOO_PEN) && + ((features->device_type & WACOM_DEVICETYPE_TOUCH) || + (features->device_type & WACOM_DEVICETYPE_PAD))) { + error = -ENODEV; + goto fail_shared_data; + } + wacom_calculate_res(features); wacom_update_name(wacom); @@ -1809,9 +1831,6 @@ static int wacom_probe(struct hid_device *hdev, goto fail_hw_start; } - /* Note that if query fails it is not a hard failure */ - wacom_query_tablet_data(hdev, features); - if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) error = hid_hw_open(hdev); diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 0215ab62bb93..af734efe3d60 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2300,6 +2300,8 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) break; case BAMBOO_PT: + case BAMBOO_PEN: + case BAMBOO_TOUCH: case INTUOSHT: if (wacom_wac->data[0] == WACOM_REPORT_USB) sync = wacom_status_irq(wacom_wac, len); @@ -2387,9 +2389,8 @@ void wacom_setup_device_quirks(struct wacom *wacom) /* The pen and pad share the same interface on most devices */ if (features->type == GRAPHIRE_BT || features->type == WACOM_G4 || - features->type == DTUS || features->type == WACOM_MO || - (features->type >= INTUOS3S && features->type <= WACOM_13HD && - features->type != INTUOSHT)) { + features->type == DTUS || + (features->type >= INTUOS3S && features->type <= WACOM_MO)) { if (features->device_type & WACOM_DEVICETYPE_PEN) features->device_type |= WACOM_DEVICETYPE_PAD; } @@ -2406,12 +2407,12 @@ void wacom_setup_device_quirks(struct wacom *wacom) * interface (PacketSize of WACOM_PKGLEN_BBTOUCH3), override the * tablet values. */ - if ((features->type >= INTUOS5S && features->type <= INTUOSHT) || - (features->type == BAMBOO_PT)) { + if ((features->type >= INTUOS5S && features->type <= INTUOSPL) || + (features->type >= INTUOSHT && features->type <= BAMBOO_PT)) { if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { if (features->touch_max) features->device_type |= WACOM_DEVICETYPE_TOUCH; - if (features->type == BAMBOO_PT || features->type == INTUOSHT) + if (features->type >= INTUOSHT || features->type <= BAMBOO_PT) features->device_type |= WACOM_DEVICETYPE_PAD; features->x_max = 4096; @@ -2598,6 +2599,7 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, case INTUOSHT: case BAMBOO_PT: + case BAMBOO_PEN: __clear_bit(ABS_MISC, input_dev->absbit); __set_bit(INPUT_PROP_POINTER, input_dev->propbit); @@ -2693,6 +2695,7 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev, /* fall through */ case BAMBOO_PT: + case BAMBOO_TOUCH: if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, @@ -2845,6 +2848,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, case INTUOSHT: case BAMBOO_PT: + case BAMBOO_TOUCH: __clear_bit(ABS_MISC, input_dev->absbit); __set_bit(BTN_LEFT, input_dev->keybit); @@ -3235,11 +3239,10 @@ static const struct wacom_features wacom_features_0x47 = { "Wacom Intuos2 6x8", 20320, 16240, 1023, 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x84 = - { "Wacom Wireless Receiver", 0, 0, 0, 0, - WIRELESS, 0, 0, .touch_max = 16 }; + { "Wacom Wireless Receiver", .type = WIRELESS, .touch_max = 16 }; static const struct wacom_features wacom_features_0xD0 = { "Wacom Bamboo 2FG", 14720, 9200, 1023, 31, - BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; + BAMBOO_TOUCH, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; static const struct wacom_features wacom_features_0xD1 = { "Wacom Bamboo 2FG 4x5", 14720, 9200, 1023, 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; @@ -3251,10 +3254,10 @@ static const struct wacom_features wacom_features_0xD3 = BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; static const struct wacom_features wacom_features_0xD4 = { "Wacom Bamboo Pen", 14720, 9200, 1023, 31, - BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xD5 = { "Wacom Bamboo Pen 6x8", 21648, 13700, 1023, 31, - BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xD6 = { "Wacom BambooPT 2FG 4x5", 14720, 9200, 1023, 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; @@ -3281,7 +3284,7 @@ static const struct wacom_features wacom_features_0xDF = BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16 }; static const struct wacom_features wacom_features_0x300 = { "Wacom Bamboo One S", 14720, 9225, 1023, 31, - BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x301 = { "Wacom Bamboo One M", 21648, 13530, 1023, 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -3329,8 +3332,8 @@ static const struct wacom_features wacom_features_0x323 = INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x331 = - { "Wacom Express Key Remote", 0, 0, 0, 0, - REMOTE, 0, 0, 18, .check_for_hid_type = true, + { "Wacom Express Key Remote", .type = REMOTE, + .numbered_buttons = 18, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_HID_ANY_ID = diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 1e270d401e18..3f2452029d2b 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -117,7 +117,6 @@ enum { INTUOSPS, INTUOSPM, INTUOSPL, - INTUOSHT, WACOM_21UX2, WACOM_22HD, DTK, @@ -129,6 +128,9 @@ enum { WACOM_13HD, WACOM_MO, WIRELESS, + BAMBOO_PEN, + INTUOSHT, + BAMBOO_TOUCH, BAMBOO_PT, WACOM_24HDT, WACOM_27QHDT, From eda01dab53b1126a20da98b5d691f3e55d79f21d Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Wed, 23 Sep 2015 13:51:15 -0700 Subject: [PATCH 10/30] HID: wacom: Add four new Intuos devices This series of devices supports both pen and touch. It reports touch data in Bamboo3 format and pen data in Intuos pro format. Signed-off-by: Ping Cheng Tested-By: Aaron Skomra Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 11 +++-- drivers/hid/wacom_wac.c | 97 +++++++++++++++++++++++++++++++---------- drivers/hid/wacom_wac.h | 2 + 3 files changed, 82 insertions(+), 28 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index f4ba6a0ca4cb..530476dc7bb9 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -1569,7 +1569,8 @@ static void wacom_wireless_work(struct work_struct *work) /* Touch interface */ if (wacom_wac1->features.touch_max || - wacom_wac1->features.type == INTUOSHT) { + (wacom_wac1->features.type >= INTUOSHT && + wacom_wac1->features.type <= BAMBOO_PT)) { wacom_wac2->features = *((struct wacom_features *)id->driver_data); wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3; @@ -1592,7 +1593,8 @@ static void wacom_wireless_work(struct work_struct *work) if (error) goto fail; - if (wacom_wac1->features.type == INTUOSHT && + if ((wacom_wac1->features.type == INTUOSHT || + wacom_wac1->features.type == INTUOSHT2) && wacom_wac1->features.touch_max) wacom_wac->shared->touch_input = wacom_wac2->touch_input; } @@ -1834,8 +1836,9 @@ static int wacom_probe(struct hid_device *hdev, if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) error = hid_hw_open(hdev); - if (wacom_wac->features.type == INTUOSHT && - wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) { + if ((wacom_wac->features.type == INTUOSHT || + wacom_wac->features.type == INTUOSHT2) && + (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)) { wacom_wac->shared->touch_input = wacom_wac->touch_input; } diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index af734efe3d60..a0d875c5ed38 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -769,9 +769,11 @@ static void wacom_intuos_general(struct wacom_wac *wacom) t = (t << 1) | (data[1] & 1); } input_report_abs(input, ABS_PRESSURE, t); - input_report_abs(input, ABS_TILT_X, + if (features->type != INTUOSHT2) { + input_report_abs(input, ABS_TILT_X, (((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64); - input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64); + input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64); + } input_report_key(input, BTN_STYLUS, data[1] & 2); input_report_key(input, BTN_STYLUS2, data[1] & 4); input_report_key(input, BTN_TOUCH, t > 10); @@ -799,6 +801,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) data[0] != WACOM_REPORT_INTUOSREAD && data[0] != WACOM_REPORT_INTUOSWRITE && data[0] != WACOM_REPORT_INTUOSPAD && + data[0] != WACOM_REPORT_INTUOS_PEN && data[0] != WACOM_REPORT_CINTIQ && data[0] != WACOM_REPORT_CINTIQPAD && data[0] != WACOM_REPORT_INTUOS5PAD) { @@ -1896,7 +1899,7 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) int y = (data[3] << 4) | (data[4] & 0x0f); int width, height; - if (features->type >= INTUOSPS && features->type <= INTUOSHT) { + if (features->type >= INTUOSPS && features->type <= INTUOSHT2) { width = data[5] * 100; height = data[6] * 100; } else { @@ -1924,7 +1927,7 @@ static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data) struct input_dev *input = wacom->pad_input; struct wacom_features *features = &wacom->features; - if (features->type == INTUOSHT) { + if (features->type == INTUOSHT || features->type == INTUOSHT2) { input_report_key(input, BTN_LEFT, (data[1] & 0x02) != 0); input_report_key(input, BTN_BACK, (data[1] & 0x08) != 0); } else { @@ -1939,7 +1942,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) { unsigned char *data = wacom->data; int count = data[1] & 0x07; - int i; + int touch_changed = 0, i; if (data[0] != 0x02) return 0; @@ -1949,15 +1952,16 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) int offset = (8 * i) + 2; int msg_id = data[offset]; - if (msg_id >= 2 && msg_id <= 17) + if (msg_id >= 2 && msg_id <= 17) { wacom_bpt3_touch_msg(wacom, data + offset); - else if (msg_id == 128) + touch_changed++; + } else if (msg_id == 128) wacom_bpt3_button_msg(wacom, data + offset); } - /* only update the touch if we actually have a touchpad */ - if (wacom->touch_registered) { + /* only update touch if we actually have a touchpad and touch data changed */ + if (wacom->touch_registered && touch_changed) { input_mt_sync_frame(wacom->touch_input); wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom); } @@ -2038,7 +2042,13 @@ static int wacom_bpt_pen(struct wacom_wac *wacom) static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) { - if (len == WACOM_PKGLEN_BBTOUCH) + struct wacom_features *features = &wacom->features; + + if ((features->type == INTUOSHT2) && + (wacom->data[0] == WACOM_REPORT_INTUOS_PEN) && + (features->device_type & WACOM_DEVICETYPE_PEN)) + return wacom_intuos_irq(wacom); + else if (len == WACOM_PKGLEN_BBTOUCH) return wacom_bpt_touch(wacom); else if (len == WACOM_PKGLEN_BBTOUCH3) return wacom_bpt3_touch(wacom); @@ -2145,7 +2155,8 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) if (connected) { int pid, battery, charging; - if ((wacom->shared->type == INTUOSHT) && + if ((wacom->shared->type == INTUOSHT || + wacom->shared->type == INTUOSHT2) && wacom->shared->touch_input && wacom->shared->touch_max) { input_report_switch(wacom->shared->touch_input, @@ -2183,7 +2194,8 @@ static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len) if (data[0] != WACOM_REPORT_USB) return 0; - if (features->type == INTUOSHT && + if ((features->type == INTUOSHT || + features->type == INTUOSHT2) && wacom_wac->shared->touch_input && features->touch_max) { input_report_switch(wacom_wac->shared->touch_input, @@ -2303,6 +2315,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) case BAMBOO_PEN: case BAMBOO_TOUCH: case INTUOSHT: + case INTUOSHT2: if (wacom_wac->data[0] == WACOM_REPORT_USB) sync = wacom_status_irq(wacom_wac, len); else @@ -2339,22 +2352,31 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) } } -static void wacom_setup_cintiq(struct wacom_wac *wacom_wac) +static void wacom_setup_basic_pro_pen(struct wacom_wac *wacom_wac) { struct input_dev *input_dev = wacom_wac->pen_input; input_set_capability(input_dev, EV_MSC, MSC_SERIAL); - __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_TOOL_BRUSH, input_dev->keybit); - __set_bit(BTN_TOOL_PENCIL, input_dev->keybit); - __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit); __set_bit(BTN_STYLUS, input_dev->keybit); __set_bit(BTN_STYLUS2, input_dev->keybit); input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features.distance_max, 0, 0); +} + +static void wacom_setup_cintiq(struct wacom_wac *wacom_wac) +{ + struct input_dev *input_dev = wacom_wac->pen_input; + + wacom_setup_basic_pro_pen(wacom_wac); + + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_BRUSH, input_dev->keybit); + __set_bit(BTN_TOOL_PENCIL, input_dev->keybit); + __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit); + input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); input_set_abs_params(input_dev, ABS_TILT_X, -64, 63, 0, 0); input_abs_set_res(input_dev, ABS_TILT_X, 57); @@ -2600,16 +2622,21 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, case INTUOSHT: case BAMBOO_PT: case BAMBOO_PEN: - __clear_bit(ABS_MISC, input_dev->absbit); - + case INTUOSHT2: __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); - __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_STYLUS, input_dev->keybit); - __set_bit(BTN_STYLUS2, input_dev->keybit); - input_set_abs_params(input_dev, ABS_DISTANCE, 0, + + if (features->type == INTUOSHT2) { + wacom_setup_basic_pro_pen(wacom_wac); + } else { + __clear_bit(ABS_MISC, input_dev->absbit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + input_set_abs_params(input_dev, ABS_DISTANCE, 0, features->distance_max, 0, 0); + } break; case BAMBOO_PAD: __clear_bit(ABS_MISC, input_dev->absbit); @@ -2690,6 +2717,7 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev, break; case INTUOSHT: + case INTUOSHT2: input_dev->evbit[0] |= BIT_MASK(EV_SW); __set_bit(SW_MUTE_DEVICE, input_dev->swbit); /* fall through */ @@ -2849,6 +2877,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, case INTUOSHT: case BAMBOO_PT: case BAMBOO_TOUCH: + case INTUOSHT2: __clear_bit(ABS_MISC, input_dev->absbit); __set_bit(BTN_LEFT, input_dev->keybit); @@ -3335,6 +3364,22 @@ static const struct wacom_features wacom_features_0x331 = { "Wacom Express Key Remote", .type = REMOTE, .numbered_buttons = 18, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x33B = + { "Wacom Intuos S 2", 15200, 9500, 2047, 63, + INTUOSHT2, WACOM_INTUOS_RES, WACOM_INTUOS_RES, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x33C = + { "Wacom Intuos PT S 2", 15200, 9500, 2047, 63, + INTUOSHT2, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x33D = + { "Wacom Intuos P M 2", 21600, 13500, 2047, 63, + INTUOSHT2, WACOM_INTUOS_RES, WACOM_INTUOS_RES, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x33E = + { "Wacom Intuos PT M 2", 21600, 13500, 2047, 63, + INTUOSHT2, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_HID_ANY_ID = { "Wacom HID", .type = HID_GENERIC }; @@ -3494,6 +3539,10 @@ const struct hid_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x333) }, { USB_DEVICE_WACOM(0x335) }, { USB_DEVICE_WACOM(0x336) }, + { USB_DEVICE_WACOM(0x33B) }, + { USB_DEVICE_WACOM(0x33C) }, + { USB_DEVICE_WACOM(0x33D) }, + { USB_DEVICE_WACOM(0x33E) }, { USB_DEVICE_WACOM(0x4001) }, { USB_DEVICE_WACOM(0x4004) }, { USB_DEVICE_WACOM(0x5000) }, diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 3f2452029d2b..1a30e4407a46 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -68,6 +68,7 @@ #define WACOM_REPORT_BPAD_PEN 3 #define WACOM_REPORT_BPAD_TOUCH 16 #define WACOM_REPORT_DEVICE_LIST 16 +#define WACOM_REPORT_INTUOS_PEN 16 #define WACOM_REPORT_REMOTE 17 /* device quirks */ @@ -130,6 +131,7 @@ enum { WIRELESS, BAMBOO_PEN, INTUOSHT, + INTUOSHT2, BAMBOO_TOUCH, BAMBOO_PT, WACOM_24HDT, From f3984edc171c9f1296502d6d5e41290785816736 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 28 Sep 2015 15:15:08 +0300 Subject: [PATCH 11/30] HID: i2c-hid: Fill in physical device providing HID functionality Currently hid_connect() prints out following when I2C connected HID devices is connected: hid-multitouch 0018:03EB:2136.0001: ... [ATML3432:00 03EB:2136] on After "on " should read physical device name but it is left empty by the driver. Make it look better and fill in the physical device name. Signed-off-by: Mika Westerberg Acked-by: Benjamin Tissoires Reviewed-by: Daniel Martin Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 2871f3c81a4c..10bd8e6e4c9c 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -1028,6 +1028,7 @@ static int i2c_hid_probe(struct i2c_client *client, snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX", client->name, hid->vendor, hid->product); + strlcpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys)); ret = hid_add_device(hid); if (ret) { From dc425a1c8c02b45fbab8b3ba522649d238cf84db Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 28 Sep 2015 17:22:17 +0300 Subject: [PATCH 12/30] HID: multitouch: Add suffix for HID_DG_TOUCHPAD Instead of printing "UNKNOWN" as device suffix for HID_DG_TOUCHPAD call the device as "Touchpad". Signed-off-by: Mika Westerberg Acked-by: Benjamin Tissoires Reviewed-by: Daniel Martin Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 426b2f1a3450..661b4fce1a5d 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -968,6 +968,9 @@ static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) case HID_DG_TOUCHSCREEN: /* we do not set suffix = "Touchscreen" */ break; + case HID_DG_TOUCHPAD: + suffix = "Touchpad"; + break; case HID_GD_SYSTEM_CONTROL: suffix = "System Control"; break; From b2c68a2f1bab3e3d3bf4ab2b5fcd94cd37f61b41 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 29 Sep 2015 15:52:59 -0700 Subject: [PATCH 13/30] HID: hid-input: allow input_configured callback return errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When configuring input device via input_configured callback we may encounter errors (for example input_mt_init_slots() may fail). Instead of continuing with half-initialized input device let's allow driver indicate failures. Signed-off-by: Jaikumar Ganesh Signed-off-by: Arve Hjønnevåg Reviewed-by: Benjamin Tissoires Reviewed-by: David Herrmann Acked-by: Nikolai Kondrashov Acked-by: Andrew Duggan Signed-off-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/hid-appleir.c | 4 +++- drivers/hid/hid-elo.c | 4 +++- drivers/hid/hid-input.c | 10 ++++++---- drivers/hid/hid-lenovo.c | 4 +++- drivers/hid/hid-logitech-hidpp.c | 4 +++- drivers/hid/hid-magicmouse.c | 8 ++++++-- drivers/hid/hid-multitouch.c | 20 +++++++++++++++----- drivers/hid/hid-ntrig.c | 6 ++++-- drivers/hid/hid-rmi.c | 11 +++++++---- drivers/hid/hid-sony.c | 13 ++++++++++--- drivers/hid/hid-uclogic.c | 6 ++++-- include/linux/hid.h | 4 ++-- 12 files changed, 66 insertions(+), 28 deletions(-) diff --git a/drivers/hid/hid-appleir.c b/drivers/hid/hid-appleir.c index 0e6a42d37eb6..07cbc70f00e7 100644 --- a/drivers/hid/hid-appleir.c +++ b/drivers/hid/hid-appleir.c @@ -256,7 +256,7 @@ out: return 0; } -static void appleir_input_configured(struct hid_device *hid, +static int appleir_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct input_dev *input_dev = hidinput->input; @@ -275,6 +275,8 @@ static void appleir_input_configured(struct hid_device *hid, for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++) set_bit(appleir->keymap[i], input_dev->keybit); clear_bit(KEY_RESERVED, input_dev->keybit); + + return 0; } static int appleir_input_mapping(struct hid_device *hid, diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c index 4e49462870ab..aad8c162a825 100644 --- a/drivers/hid/hid-elo.c +++ b/drivers/hid/hid-elo.c @@ -37,7 +37,7 @@ static bool use_fw_quirk = true; module_param(use_fw_quirk, bool, S_IRUGO); MODULE_PARM_DESC(use_fw_quirk, "Do periodic pokes for broken M firmwares (default = true)"); -static void elo_input_configured(struct hid_device *hdev, +static int elo_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { struct input_dev *input = hidinput->input; @@ -45,6 +45,8 @@ static void elo_input_configured(struct hid_device *hdev, set_bit(BTN_TOUCH, input->keybit); set_bit(ABS_PRESSURE, input->absbit); input_set_abs_params(input, ABS_PRESSURE, 0, 256, 0, 0); + + return 0; } static void elo_process_data(struct input_dev *input, const u8 *data, int size) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 53aeaf6252c7..2ba6bf69b7d0 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1510,8 +1510,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) * UGCI) cram a lot of unrelated inputs into the * same interface. */ hidinput->report = report; - if (drv->input_configured) - drv->input_configured(hid, hidinput); + if (drv->input_configured && + drv->input_configured(hid, hidinput)) + goto out_cleanup; if (input_register_device(hidinput->input)) goto out_cleanup; hidinput = NULL; @@ -1532,8 +1533,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) } if (hidinput) { - if (drv->input_configured) - drv->input_configured(hid, hidinput); + if (drv->input_configured && + drv->input_configured(hid, hidinput)) + goto out_cleanup; if (input_register_device(hidinput->input)) goto out_cleanup; } diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index e4bc6cb6d7fa..8979f1fd5208 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -848,7 +848,7 @@ static void lenovo_remove(struct hid_device *hdev) hid_hw_stop(hdev); } -static void lenovo_input_configured(struct hid_device *hdev, +static int lenovo_input_configured(struct hid_device *hdev, struct hid_input *hi) { switch (hdev->product) { @@ -863,6 +863,8 @@ static void lenovo_input_configured(struct hid_device *hdev, } break; } + + return 0; } diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 484196459305..a25f562f2d7b 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -1160,13 +1160,15 @@ static void hidpp_populate_input(struct hidpp_device *hidpp, m560_populate_input(hidpp, input, origin_is_hid_core); } -static void hidpp_input_configured(struct hid_device *hdev, +static int hidpp_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct input_dev *input = hidinput->input; hidpp_populate_input(hidpp, input, true); + + return 0; } static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 29a74c1efcb8..d6fa496d0ca2 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -471,18 +471,22 @@ static int magicmouse_input_mapping(struct hid_device *hdev, return 0; } -static void magicmouse_input_configured(struct hid_device *hdev, +static int magicmouse_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct magicmouse_sc *msc = hid_get_drvdata(hdev); + int ret; - int ret = magicmouse_setup_input(msc->input, hdev); + ret = magicmouse_setup_input(msc->input, hdev); if (ret) { hid_err(hdev, "magicmouse setup input failed (%d)\n", ret); /* clean msc->input to notify probe() of the failure */ msc->input = NULL; + return ret; } + + return 0; } diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 426b2f1a3450..2ed42d8f805b 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -725,12 +725,13 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) mt_sync_frame(td, report->field[0]->hidinput->input); } -static void mt_touch_input_configured(struct hid_device *hdev, +static int mt_touch_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); struct mt_class *cls = &td->mtclass; struct input_dev *input = hi->input; + int ret; if (!td->maxcontacts) td->maxcontacts = MT_DEFAULT_MAXCONTACT; @@ -752,9 +753,12 @@ static void mt_touch_input_configured(struct hid_device *hdev, if (td->is_buttonpad) __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); - input_mt_init_slots(input, td->maxcontacts, td->mt_flags); + ret = input_mt_init_slots(input, td->maxcontacts, td->mt_flags); + if (ret) + return ret; td->mt_flags = 0; + return 0; } static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -930,15 +934,19 @@ static void mt_post_parse(struct mt_device *td) cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } -static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) +static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); char *name; const char *suffix = NULL; struct hid_field *field = hi->report->field[0]; + int ret; - if (hi->report->id == td->mt_report_id) - mt_touch_input_configured(hdev, hi); + if (hi->report->id == td->mt_report_id) { + ret = mt_touch_input_configured(hdev, hi); + if (ret) + return ret; + } /* * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" @@ -989,6 +997,8 @@ static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) hi->input->name = name; } } + + return 0; } static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 600f2075512f..756d1ef9bd99 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -859,14 +859,14 @@ not_claimed_input: return 1; } -static void ntrig_input_configured(struct hid_device *hid, +static int ntrig_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct input_dev *input = hidinput->input; if (hidinput->report->maxfield < 1) - return; + return 0; switch (hidinput->report->field[0]->application) { case HID_DG_PEN: @@ -890,6 +890,8 @@ static void ntrig_input_configured(struct hid_device *hid, "N-Trig MultiTouch"; break; } + + return 0; } static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 2c148129beb2..67cd059a8f46 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -1173,7 +1173,7 @@ static int rmi_populate(struct hid_device *hdev) return 0; } -static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) +static int rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct rmi_data *data = hid_get_drvdata(hdev); struct input_dev *input = hi->input; @@ -1185,10 +1185,10 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) hid_dbg(hdev, "Opening low level driver\n"); ret = hid_hw_open(hdev); if (ret) - return; + return ret; if (!(data->device_flags & RMI_DEVICE)) - return; + return 0; /* Allow incoming hid reports */ hid_device_io_start(hdev); @@ -1228,7 +1228,9 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); - input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER); + ret = input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER); + if (ret < 0) + goto exit; if (data->button_count) { __set_bit(EV_KEY, input->evbit); @@ -1244,6 +1246,7 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) exit: hid_device_io_stop(hdev); hid_hw_close(hdev); + return ret; } static int rmi_input_mapping(struct hid_device *hdev, diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 661f94f8ab8b..774cd2210566 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1360,20 +1360,27 @@ static int sony_register_touchpad(struct hid_input *hi, int touch_count, return 0; } -static void sony_input_configured(struct hid_device *hdev, +static int sony_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { struct sony_sc *sc = hid_get_drvdata(hdev); + int ret; /* * The Dualshock 4 touchpad supports 2 touches and has a * resolution of 1920x942 (44.86 dots/mm). */ if (sc->quirks & DUALSHOCK4_CONTROLLER) { - if (sony_register_touchpad(hidinput, 2, 1920, 942) != 0) + ret = sony_register_touchpad(hidinput, 2, 1920, 942); + if (ret) { hid_err(sc->hdev, - "Unable to initialize multi-touch slots\n"); + "Unable to initialize multi-touch slots: %d\n", + ret); + return ret; + } } + + return 0; } /* diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c index b905d501e752..85ac43517e3f 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic.c @@ -731,7 +731,7 @@ static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } -static void uclogic_input_configured(struct hid_device *hdev, +static int uclogic_input_configured(struct hid_device *hdev, struct hid_input *hi) { char *name; @@ -741,7 +741,7 @@ static void uclogic_input_configured(struct hid_device *hdev, /* no report associated (HID_QUIRK_MULTI_INPUT not set) */ if (!hi->report) - return; + return 0; field = hi->report->field[0]; @@ -774,6 +774,8 @@ static void uclogic_input_configured(struct hid_device *hdev, hi->input->name = name; } } + + return 0; } /** diff --git a/include/linux/hid.h b/include/linux/hid.h index f17980de2662..251a1d382e23 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -698,8 +698,8 @@ struct hid_driver { int (*input_mapped)(struct hid_device *hdev, struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max); - void (*input_configured)(struct hid_device *hdev, - struct hid_input *hidinput); + int (*input_configured)(struct hid_device *hdev, + struct hid_input *hidinput); void (*feature_mapping)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage); From 6f78193ee9ea5575180d4462f0f7273a22dd5057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Vuchener?= Date: Wed, 30 Sep 2015 15:14:20 +0200 Subject: [PATCH 14/30] HID: corsair: Add Corsair Vengeance K90 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements a HID driver for the Corsair Vengeance K90 keyboard. It fixes the behaviour of the keys using incorrect HID usage codes and exposes the macro playback mode and current profile to the user space through sysfs attributes. It also adds two LED class devices controlling the "record" LED and the backlight. Signed-off-by: Clément Vuchener Signed-off-by: Jiri Kosina --- .../ABI/testing/sysfs-driver-hid-corsair | 15 + drivers/hid/Kconfig | 10 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-corsair.c | 673 ++++++++++++++++++ drivers/hid/hid-ids.h | 3 + 6 files changed, 703 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-corsair create mode 100644 drivers/hid/hid-corsair.c diff --git a/Documentation/ABI/testing/sysfs-driver-hid-corsair b/Documentation/ABI/testing/sysfs-driver-hid-corsair new file mode 100644 index 000000000000..b8827f0f12c4 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-corsair @@ -0,0 +1,15 @@ +What: /sys/bus/drivers/corsair//macro_mode +Date: August 2015 +KernelVersion: 4.2 +Contact: Clement Vuchener +Description: Get/set the current playback mode. "SW" for software mode + where G-keys triggers their regular key codes. "HW" for + hardware playback mode where the G-keys play their macro + from the on-board memory. + + +What: /sys/bus/drivers/corsair//current_profile +Date: August 2015 +KernelVersion: 4.2 +Contact: Clement Vuchener +Description: Get/set the current selected profile. Values are from 1 to 3. diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 6ab51ae3c39d..3fe96787c87e 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -171,6 +171,16 @@ config HID_CHICONY ---help--- Support for Chicony Tactical pad. +config HID_CORSAIR + tristate "Corsair devices" + depends on HID && USB && LEDS_CLASS + ---help--- + Support for Corsair devices that are not fully compliant with the + HID standard. + + Supported devices: + - Vengeance K90 + config HID_PRODIKEYS tristate "Prodikeys PC-MIDI Keyboard support" depends on HID && SND diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index e6441bc7dae4..edaa0f264bde 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o +obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o obj-$(CONFIG_HID_CP2112) += hid-cp2112.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bcd914a63af2..d5fc4d18d230 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1828,6 +1828,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90) }, { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c new file mode 100644 index 000000000000..cd3429e4a75d --- /dev/null +++ b/drivers/hid/hid-corsair.c @@ -0,0 +1,673 @@ +/* + * HID driver for Corsair devices + * + * Supported devices: + * - Vengeance K90 Keyboard + * + * Copyright (c) 2015 Clement Vuchener + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +#define CORSAIR_USE_K90_MACRO (1<<0) +#define CORSAIR_USE_K90_BACKLIGHT (1<<1) + +struct k90_led { + struct led_classdev cdev; + int brightness; + struct work_struct work; + int removed; +}; + +struct k90_drvdata { + struct k90_led record_led; +}; + +struct corsair_drvdata { + unsigned long quirks; + struct k90_drvdata *k90; + struct k90_led *backlight; +}; + +#define K90_GKEY_COUNT 18 + +static int corsair_usage_to_gkey(unsigned int usage) +{ + /* G1 (0xd0) to G16 (0xdf) */ + if (usage >= 0xd0 && usage <= 0xdf) + return usage - 0xd0 + 1; + /* G17 (0xe8) to G18 (0xe9) */ + if (usage >= 0xe8 && usage <= 0xe9) + return usage - 0xe8 + 17; + return 0; +} + +static unsigned short corsair_gkey_map[K90_GKEY_COUNT] = { + BTN_TRIGGER_HAPPY1, + BTN_TRIGGER_HAPPY2, + BTN_TRIGGER_HAPPY3, + BTN_TRIGGER_HAPPY4, + BTN_TRIGGER_HAPPY5, + BTN_TRIGGER_HAPPY6, + BTN_TRIGGER_HAPPY7, + BTN_TRIGGER_HAPPY8, + BTN_TRIGGER_HAPPY9, + BTN_TRIGGER_HAPPY10, + BTN_TRIGGER_HAPPY11, + BTN_TRIGGER_HAPPY12, + BTN_TRIGGER_HAPPY13, + BTN_TRIGGER_HAPPY14, + BTN_TRIGGER_HAPPY15, + BTN_TRIGGER_HAPPY16, + BTN_TRIGGER_HAPPY17, + BTN_TRIGGER_HAPPY18, +}; + +module_param_array_named(gkey_codes, corsair_gkey_map, ushort, NULL, S_IRUGO); +MODULE_PARM_DESC(gkey_codes, "Key codes for the G-keys"); + +static unsigned short corsair_record_keycodes[2] = { + BTN_TRIGGER_HAPPY19, + BTN_TRIGGER_HAPPY20 +}; + +module_param_array_named(recordkey_codes, corsair_record_keycodes, ushort, + NULL, S_IRUGO); +MODULE_PARM_DESC(recordkey_codes, "Key codes for the MR (start and stop record) button"); + +static unsigned short corsair_profile_keycodes[3] = { + BTN_TRIGGER_HAPPY21, + BTN_TRIGGER_HAPPY22, + BTN_TRIGGER_HAPPY23 +}; + +module_param_array_named(profilekey_codes, corsair_profile_keycodes, ushort, + NULL, S_IRUGO); +MODULE_PARM_DESC(profilekey_codes, "Key codes for the profile buttons"); + +#define CORSAIR_USAGE_SPECIAL_MIN 0xf0 +#define CORSAIR_USAGE_SPECIAL_MAX 0xff + +#define CORSAIR_USAGE_MACRO_RECORD_START 0xf6 +#define CORSAIR_USAGE_MACRO_RECORD_STOP 0xf7 + +#define CORSAIR_USAGE_PROFILE 0xf1 +#define CORSAIR_USAGE_M1 0xf1 +#define CORSAIR_USAGE_M2 0xf2 +#define CORSAIR_USAGE_M3 0xf3 +#define CORSAIR_USAGE_PROFILE_MAX 0xf3 + +#define CORSAIR_USAGE_META_OFF 0xf4 +#define CORSAIR_USAGE_META_ON 0xf5 + +#define CORSAIR_USAGE_LIGHT 0xfa +#define CORSAIR_USAGE_LIGHT_OFF 0xfa +#define CORSAIR_USAGE_LIGHT_DIM 0xfb +#define CORSAIR_USAGE_LIGHT_MEDIUM 0xfc +#define CORSAIR_USAGE_LIGHT_BRIGHT 0xfd +#define CORSAIR_USAGE_LIGHT_MAX 0xfd + +/* USB control protocol */ + +#define K90_REQUEST_BRIGHTNESS 49 +#define K90_REQUEST_MACRO_MODE 2 +#define K90_REQUEST_STATUS 4 +#define K90_REQUEST_GET_MODE 5 +#define K90_REQUEST_PROFILE 20 + +#define K90_MACRO_MODE_SW 0x0030 +#define K90_MACRO_MODE_HW 0x0001 + +#define K90_MACRO_LED_ON 0x0020 +#define K90_MACRO_LED_OFF 0x0040 + +/* + * LED class devices + */ + +#define K90_BACKLIGHT_LED_SUFFIX "::backlight" +#define K90_RECORD_LED_SUFFIX "::record" + +static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev) +{ + int ret; + struct k90_led *led = container_of(led_cdev, struct k90_led, cdev); + struct device *dev = led->cdev.dev->parent; + struct usb_interface *usbif = to_usb_interface(dev->parent); + struct usb_device *usbdev = interface_to_usbdev(usbif); + int brightness; + char data[8]; + + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), + K90_REQUEST_STATUS, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, 0, 0, data, 8, + USB_CTRL_SET_TIMEOUT); + if (ret < 0) { + dev_warn(dev, "Failed to get K90 initial state (error %d).\n", + ret); + return -EIO; + } + brightness = data[4]; + if (brightness < 0 || brightness > 3) { + dev_warn(dev, + "Read invalid backlight brightness: %02hhx.\n", + data[4]); + return -EIO; + } + return brightness; +} + +static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev) +{ + struct k90_led *led = container_of(led_cdev, struct k90_led, cdev); + + return led->brightness; +} + +static void k90_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct k90_led *led = container_of(led_cdev, struct k90_led, cdev); + + led->brightness = brightness; + schedule_work(&led->work); +} + +static void k90_backlight_work(struct work_struct *work) +{ + int ret; + struct k90_led *led = container_of(work, struct k90_led, work); + struct device *dev; + struct usb_interface *usbif; + struct usb_device *usbdev; + + if (led->removed) + return; + + dev = led->cdev.dev->parent; + usbif = to_usb_interface(dev->parent); + usbdev = interface_to_usbdev(usbif); + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + K90_REQUEST_BRIGHTNESS, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, led->brightness, 0, + NULL, 0, USB_CTRL_SET_TIMEOUT); + if (ret != 0) + dev_warn(dev, "Failed to set backlight brightness (error: %d).\n", + ret); +} + +static void k90_record_led_work(struct work_struct *work) +{ + int ret; + struct k90_led *led = container_of(work, struct k90_led, work); + struct device *dev; + struct usb_interface *usbif; + struct usb_device *usbdev; + int value; + + if (led->removed) + return; + + dev = led->cdev.dev->parent; + usbif = to_usb_interface(dev->parent); + usbdev = interface_to_usbdev(usbif); + + if (led->brightness > 0) + value = K90_MACRO_LED_ON; + else + value = K90_MACRO_LED_OFF; + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + K90_REQUEST_MACRO_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (ret != 0) + dev_warn(dev, "Failed to set record LED state (error: %d).\n", + ret); +} + +/* + * Keyboard attributes + */ + +static ssize_t k90_show_macro_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct usb_interface *usbif = to_usb_interface(dev->parent); + struct usb_device *usbdev = interface_to_usbdev(usbif); + const char *macro_mode; + char data[8]; + + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), + K90_REQUEST_GET_MODE, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, 0, 0, data, 2, + USB_CTRL_SET_TIMEOUT); + if (ret < 0) { + dev_warn(dev, "Failed to get K90 initial mode (error %d).\n", + ret); + return -EIO; + } + + switch (data[0]) { + case K90_MACRO_MODE_HW: + macro_mode = "HW"; + break; + + case K90_MACRO_MODE_SW: + macro_mode = "SW"; + break; + default: + dev_warn(dev, "K90 in unknown mode: %02hhx.\n", + data[0]); + return -EIO; + } + + return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode); +} + +static ssize_t k90_store_macro_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct usb_interface *usbif = to_usb_interface(dev->parent); + struct usb_device *usbdev = interface_to_usbdev(usbif); + __u16 value; + + if (strncmp(buf, "SW", 2) == 0) + value = K90_MACRO_MODE_SW; + else if (strncmp(buf, "HW", 2) == 0) + value = K90_MACRO_MODE_HW; + else + return -EINVAL; + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + K90_REQUEST_MACRO_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (ret != 0) { + dev_warn(dev, "Failed to set macro mode.\n"); + return ret; + } + + return count; +} + +static ssize_t k90_show_current_profile(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + struct usb_interface *usbif = to_usb_interface(dev->parent); + struct usb_device *usbdev = interface_to_usbdev(usbif); + int current_profile; + char data[8]; + + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), + K90_REQUEST_STATUS, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, 0, 0, data, 8, + USB_CTRL_SET_TIMEOUT); + if (ret < 0) { + dev_warn(dev, "Failed to get K90 initial state (error %d).\n", + ret); + return -EIO; + } + current_profile = data[7]; + if (current_profile < 1 || current_profile > 3) { + dev_warn(dev, "Read invalid current profile: %02hhx.\n", + data[7]); + return -EIO; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", current_profile); +} + +static ssize_t k90_store_current_profile(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct usb_interface *usbif = to_usb_interface(dev->parent); + struct usb_device *usbdev = interface_to_usbdev(usbif); + int profile; + + if (kstrtoint(buf, 10, &profile)) + return -EINVAL; + if (profile < 1 || profile > 3) + return -EINVAL; + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + K90_REQUEST_PROFILE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, profile, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (ret != 0) { + dev_warn(dev, "Failed to change current profile (error %d).\n", + ret); + return ret; + } + + return count; +} + +static DEVICE_ATTR(macro_mode, 0644, k90_show_macro_mode, k90_store_macro_mode); +static DEVICE_ATTR(current_profile, 0644, k90_show_current_profile, + k90_store_current_profile); + +static struct attribute *k90_attrs[] = { + &dev_attr_macro_mode.attr, + &dev_attr_current_profile.attr, + NULL +}; + +static const struct attribute_group k90_attr_group = { + .attrs = k90_attrs, +}; + +/* + * Driver functions + */ + +static int k90_init_backlight(struct hid_device *dev) +{ + int ret; + struct corsair_drvdata *drvdata = hid_get_drvdata(dev); + size_t name_sz; + char *name; + + drvdata->backlight = kzalloc(sizeof(struct k90_led), GFP_KERNEL); + if (!drvdata->backlight) { + ret = -ENOMEM; + goto fail_backlight_alloc; + } + + name_sz = + strlen(dev_name(&dev->dev)) + sizeof(K90_BACKLIGHT_LED_SUFFIX); + name = kzalloc(name_sz, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto fail_name_alloc; + } + snprintf(name, name_sz, "%s" K90_BACKLIGHT_LED_SUFFIX, + dev_name(&dev->dev)); + drvdata->backlight->removed = 0; + drvdata->backlight->cdev.name = name; + drvdata->backlight->cdev.max_brightness = 3; + drvdata->backlight->cdev.brightness_set = k90_brightness_set; + drvdata->backlight->cdev.brightness_get = k90_backlight_get; + INIT_WORK(&drvdata->backlight->work, k90_backlight_work); + ret = led_classdev_register(&dev->dev, &drvdata->backlight->cdev); + if (ret != 0) + goto fail_register_cdev; + + return 0; + +fail_register_cdev: + kfree(drvdata->backlight->cdev.name); +fail_name_alloc: + kfree(drvdata->backlight); + drvdata->backlight = NULL; +fail_backlight_alloc: + return ret; +} + +static int k90_init_macro_functions(struct hid_device *dev) +{ + int ret; + struct corsair_drvdata *drvdata = hid_get_drvdata(dev); + struct k90_drvdata *k90; + size_t name_sz; + char *name; + + k90 = kzalloc(sizeof(struct k90_drvdata), GFP_KERNEL); + if (!k90) { + ret = -ENOMEM; + goto fail_drvdata; + } + drvdata->k90 = k90; + + /* Init LED device for record LED */ + name_sz = strlen(dev_name(&dev->dev)) + sizeof(K90_RECORD_LED_SUFFIX); + name = kzalloc(name_sz, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto fail_record_led_alloc; + } + snprintf(name, name_sz, "%s" K90_RECORD_LED_SUFFIX, + dev_name(&dev->dev)); + k90->record_led.removed = 0; + k90->record_led.cdev.name = name; + k90->record_led.cdev.max_brightness = 1; + k90->record_led.cdev.brightness_set = k90_brightness_set; + k90->record_led.cdev.brightness_get = k90_record_led_get; + INIT_WORK(&k90->record_led.work, k90_record_led_work); + k90->record_led.brightness = 0; + ret = led_classdev_register(&dev->dev, &k90->record_led.cdev); + if (ret != 0) + goto fail_record_led; + + /* Init attributes */ + ret = sysfs_create_group(&dev->dev.kobj, &k90_attr_group); + if (ret != 0) + goto fail_sysfs; + + return 0; + +fail_sysfs: + k90->record_led.removed = 1; + led_classdev_unregister(&k90->record_led.cdev); + cancel_work_sync(&k90->record_led.work); +fail_record_led: + kfree(k90->record_led.cdev.name); +fail_record_led_alloc: + kfree(k90); +fail_drvdata: + drvdata->k90 = NULL; + return ret; +} + +static void k90_cleanup_backlight(struct hid_device *dev) +{ + struct corsair_drvdata *drvdata = hid_get_drvdata(dev); + + if (drvdata->backlight) { + drvdata->backlight->removed = 1; + led_classdev_unregister(&drvdata->backlight->cdev); + cancel_work_sync(&drvdata->backlight->work); + kfree(drvdata->backlight->cdev.name); + kfree(drvdata->backlight); + } +} + +static void k90_cleanup_macro_functions(struct hid_device *dev) +{ + struct corsair_drvdata *drvdata = hid_get_drvdata(dev); + struct k90_drvdata *k90 = drvdata->k90; + + if (k90) { + sysfs_remove_group(&dev->dev.kobj, &k90_attr_group); + + k90->record_led.removed = 1; + led_classdev_unregister(&k90->record_led.cdev); + cancel_work_sync(&k90->record_led.work); + kfree(k90->record_led.cdev.name); + + kfree(k90); + } +} + +static int corsair_probe(struct hid_device *dev, const struct hid_device_id *id) +{ + int ret; + unsigned long quirks = id->driver_data; + struct corsair_drvdata *drvdata; + struct usb_interface *usbif = to_usb_interface(dev->dev.parent); + + drvdata = devm_kzalloc(&dev->dev, sizeof(struct corsair_drvdata), + GFP_KERNEL); + if (drvdata == NULL) + return -ENOMEM; + drvdata->quirks = quirks; + hid_set_drvdata(dev, drvdata); + + ret = hid_parse(dev); + if (ret != 0) { + hid_err(dev, "parse failed\n"); + return ret; + } + ret = hid_hw_start(dev, HID_CONNECT_DEFAULT); + if (ret != 0) { + hid_err(dev, "hw start failed\n"); + return ret; + } + + if (usbif->cur_altsetting->desc.bInterfaceNumber == 0) { + if (quirks & CORSAIR_USE_K90_MACRO) { + ret = k90_init_macro_functions(dev); + if (ret != 0) + hid_warn(dev, "Failed to initialize K90 macro functions.\n"); + } + if (quirks & CORSAIR_USE_K90_BACKLIGHT) { + ret = k90_init_backlight(dev); + if (ret != 0) + hid_warn(dev, "Failed to initialize K90 backlight.\n"); + } + } + + return 0; +} + +static void corsair_remove(struct hid_device *dev) +{ + k90_cleanup_macro_functions(dev); + k90_cleanup_backlight(dev); + + hid_hw_stop(dev); +} + +static int corsair_event(struct hid_device *dev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct corsair_drvdata *drvdata = hid_get_drvdata(dev); + + if (!drvdata->k90) + return 0; + + switch (usage->hid & HID_USAGE) { + case CORSAIR_USAGE_MACRO_RECORD_START: + drvdata->k90->record_led.brightness = 1; + break; + case CORSAIR_USAGE_MACRO_RECORD_STOP: + drvdata->k90->record_led.brightness = 0; + break; + default: + break; + } + + return 0; +} + +static int corsair_input_mapping(struct hid_device *dev, + struct hid_input *input, + struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, + int *max) +{ + int gkey; + + gkey = corsair_usage_to_gkey(usage->hid & HID_USAGE); + if (gkey != 0) { + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_gkey_map[gkey - 1]); + return 1; + } + if ((usage->hid & HID_USAGE) >= CORSAIR_USAGE_SPECIAL_MIN && + (usage->hid & HID_USAGE) <= CORSAIR_USAGE_SPECIAL_MAX) { + switch (usage->hid & HID_USAGE) { + case CORSAIR_USAGE_MACRO_RECORD_START: + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_record_keycodes[0]); + return 1; + + case CORSAIR_USAGE_MACRO_RECORD_STOP: + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_record_keycodes[1]); + return 1; + + case CORSAIR_USAGE_M1: + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_profile_keycodes[0]); + return 1; + + case CORSAIR_USAGE_M2: + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_profile_keycodes[1]); + return 1; + + case CORSAIR_USAGE_M3: + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_profile_keycodes[2]); + return 1; + + default: + return -1; + } + } + + return 0; +} + +static const struct hid_device_id corsair_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90), + .driver_data = CORSAIR_USE_K90_MACRO | + CORSAIR_USE_K90_BACKLIGHT }, + {} +}; + +MODULE_DEVICE_TABLE(hid, corsair_devices); + +static struct hid_driver corsair_driver = { + .name = "corsair", + .id_table = corsair_devices, + .probe = corsair_probe, + .event = corsair_event, + .remove = corsair_remove, + .input_mapping = corsair_input_mapping, +}; + +static int __init corsair_init(void) +{ + return hid_register_driver(&corsair_driver); +} + +static void corsair_exit(void) +{ + hid_unregister_driver(&corsair_driver); +} + +module_init(corsair_init); +module_exit(corsair_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Clement Vuchener"); +MODULE_DESCRIPTION("HID driver for Corsair devices"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index f769208276ae..45a61e6d8691 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -251,6 +251,9 @@ #define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 #define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff +#define USB_VENDOR_ID_CORSAIR 0x1b1c +#define USB_DEVICE_ID_CORSAIR_K90 0x1b02 + #define USB_VENDOR_ID_CREATIVELABS 0x041e #define USB_DEVICE_ID_PRODIKEYS_PCMIDI 0x2801 From 937804f3c706970f83ca5f58483e0ca14ef19d84 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 30 Sep 2015 21:30:51 +0200 Subject: [PATCH 15/30] HID: corsair: boolify struct k90_led.removed 'removed' field of struct k90_led is in its nature bool, let's use it that way. Signed-off-by: Jiri Kosina --- drivers/hid/hid-corsair.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c index cd3429e4a75d..bcefb9ebb026 100644 --- a/drivers/hid/hid-corsair.c +++ b/drivers/hid/hid-corsair.c @@ -28,7 +28,7 @@ struct k90_led { struct led_classdev cdev; int brightness; struct work_struct work; - int removed; + bool removed; }; struct k90_drvdata { @@ -410,7 +410,7 @@ static int k90_init_backlight(struct hid_device *dev) } snprintf(name, name_sz, "%s" K90_BACKLIGHT_LED_SUFFIX, dev_name(&dev->dev)); - drvdata->backlight->removed = 0; + drvdata->backlight->removed = false; drvdata->backlight->cdev.name = name; drvdata->backlight->cdev.max_brightness = 3; drvdata->backlight->cdev.brightness_set = k90_brightness_set; @@ -455,7 +455,7 @@ static int k90_init_macro_functions(struct hid_device *dev) } snprintf(name, name_sz, "%s" K90_RECORD_LED_SUFFIX, dev_name(&dev->dev)); - k90->record_led.removed = 0; + k90->record_led.removed = false; k90->record_led.cdev.name = name; k90->record_led.cdev.max_brightness = 1; k90->record_led.cdev.brightness_set = k90_brightness_set; @@ -474,7 +474,7 @@ static int k90_init_macro_functions(struct hid_device *dev) return 0; fail_sysfs: - k90->record_led.removed = 1; + k90->record_led.removed = true; led_classdev_unregister(&k90->record_led.cdev); cancel_work_sync(&k90->record_led.work); fail_record_led: @@ -491,7 +491,7 @@ static void k90_cleanup_backlight(struct hid_device *dev) struct corsair_drvdata *drvdata = hid_get_drvdata(dev); if (drvdata->backlight) { - drvdata->backlight->removed = 1; + drvdata->backlight->removed = true; led_classdev_unregister(&drvdata->backlight->cdev); cancel_work_sync(&drvdata->backlight->work); kfree(drvdata->backlight->cdev.name); @@ -507,7 +507,7 @@ static void k90_cleanup_macro_functions(struct hid_device *dev) if (k90) { sysfs_remove_group(&dev->dev.kobj, &k90_attr_group); - k90->record_led.removed = 1; + k90->record_led.removed = true; led_classdev_unregister(&k90->record_led.cdev); cancel_work_sync(&k90->record_led.work); kfree(k90->record_led.cdev.name); From b6ad9a26e7c6fae74062baa9b8a7f583a803e092 Mon Sep 17 00:00:00 2001 From: Oliver Schmitt Date: Sat, 3 Oct 2015 18:20:02 +0200 Subject: [PATCH 16/30] HID: usbhid: Fix for the WiiU adapter from Mayflash The WiiU adapter from Mayflash (see http://www.mayflash.com/Products/NINTENDOWiiU/W009.html) is not working correctly. The "XInput" mode works fine, the controller is recognized as a xbox controller. But it is only possible to connect one controller with this method. In "DInput" mode the device is recognized as some kind of mouse input but no joystick is created. This commit will change this behavior with HID_QUIRK_MULTI_INPUT to split the device into 4 input devices so that it will also create joysticks in /dev/input/js*. Signed-off-by: Oliver Schmitt Reviewed-by: Felipe Balbi Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/usbhid/hid-quirks.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index f769208276ae..cfb317e66486 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -288,6 +288,7 @@ #define USB_DEVICE_ID_DMI_ENC 0x5fab #define USB_VENDOR_ID_DRAGONRISE 0x0079 +#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800 #define USB_VENDOR_ID_DWAV 0x0eef #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 1dff8f0015ba..26d2f83308c8 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -71,6 +71,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103, HID_QUIRK_ALWAYS_POLL }, From e8e88438008375b17561dcc6a22abc24decf04ac Mon Sep 17 00:00:00 2001 From: Ritesh Raj Sarraf Date: Mon, 5 Oct 2015 22:02:18 +0530 Subject: [PATCH 17/30] HID: sensor-hub: Add quirk for Lenovo Yoga 2 with ITE Chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch is a follow-up to 47eeca8a48 (" HID: sensor-hub: Add in quirk for Lenovo Yogas with ITE") The Lenovo Yoga 2 13 seems to be sold in multiple variants with minor difference3s. IN my case, the USB ID for ITE chip is different than the Yoga 2 11 and Yoga 3 14. Without the quirk, no data is received from the accelerometer. I have verified the patch, testing this on 4.3-rc4 (and 4.2 stable). With this patch, proper orientation data is received. rrs@learner:~/Community/UpstreamSources/linux-upstream_GIT (stable-42)$ monitor-sensor ** Message: Accelerometer orientation changed: bottom-up ** Message: Light changed: 0.000000 (lux) ±** Message: Accelerometer orientation changed: left-up ** Message: Accelerometer orientation changed: bottom-up ** Message: Accelerometer orientation changed: left-up ** Message: Accelerometer orientation changed: normal ** Message: Light changed: 29.999999 (lux) monitor-sensor can be found in the iio-sensor-proxy tool. Signed-off-by: Ritesh Raj Sarraf Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-sensor-hub.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index cfb317e66486..0b797adda2bc 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -511,6 +511,7 @@ #define USB_VENDOR_ID_ITE 0x048d #define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386 +#define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350 #define USB_VENDOR_ID_JABRA 0x0b0e #define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 51d51d7b2dad..90116ee1624e 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -791,6 +791,9 @@ static const struct hid_device_id sensor_hub_devices[] = { { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE_LENOVO_YOGA), .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE, + USB_DEVICE_ID_ITE_LENOVO_YOGA2), + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID, HID_ANY_ID) }, { } From 6d4f5440a3a2bb2e9d0d582bbf98234e9e9bb095 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 7 Oct 2015 15:33:43 +0300 Subject: [PATCH 18/30] HID: multitouch: Fetch feature reports on demand for Win8 devices Some newer Intel Skylake based Dell laptops with Win8 precision touchpad fail when initial feature reports are fetched from it. Below is an example output with some additional debug included: i2c_hid i2c-DLL0704:01: Fetching the HID descriptor i2c_hid i2c-DLL0704:01: __i2c_hid_command: cmd=20 00 i2c_hid i2c-DLL0704:01: HID Descriptor: 1e 00 00 01 99 02 21 00 24 ... ... i2c_hid i2c-DLL0704:01: i2c_hid_get_report i2c_hid i2c-DLL0704:01: __i2c_hid_command: cmd=22 00 38 02 23 00 i2c_hid i2c-DLL0704:01: report (len=4): 04 00 08 05 i2c_hid i2c-DLL0704:01: report id 13 i2c_hid i2c-DLL0704:01: i2c_hid_get_report i2c_hid i2c-DLL0704:01: __i2c_hid_command: cmd=22 00 3d 02 23 00 i2c_hid i2c-DLL0704:01: failed to retrieve report from device. i2c_hid i2c-DLL0704:01: report id 7 i2c_hid i2c-DLL0704:01: i2c_hid_get_report i2c_hid i2c-DLL0704:01: __i2c_hid_command: cmd=22 00 37 02 23 00 i2c_hid i2c-DLL0704:01: report (len=259): 03 01 07 fc 28 fe 84 40 ... i2c_hid i2c-DLL0704:01: report id 4 i2c_hid i2c-DLL0704:01: i2c_hid_get_report i2c_hid i2c-DLL0704:01: __i2c_hid_command: cmd=22 00 34 02 23 00 We manage to fetch few reports but then the touchpad dies: i2c_designware i2c_designware.1: i2c_dw_handle_tx_abort: lost arbitration i2c_hid i2c-DLL0704:01: failed to retrieve report from device. it eventually pulls the whole I2C bus low: i2c_designware i2c_designware.1: controller timed out i2c_hid i2c-DLL0704:01: failed to set a report to device. Fix this by preventing initial feature report retrieval for Win8 devices. Instead we fetch reports as needed in mt_feature_mapping(). This prevents fetching reports which might cause problems with the device in question. Suggested-by: Benjamin Tissoires Signed-off-by: Mika Westerberg Reviewed-by: Benjamin Tissoires Tested-by: Seth Forshee Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 45 +++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 661b4fce1a5d..dd64461e5e85 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -309,6 +309,41 @@ static struct attribute_group mt_attribute_group = { .attrs = sysfs_attrs }; +static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) +{ + struct mt_device *td = hid_get_drvdata(hdev); + int ret, size = hid_report_len(report); + u8 *buf; + + /* + * Only fetch the feature report if initial reports are not already + * been retrieved. Currently this is only done for Windows 8 touch + * devices. + */ + if (!(hdev->quirks & HID_QUIRK_NO_INIT_REPORTS)) + return; + if (td->mtclass.name != MT_CLS_WIN_8) + return; + + buf = hid_alloc_report_buf(report, GFP_KERNEL); + if (!buf) + return; + + ret = hid_hw_raw_request(hdev, report->id, buf, size, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret < 0) { + dev_warn(&hdev->dev, "failed to fetch feature %d\n", + report->id); + } else { + ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf, + size, 0); + if (ret) + dev_warn(&hdev->dev, "failed to report feature\n"); + } + + kfree(buf); +} + static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { @@ -327,6 +362,8 @@ static void mt_feature_mapping(struct hid_device *hdev, break; case HID_DG_CONTACTMAX: + mt_get_feature(hdev, field->report); + td->maxcontact_report_id = field->report->id; td->maxcontacts = field->value[0]; if (!td->maxcontacts && @@ -343,6 +380,7 @@ static void mt_feature_mapping(struct hid_device *hdev, break; } + mt_get_feature(hdev, field->report); if (field->value[usage->usage_index] == MT_BUTTONTYPE_CLICKPAD) td->is_buttonpad = true; @@ -1029,8 +1067,13 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) * reports. Fortunately, the Win8 spec says that all touches * should be sent during each report, making the initialization * of input reports unnecessary. + * + * In addition some touchpads do not behave well if we read + * all feature reports from them. Instead we prevent + * initial report fetching and then selectively fetch each + * report we are interested in. */ - hdev->quirks |= HID_QUIRK_NO_INIT_INPUT_REPORTS; + hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL); if (!td) { From f7acb55cf1b414f8f515697f2a7bb324ba009062 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 13 Oct 2015 10:03:49 -0700 Subject: [PATCH 19/30] HID: wacom: Add support for Cintiq Companion 2 Adds support for the EMR (pen+pad) and touchscreen devices used by the Wacom Cintiq Companion 2. This applies both to using the device as a standalone system, as well as when operating in "Cintiq mode" (where the EMR/touchscreen are simply exposed as USB devices to the system its connected to). Signed-off-by: Jason Gerecke Signed-off-by: Clifford Jolly Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 2 +- drivers/hid/wacom_wac.c | 36 +++++++++++++++++++++++++++++++++++- drivers/hid/wacom_wac.h | 1 + 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 530476dc7bb9..125e9d5dce38 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -422,7 +422,7 @@ static int wacom_query_tablet_data(struct hid_device *hdev, /* MT Tablet PC touch */ return wacom_set_device_mode(hdev, 3, 4, 4); } - else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID) { + else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID || features->type == CINTIQ_COMPANION_2) { return wacom_set_device_mode(hdev, 18, 3, 2); } else if (features->type == WACOM_27QHDT) { diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index a0d875c5ed38..01ff648d6098 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -765,7 +765,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom) /* general pen packet */ if ((data[1] & 0xb8) == 0xa0) { t = (data[6] << 2) | ((data[7] >> 6) & 3); - if (features->type >= INTUOS4S && features->type <= CINTIQ_HYBRID) { + if (features->type >= INTUOS4S && features->type <= CINTIQ_COMPANION_2) { t = (t << 1) | (data[1] & 1); } input_report_abs(input, ABS_PRESSURE, t); @@ -951,6 +951,27 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) } else { input_report_abs(input, ABS_MISC, 0); } + + } else if (features->type == CINTIQ_COMPANION_2) { + input_report_key(input, BTN_1, (data[1] & 0x02)); + input_report_key(input, BTN_2, (data[2] & 0x01)); + input_report_key(input, BTN_3, (data[2] & 0x02)); + input_report_key(input, BTN_4, (data[2] & 0x04)); + input_report_key(input, BTN_5, (data[2] & 0x08)); + input_report_key(input, BTN_6, (data[1] & 0x04)); + + input_report_key(input, BTN_7, (data[2] & 0x10)); /* Right */ + input_report_key(input, BTN_8, (data[2] & 0x20)); /* Up */ + input_report_key(input, BTN_9, (data[2] & 0x40)); /* Left */ + input_report_key(input, BTN_A, (data[2] & 0x80)); /* Down */ + input_report_key(input, BTN_0, (data[1] & 0x01)); /* Center */ + + if (data[4] | (data[3] & 0x01)) { + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_abs(input, ABS_MISC, 0); + } + } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) { int i; @@ -2276,6 +2297,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) case WACOM_27QHD: case DTK: case CINTIQ_HYBRID: + case CINTIQ_COMPANION_2: sync = wacom_intuos_irq(wacom_wac); break; @@ -2543,6 +2565,7 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, case CINTIQ: case WACOM_13HD: case CINTIQ_HYBRID: + case CINTIQ_COMPANION_2: input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); input_abs_set_res(input_dev, ABS_Z, 287); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); @@ -2783,6 +2806,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, switch (features->type) { case CINTIQ_HYBRID: + case CINTIQ_COMPANION_2: case DTK: case DTUS: case GRAPHIRE_BT: @@ -3356,6 +3380,14 @@ static const struct wacom_features wacom_features_0x318 = static const struct wacom_features wacom_features_0x319 = { "Wacom Wireless Bamboo PAD", 4095, 4095, /* Touch */ .type = BAMBOO_PAD, 35, 48, .touch_max = 4 }; +static const struct wacom_features wacom_features_0x325 = + { "Wacom ISDv5 325", 59552, 33848, 2047, 63, + CINTIQ_COMPANION_2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 11, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x326 }; +static const struct wacom_features wacom_features_0x326 = /* Touch */ + { "Wacom ISDv5 326", .type = HID_GENERIC, .oVid = USB_VENDOR_ID_WACOM, + .oPid = 0x325 }; static const struct wacom_features wacom_features_0x323 = { "Wacom Intuos P M", 21600, 13500, 1023, 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, @@ -3531,6 +3563,8 @@ const struct hid_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x318) }, { USB_DEVICE_WACOM(0x319) }, { USB_DEVICE_WACOM(0x323) }, + { USB_DEVICE_WACOM(0x325) }, + { USB_DEVICE_WACOM(0x326) }, { USB_DEVICE_WACOM(0x32A) }, { USB_DEVICE_WACOM(0x32B) }, { USB_DEVICE_WACOM(0x32C) }, diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 1a30e4407a46..95ee92b807e9 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -124,6 +124,7 @@ enum { WACOM_24HD, WACOM_27QHD, CINTIQ_HYBRID, + CINTIQ_COMPANION_2, CINTIQ, WACOM_BEE, WACOM_13HD, From 8d515fda8946cbfb359dca9d6bd416478f1b45cc Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Thu, 15 Oct 2015 10:17:06 -0700 Subject: [PATCH 20/30] HID: wacom: Report full pressure range for Intuos, Cintiq 13HD Touch The new Intuos tablets added in eda01da and the Cintiq 13HD Touch added in b4bf212 are capable of reporting 2048 levels of pressure. Although the kernel reports the correct range to userspace, an oversight has resulted in the driver ingoring the 11th pressure bit and only sending pressures of 0 through 1023. We could fix this issue by expanding the type check to include these devices, but it makes much more sense to just have the driver look at the device's maximum pressure when determining if it should read the 11th bit. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 01ff648d6098..7b3784826cce 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -765,7 +765,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom) /* general pen packet */ if ((data[1] & 0xb8) == 0xa0) { t = (data[6] << 2) | ((data[7] >> 6) & 3); - if (features->type >= INTUOS4S && features->type <= CINTIQ_COMPANION_2) { + if (features->pressure_max == 2047) { t = (t << 1) | (data[1] & 1); } input_report_abs(input, ABS_PRESSURE, t); From 8d2f8479da3c319218f030dc47382e55b67c2007 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sat, 17 Oct 2015 15:10:28 +0200 Subject: [PATCH 21/30] HID: roccat: Fixed resubmit: Deprecating most Roccat sysfs attributes Deprecates all Roccat sysfs attributes except the ones for the old Kone by moving abi descriptions from testing to obsolete. For most devices everything can be done using the hidraw ioctls HIDIOCGFEATURE and HIDIOCSFEATURE, so I would suggest future removal of device specific drivers. The userspace tools don't use these attributes for a year now. The first Kone is not fully HID-compliant and will still need a module. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../sysfs-driver-hid-roccat-arvo | 0 .../sysfs-driver-hid-roccat-isku | 0 .../obsolete/sysfs-driver-hid-roccat-koneplus | 99 ++++++++++++++++++- .../sysfs-driver-hid-roccat-konepure | 0 .../obsolete/sysfs-driver-hid-roccat-kovaplus | 50 ++++++++++ .../sysfs-driver-hid-roccat-lua | 0 .../ABI/obsolete/sysfs-driver-hid-roccat-pyra | 50 ++++++++++ .../sysfs-driver-hid-roccat-ryos | 0 .../sysfs-driver-hid-roccat-savu | 0 .../testing/sysfs-driver-hid-roccat-koneplus | 96 ------------------ .../testing/sysfs-driver-hid-roccat-kovaplus | 49 --------- .../ABI/testing/sysfs-driver-hid-roccat-pyra | 49 --------- 12 files changed, 198 insertions(+), 195 deletions(-) rename Documentation/ABI/{testing => obsolete}/sysfs-driver-hid-roccat-arvo (100%) rename Documentation/ABI/{testing => obsolete}/sysfs-driver-hid-roccat-isku (100%) rename Documentation/ABI/{testing => obsolete}/sysfs-driver-hid-roccat-konepure (100%) rename Documentation/ABI/{testing => obsolete}/sysfs-driver-hid-roccat-lua (100%) rename Documentation/ABI/{testing => obsolete}/sysfs-driver-hid-roccat-ryos (100%) rename Documentation/ABI/{testing => obsolete}/sysfs-driver-hid-roccat-savu (100%) delete mode 100644 Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus delete mode 100644 Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus delete mode 100644 Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-arvo similarity index 100% rename from Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo rename to Documentation/ABI/obsolete/sysfs-driver-hid-roccat-arvo diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-isku similarity index 100% rename from Documentation/ABI/testing/sysfs-driver-hid-roccat-isku rename to Documentation/ABI/obsolete/sysfs-driver-hid-roccat-isku diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus index 833fd59926a7..545e69f43229 100644 --- a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus @@ -1,3 +1,14 @@ +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/actual_profile +Date: October 2010 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 0-4. + When read, this attribute returns the number of the actual + profile. This value is persistent, so its equivalent to the + profile that's active when the mouse is powered on next time. + When written, this file sets the number of the startup profile + and the mouse activates this profile immediately. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/startup_profile Date: October 2010 Contact: Stefan Achatz @@ -22,6 +33,40 @@ Description: When read, this file returns the raw integer version number of the Please read binary attribute info which contains firmware version. Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/info +Date: November 2012 +Contact: Stefan Achatz +Description: When read, this file returns general data like firmware version. + When written, the device can be reset. + The data is 8 bytes long. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/macro +Date: October 2010 +Contact: Stefan Achatz +Description: The mouse can store a macro with max 500 key/button strokes + internally. + When written, this file lets one set the sequence for a specific + button for a specific profile. Button and profile numbers are + included in written data. The data has to be 2082 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile_buttons +Date: August 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds information about button layout. + When written, this file lets one write the respective profile + buttons back to the mouse. The data has to be 77 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile[1-5]_buttons Date: August 2010 Contact: Stefan Achatz @@ -34,6 +79,22 @@ Description: The mouse can store 5 profiles which can be switched by the Write control to select profile and read profile_buttons instead. Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile_settings +Date: October 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings holds information like resolution, sensitivity + and light effects. + When written, this file lets one write the respective profile + settings back to the mouse. The data has to be 43 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile[1-5]_settings Date: August 2010 Contact: Stefan Achatz @@ -45,4 +106,40 @@ Description: The mouse can store 5 profiles which can be switched by the The returned data is 43 bytes in size. This file is readonly. Write control to select profile and read profile_settings instead. -Users: http://roccat.sourceforge.net \ No newline at end of file +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/sensor +Date: October 2010 +Contact: Stefan Achatz +Description: The mouse has a tracking- and a distance-control-unit. These + can be activated/deactivated and the lift-off distance can be + set. The data has to be 6 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/talk +Date: May 2011 +Contact: Stefan Achatz +Description: Used to active some easy* functions of the mouse from outside. + The data has to be 16 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/tcu +Date: October 2010 +Contact: Stefan Achatz +Description: When written a calibration process for the tracking control unit + can be initiated/cancelled. Also lets one read/write sensor + registers. + The data has to be 4 bytes long. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/tcu_image +Date: October 2010 +Contact: Stefan Achatz +Description: When read the mouse returns a 30x30 pixel image of the + sampled underground. This works only in the course of a + calibration process initiated with tcu. + The returned data is 1028 bytes in size. + This file is readonly. +Users: http://roccat.sourceforge.net diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-konepure b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-konepure similarity index 100% rename from Documentation/ABI/testing/sysfs-driver-hid-roccat-konepure rename to Documentation/ABI/obsolete/sysfs-driver-hid-roccat-konepure diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus index 4a98e02b6c6a..ab01631e1e0f 100644 --- a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus @@ -8,6 +8,17 @@ Description: The integer value of this attribute ranges from 1-4. Has never been used. If bookkeeping is done, it's done in userland tools. Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_profile +Date: January 2011 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 0-4. + When read, this attribute returns the number of the active + profile. + When written, the mouse activates this profile immediately. + The profile that's active when powered down is the same that's + active when the mouse is powered on. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_sensitivity_x Date: January 2011 Contact: Stefan Achatz @@ -40,6 +51,29 @@ Description: When read, this file returns the raw integer version number of the Obsoleted by binary sysfs attribute "info". Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/info +Date: November 2012 +Contact: Stefan Achatz +Description: When read, this file returns general data like firmware version. + When written, the device can be reset. + The data is 6 bytes long. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_buttons +Date: January 2011 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds information about button layout. + When written, this file lets one write the respective profile + buttons back to the mouse. The data has to be 23 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile[1-5]_buttons Date: January 2011 Contact: Stefan Achatz @@ -52,6 +86,22 @@ Description: The mouse can store 5 profiles which can be switched by the Write control to select profile and read profile_buttons instead. Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_settings +Date: January 2011 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings holds information like resolution, sensitivity + and light effects. + When written, this file lets one write the respective profile + settings back to the mouse. The data has to be 16 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile[1-5]_settings Date: January 2011 Contact: Stefan Achatz diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-lua b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-lua similarity index 100% rename from Documentation/ABI/testing/sysfs-driver-hid-roccat-lua rename to Documentation/ABI/obsolete/sysfs-driver-hid-roccat-lua diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra index 87ac87e9556d..16020b31ae64 100644 --- a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra @@ -37,6 +37,29 @@ Description: When read, this file returns the raw integer version number of the Please use binary attribute "info" which provides this information. Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/info +Date: November 2012 +Contact: Stefan Achatz +Description: When read, this file returns general data like firmware version. + When written, the device can be reset. + The data is 6 bytes long. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile_buttons +Date: August 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds information about button layout. + When written, this file lets one write the respective profile + buttons back to the mouse. The data has to be 19 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile[1-5]_buttons Date: August 2010 Contact: Stefan Achatz @@ -49,6 +72,22 @@ Description: The mouse can store 5 profiles which can be switched by the Write control to select profile and read profile_buttons instead. Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile_settings +Date: August 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings holds information like resolution, sensitivity + and light effects. + When written, this file lets one write the respective profile + settings back to the mouse. The data has to be 13 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile[1-5]_settings Date: August 2010 Contact: Stefan Achatz @@ -62,6 +101,17 @@ Description: The mouse can store 5 profiles which can be switched by the Write control to select profile and read profile_settings instead. Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/settings +Date: August 2010 +Contact: Stefan Achatz +Description: When read, this file returns the settings stored in the mouse. + The size of the data is 3 bytes and holds information on the + startup_profile. + When written, this file lets write settings back to the mouse. + The data has to be 3 bytes long. The mouse will reject invalid + data. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/startup_profile Date: August 2010 Contact: Stefan Achatz diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-ryos b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-ryos similarity index 100% rename from Documentation/ABI/testing/sysfs-driver-hid-roccat-ryos rename to Documentation/ABI/obsolete/sysfs-driver-hid-roccat-ryos diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-savu similarity index 100% rename from Documentation/ABI/testing/sysfs-driver-hid-roccat-savu rename to Documentation/ABI/obsolete/sysfs-driver-hid-roccat-savu diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus deleted file mode 100644 index 7bd776f9c3c7..000000000000 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus +++ /dev/null @@ -1,96 +0,0 @@ -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/actual_profile -Date: October 2010 -Contact: Stefan Achatz -Description: The integer value of this attribute ranges from 0-4. - When read, this attribute returns the number of the actual - profile. This value is persistent, so its equivalent to the - profile that's active when the mouse is powered on next time. - When written, this file sets the number of the startup profile - and the mouse activates this profile immediately. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/info -Date: November 2012 -Contact: Stefan Achatz -Description: When read, this file returns general data like firmware version. - When written, the device can be reset. - The data is 8 bytes long. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/macro -Date: October 2010 -Contact: Stefan Achatz -Description: The mouse can store a macro with max 500 key/button strokes - internally. - When written, this file lets one set the sequence for a specific - button for a specific profile. Button and profile numbers are - included in written data. The data has to be 2082 bytes long. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile_buttons -Date: August 2010 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_buttons holds information about button layout. - When written, this file lets one write the respective profile - buttons back to the mouse. The data has to be 77 bytes long. - The mouse will reject invalid data. - Which profile to write is determined by the profile number - contained in the data. - Before reading this file, control has to be written to select - which profile to read. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile_settings -Date: October 2010 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_settings holds information like resolution, sensitivity - and light effects. - When written, this file lets one write the respective profile - settings back to the mouse. The data has to be 43 bytes long. - The mouse will reject invalid data. - Which profile to write is determined by the profile number - contained in the data. - Before reading this file, control has to be written to select - which profile to read. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/sensor -Date: October 2010 -Contact: Stefan Achatz -Description: The mouse has a tracking- and a distance-control-unit. These - can be activated/deactivated and the lift-off distance can be - set. The data has to be 6 bytes long. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/talk -Date: May 2011 -Contact: Stefan Achatz -Description: Used to active some easy* functions of the mouse from outside. - The data has to be 16 bytes long. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/tcu -Date: October 2010 -Contact: Stefan Achatz -Description: When written a calibration process for the tracking control unit - can be initiated/cancelled. Also lets one read/write sensor - registers. - The data has to be 4 bytes long. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/tcu_image -Date: October 2010 -Contact: Stefan Achatz -Description: When read the mouse returns a 30x30 pixel image of the - sampled underground. This works only in the course of a - calibration process initiated with tcu. - The returned data is 1028 bytes in size. - This file is readonly. -Users: http://roccat.sourceforge.net diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus deleted file mode 100644 index a10404f15a54..000000000000 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus +++ /dev/null @@ -1,49 +0,0 @@ -What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_profile -Date: January 2011 -Contact: Stefan Achatz -Description: The integer value of this attribute ranges from 0-4. - When read, this attribute returns the number of the active - profile. - When written, the mouse activates this profile immediately. - The profile that's active when powered down is the same that's - active when the mouse is powered on. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/info -Date: November 2012 -Contact: Stefan Achatz -Description: When read, this file returns general data like firmware version. - When written, the device can be reset. - The data is 6 bytes long. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_buttons -Date: January 2011 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_buttons holds information about button layout. - When written, this file lets one write the respective profile - buttons back to the mouse. The data has to be 23 bytes long. - The mouse will reject invalid data. - Which profile to write is determined by the profile number - contained in the data. - Before reading this file, control has to be written to select - which profile to read. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_settings -Date: January 2011 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_settings holds information like resolution, sensitivity - and light effects. - When written, this file lets one write the respective profile - settings back to the mouse. The data has to be 16 bytes long. - The mouse will reject invalid data. - Which profile to write is determined by the profile number - contained in the data. - Before reading this file, control has to be written to select - which profile to read. -Users: http://roccat.sourceforge.net diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra deleted file mode 100644 index 9fa9de30d14b..000000000000 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra +++ /dev/null @@ -1,49 +0,0 @@ -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/info -Date: November 2012 -Contact: Stefan Achatz -Description: When read, this file returns general data like firmware version. - When written, the device can be reset. - The data is 6 bytes long. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile_settings -Date: August 2010 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_settings holds information like resolution, sensitivity - and light effects. - When written, this file lets one write the respective profile - settings back to the mouse. The data has to be 13 bytes long. - The mouse will reject invalid data. - Which profile to write is determined by the profile number - contained in the data. - Before reading this file, control has to be written to select - which profile to read. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile_buttons -Date: August 2010 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_buttons holds information about button layout. - When written, this file lets one write the respective profile - buttons back to the mouse. The data has to be 19 bytes long. - The mouse will reject invalid data. - Which profile to write is determined by the profile number - contained in the data. - Before reading this file, control has to be written to select - which profile to read. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/settings -Date: August 2010 -Contact: Stefan Achatz -Description: When read, this file returns the settings stored in the mouse. - The size of the data is 3 bytes and holds information on the - startup_profile. - When written, this file lets write settings back to the mouse. - The data has to be 3 bytes long. The mouse will reject invalid - data. -Users: http://roccat.sourceforge.net From 499522c8c015de995aabce3d0f0bf4b9b17f44c3 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 7 Oct 2015 16:54:21 -0700 Subject: [PATCH 22/30] HID: wacom: Tie cached HID_DG_CONTACTCOUNT indices to report ID The cached indicies 'cc_index' and 'cc_value_index' introduced in 1b5d514 are only valid for a single report ID. If a touchscreen has multiple reports with a HID_DG_CONTACTCOUNT usage, its possible that the values will not be correct for the report we're handling, resulting in an incorrect value for 'num_expected'. This has been observed with the Cintiq Companion 2. To address this, we store the ID of the report those indicies are valid for in a new 'cc_report' variable. Before using them to get the expected contact count, we first check if the ID of the report we're processing matches 'cc_report'. If it doesn't, we update the indicies to point to the HID_DG_CONTACTCOUNT usage of the current report (if it has one). Cc: stable@vger.kernel.org Signed-off-by: Jason Gerecke Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 26 ++++++++++++++++++++++++++ drivers/hid/wacom_wac.h | 1 + 2 files changed, 27 insertions(+) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 0215ab62bb93..9c3b14496c97 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1628,6 +1628,7 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0); break; case HID_DG_CONTACTCOUNT: + wacom_wac->hid_data.cc_report = field->report->id; wacom_wac->hid_data.cc_index = field->index; wacom_wac->hid_data.cc_value_index = usage->usage_index; break; @@ -1715,6 +1716,31 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev, struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct hid_data* hid_data = &wacom_wac->hid_data; + if (hid_data->cc_report != 0 && + hid_data->cc_report != report->id) { + int i; + + hid_data->cc_report = report->id; + hid_data->cc_index = -1; + hid_data->cc_value_index = -1; + + for (i = 0; i < report->maxfield; i++) { + struct hid_field *field = report->field[i]; + int j; + + for (j = 0; j < field->maxusage; j++) { + if (field->usage[j].hid == HID_DG_CONTACTCOUNT) { + hid_data->cc_index = i; + hid_data->cc_value_index = j; + + /* break */ + i = report->maxfield; + j = field->maxusage; + } + } + } + } + if (hid_data->cc_index >= 0) { struct hid_field *field = report->field[hid_data->cc_index]; int value = field->value[hid_data->cc_value_index]; diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 1e270d401e18..809c03e34f74 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -198,6 +198,7 @@ struct hid_data { int width; int height; int id; + int cc_report; int cc_index; int cc_value_index; int num_expected; From df7079380554e6e8e13a0812c7e6c72f669aba5c Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 7 Oct 2015 16:54:22 -0700 Subject: [PATCH 23/30] HID: wacom: Expect 'touch_max' touches if HID_DG_CONTACTCOUNT not present When introduced in commit 1b5d514, the check 'if (hid_data->cc_index >= 0)' in 'wacom_wac_finger_pre_report' was intended to switch where the driver got the expected number of contacts from: HID_DG_CONTACTCOUNT if the usage was present, or 'touch_max' otherwise. Unfortunately, an oversight worthy of a brown paper bag (specifically, that 'cc_index' could never be negative) meant that the latter 'else' clause would never be entered. The patch prior to this one introduced a way for 'cc_index' to be negative, but only if HID_DG_CONTACTCOUNT is present in some report _other_ than the one being processed. To ensure the 'else' clause is also entered for devices which don't have HID_DG_CONTACTCOUNT on _any_ report, we add the additional constraint that 'cc_report' be non-zero (which is true only if the usage is present in some report). Cc: stable@vger.kernel.org Signed-off-by: Jason Gerecke Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 9c3b14496c97..cba008ac9cff 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1740,8 +1740,8 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev, } } } - - if (hid_data->cc_index >= 0) { + if (hid_data->cc_report != 0 && + hid_data->cc_index >= 0) { struct hid_field *field = report->field[hid_data->cc_index]; int value = field->value[hid_data->cc_value_index]; if (value) From 636a89d43e39edacc473c722879f10527acfc95c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 11 Oct 2015 16:18:22 +0200 Subject: [PATCH 24/30] HID: fix some indenting issues Some drivers indent some lines in a very weird manner. Fix that. Signed-off-by: Jiri Slaby Reviewed-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/hid-aureal.c | 3 ++- drivers/hid/hid-elecom.c | 2 +- drivers/hid/hid-prodikeys.c | 4 ++-- drivers/hid/hid-sensor-hub.c | 8 ++++---- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-aureal.c b/drivers/hid/hid-aureal.c index 340ba9d394a0..3280aff28e90 100644 --- a/drivers/hid/hid-aureal.c +++ b/drivers/hid/hid-aureal.c @@ -23,7 +23,8 @@ static __u8 *aureal_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (*rsize >= 54 && rdesc[52] == 0x25 && rdesc[53] == 0x01) { dev_info(&hdev->dev, "fixing Aureal Cy se W-01RN USB_V3.1 report descriptor.\n"); rdesc[53] = 0x65; - } return rdesc; + } + return rdesc; } static const struct hid_device_id aureal_devices[] = { diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c index d0bd13b62dc2..6e3848a8d8dd 100644 --- a/drivers/hid/hid-elecom.c +++ b/drivers/hid/hid-elecom.c @@ -27,7 +27,7 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, hid_info(hdev, "Fixing up Elecom BM084 report descriptor\n"); rdesc[47] = 0x00; } - return rdesc; + return rdesc; } static const struct hid_device_id elecom_devices[] = { diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c index e3e98ccf137b..3a207c0ac0e3 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c @@ -427,7 +427,7 @@ static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data) pm->midi_octave = 2; dbg_hid("pcmidi mode: %d octave: %d\n", pm->midi_mode, pm->midi_octave); - continue; + continue; } else key = KEY_MESSENGER; break; @@ -695,7 +695,7 @@ static int pcmidi_snd_initialise(struct pcmidi_snd *pm) if (err < 0) { pk_error("failed to register pc-midi sound card: error %d\n", err); - goto fail_register; + goto fail_register; } dbg_hid("pcmidi_snd_initialise finished ok\n"); diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index a76eb2a0a987..5f6945dd3db0 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -646,8 +646,8 @@ static int sensor_hub_probe(struct hid_device *hdev, GFP_KERNEL); if (sd->hid_sensor_hub_client_devs == NULL) { hid_err(hdev, "Failed to allocate memory for mfd cells\n"); - ret = -ENOMEM; - goto err_stop_hw; + ret = -ENOMEM; + goto err_stop_hw; } for (i = 0; i < hdev->maxcollection; ++i) { @@ -684,8 +684,8 @@ static int sensor_hub_probe(struct hid_device *hdev, collection->usage); if (name == NULL) { hid_err(hdev, "Failed MFD device name\n"); - ret = -ENOMEM; - goto err_stop_hw; + ret = -ENOMEM; + goto err_stop_hw; } sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].name = name; From 7adb91bd78a5e00209c56068dc0ad53d4cf4348f Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Mon, 26 Oct 2015 14:29:12 -0700 Subject: [PATCH 25/30] HID: wacom: fix Intuos wireless report id issue Intuos Pen in wireless mode does not have the same report id (2) as when it is in USB mode (17). This patch also moves WIRELESS next to REMOTE in type enum so we can group devices with similar features easily. Reported-by: Dale Brewe Tested-by: Dale Brewe Signed-off-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 2 +- drivers/hid/wacom_wac.c | 1 - drivers/hid/wacom_wac.h | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 125e9d5dce38..b4de3256aa90 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -432,7 +432,7 @@ static int wacom_query_tablet_data(struct hid_device *hdev, return wacom_set_device_mode(hdev, 2, 2, 2); } } else if (features->device_type & WACOM_DEVICETYPE_PEN) { - if (features->type <= BAMBOO_PT && features->type != WIRELESS) { + if (features->type <= BAMBOO_PT) { return wacom_set_device_mode(hdev, 2, 2, 2); } } diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 7b3784826cce..453da6d5c583 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2066,7 +2066,6 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) struct wacom_features *features = &wacom->features; if ((features->type == INTUOSHT2) && - (wacom->data[0] == WACOM_REPORT_INTUOS_PEN) && (features->device_type & WACOM_DEVICETYPE_PEN)) return wacom_intuos_irq(wacom); else if (len == WACOM_PKGLEN_BBTOUCH) diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 95ee92b807e9..d9e9515fd149 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -129,7 +129,6 @@ enum { WACOM_BEE, WACOM_13HD, WACOM_MO, - WIRELESS, BAMBOO_PEN, INTUOSHT, INTUOSHT2, @@ -138,6 +137,7 @@ enum { WACOM_24HDT, WACOM_27QHDT, BAMBOO_PAD, + WIRELESS, REMOTE, TABLETPC, /* add new TPC below */ TABLETPCE, From 6212aae0d4de273e8dbc9f889d0c7ef8317284df Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Mon, 2 Nov 2015 15:41:12 -0800 Subject: [PATCH 26/30] HID: wacom: Remove useless conditions from 'wacom_query_tablet_data' When support for the Cintiq Companion Hybrid and Cintiq Companion 2 was added (36d3c51 and f7acb55), the 'wacom_query_tablet_data' function was updated to include references to CINTIQ_HYBRID and CINTIQ_COMPANION_2 with the thought that they were necessary to switch the touch interface into the proper mode. This is unnecessary, however, since those types are only ever associated with the pen interface -- the touch interfaces are either CINTIQ_24HDT or HID_GENERIC. To avoid confusion in the future, we remove the unnecessary CINTIQ_HYBRID and CINTIQ_COMPANION_2 conditions. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index b4de3256aa90..abb7fdf05d92 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -422,7 +422,7 @@ static int wacom_query_tablet_data(struct hid_device *hdev, /* MT Tablet PC touch */ return wacom_set_device_mode(hdev, 3, 4, 4); } - else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID || features->type == CINTIQ_COMPANION_2) { + else if (features->type == WACOM_24HDT) { return wacom_set_device_mode(hdev, 18, 3, 2); } else if (features->type == WACOM_27QHDT) { From 8253d42be8ae8c81cabcf678448c09a69d4f3b8c Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Mon, 2 Nov 2015 15:41:13 -0800 Subject: [PATCH 27/30] HID: wacom: Fix ABS_MISC reporting for Cintiq Companion 2 The pad handling code introduced for the Cintiq Companion 2 (f7acb55) looks at the wrong bytes in the report when deciding whether ABS_MISC should be sent. This does not cause any issues with the X driver now that the pen and pad have been split to separate devices, but is incorrect and has caused issues when backporting to distros with pre- 3.17 kernels. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 453da6d5c583..9db6a8b1150c 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -966,7 +966,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) input_report_key(input, BTN_A, (data[2] & 0x80)); /* Down */ input_report_key(input, BTN_0, (data[1] & 0x01)); /* Center */ - if (data[4] | (data[3] & 0x01)) { + if (data[2] | (data[1] & 0x07)) { input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); } else { input_report_abs(input, ABS_MISC, 0); From 86e88f0e7068e37d9774eb9d93515c44ffa74006 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 3 Nov 2015 16:57:58 -0800 Subject: [PATCH 28/30] HID: wacom: Call 'wacom_query_tablet_data' only after 'hid_hw_start' When connecting the Cintiq Companion 2 as an external tablet (i.e., using it in "hybrid" mode) it has been seen to cause the kernel of the machine it is connected to to Oops. The cause has been traced to us attempting to switch the tablet's mode prior to actually starting HID device (resulting in the eventual dereference of the uninitialized control URB). Commit 3b164a0 moved the mode switch from occuring post-start to occurring pre-start. The change was not seen to cause issues largely due to the fact that most devices mode switch with 'hid_hw_raw_request' (which is safe to call prior to start) rather than 'hid_hw_request'. Moving the call back to its original location resolves the issue, but causes some touch-only Bamboo tablets (e.g. 056a:00d0) to stop working. The affected tablets require us to perform a mode switch on their vestigial pen interface prior ignoring with -ENODEV, meaning that the code which is responsible for doing the ignoring has to move as well. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index abb7fdf05d92..e06af5b9f59e 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -1778,24 +1778,6 @@ static int wacom_probe(struct hid_device *hdev, features->device_type |= WACOM_DEVICETYPE_PEN; } - /* Note that if query fails it is not a hard failure */ - wacom_query_tablet_data(hdev, features); - - /* touch only Bamboo doesn't support pen */ - if ((features->type == BAMBOO_TOUCH) && - (features->device_type & WACOM_DEVICETYPE_PEN)) { - error = -ENODEV; - goto fail_shared_data; - } - - /* pen only Bamboo neither support touch nor pad */ - if ((features->type == BAMBOO_PEN) && - ((features->device_type & WACOM_DEVICETYPE_TOUCH) || - (features->device_type & WACOM_DEVICETYPE_PAD))) { - error = -ENODEV; - goto fail_shared_data; - } - wacom_calculate_res(features); wacom_update_name(wacom); @@ -1833,6 +1815,24 @@ static int wacom_probe(struct hid_device *hdev, goto fail_hw_start; } + /* Note that if query fails it is not a hard failure */ + wacom_query_tablet_data(hdev, features); + + /* touch only Bamboo doesn't support pen */ + if ((features->type == BAMBOO_TOUCH) && + (features->device_type & WACOM_DEVICETYPE_PEN)) { + error = -ENODEV; + goto fail_hw_start; + } + + /* pen only Bamboo neither support touch nor pad */ + if ((features->type == BAMBOO_PEN) && + ((features->device_type & WACOM_DEVICETYPE_TOUCH) || + (features->device_type & WACOM_DEVICETYPE_PAD))) { + error = -ENODEV; + goto fail_hw_start; + } + if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) error = hid_hw_open(hdev); From bbec1bd0faa211a0a0abaf947cd4a236d080ad28 Mon Sep 17 00:00:00 2001 From: Simon Wood Date: Mon, 2 Nov 2015 07:56:51 -0700 Subject: [PATCH 29/30] HID: logitech: Simplify wheel detection scheme Simplfy how hid-logitech driver detects the native mode of the wheel, done by looking at the USB-ID revision and comparing bit mask. Signed-off-by: Simon Wood Signed-off-by: Jiri Kosina --- drivers/hid/hid-lg4ff.c | 70 +++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 02cec83caac3..b363d88267c2 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -114,16 +114,12 @@ struct lg4ff_compat_mode_switch { }; struct lg4ff_wheel_ident_info { + const u32 modes; const u16 mask; const u16 result; const u16 real_product_id; }; -struct lg4ff_wheel_ident_checklist { - const u32 count; - const struct lg4ff_wheel_ident_info *models[]; -}; - struct lg4ff_multimode_wheel { const u16 product_id; const u32 alternate_modes; @@ -174,36 +170,39 @@ static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = { /* Multimode wheel identificators */ static const struct lg4ff_wheel_ident_info lg4ff_dfp_ident_info = { + LG4FF_MODE_DFP | LG4FF_MODE_DFEX, 0xf000, 0x1000, USB_DEVICE_ID_LOGITECH_DFP_WHEEL }; static const struct lg4ff_wheel_ident_info lg4ff_g25_ident_info = { + LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, 0xff00, 0x1200, USB_DEVICE_ID_LOGITECH_G25_WHEEL }; static const struct lg4ff_wheel_ident_info lg4ff_g27_ident_info = { + LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, 0xfff0, 0x1230, USB_DEVICE_ID_LOGITECH_G27_WHEEL }; static const struct lg4ff_wheel_ident_info lg4ff_dfgt_ident_info = { + LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, 0xff00, 0x1300, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL }; /* Multimode wheel identification checklists */ -static const struct lg4ff_wheel_ident_checklist lg4ff_main_checklist = { - 4, - {&lg4ff_dfgt_ident_info, - &lg4ff_g27_ident_info, - &lg4ff_g25_ident_info, - &lg4ff_dfp_ident_info} +static const struct lg4ff_wheel_ident_info *lg4ff_main_checklist[] = { + &lg4ff_dfgt_ident_info, + &lg4ff_g27_ident_info, + &lg4ff_g25_ident_info, + &lg4ff_dfp_ident_info }; /* Compatibility mode switching commands */ @@ -1037,41 +1036,28 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde static u16 lg4ff_identify_multimode_wheel(struct hid_device *hid, const u16 reported_product_id, const u16 bcdDevice) { - const struct lg4ff_wheel_ident_checklist *checklist; - int i, from_idx, to_idx; + u32 current_mode; + int i; - switch (reported_product_id) { - case USB_DEVICE_ID_LOGITECH_WHEEL: - case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: - checklist = &lg4ff_main_checklist; - from_idx = 0; - to_idx = checklist->count - 1; - break; - case USB_DEVICE_ID_LOGITECH_G25_WHEEL: - checklist = &lg4ff_main_checklist; - from_idx = 0; - to_idx = checklist->count - 2; /* End identity check at G25 */ - break; - case USB_DEVICE_ID_LOGITECH_G27_WHEEL: - checklist = &lg4ff_main_checklist; - from_idx = 1; /* Start identity check at G27 */ - to_idx = checklist->count - 3; /* End identity check at G27 */ - break; - case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: - checklist = &lg4ff_main_checklist; - from_idx = 0; - to_idx = checklist->count - 4; /* End identity check at DFGT */ - break; - default: - return 0; + /* identify current mode from USB PID */ + for (i = 1; i < ARRAY_SIZE(lg4ff_alternate_modes); i++) { + dbg_hid("Testing whether PID is %X\n", lg4ff_alternate_modes[i].product_id); + if (reported_product_id == lg4ff_alternate_modes[i].product_id) + break; } - for (i = from_idx; i <= to_idx; i++) { - const u16 mask = checklist->models[i]->mask; - const u16 result = checklist->models[i]->result; - const u16 real_product_id = checklist->models[i]->real_product_id; + if (i == ARRAY_SIZE(lg4ff_alternate_modes)) + return 0; - if ((bcdDevice & mask) == result) { + current_mode = BIT(i); + + for (i = 0; i < ARRAY_SIZE(lg4ff_main_checklist); i++) { + const u16 mask = lg4ff_main_checklist[i]->mask; + const u16 result = lg4ff_main_checklist[i]->result; + const u16 real_product_id = lg4ff_main_checklist[i]->real_product_id; + + if ((current_mode & lg4ff_main_checklist[i]->modes) && \ + (bcdDevice & mask) == result) { dbg_hid("Found wheel with real PID %X whose reported PID is %X\n", real_product_id, reported_product_id); return real_product_id; } From 29fae1c85166ef525b8b6518e749295e0c9d1e20 Mon Sep 17 00:00:00 2001 From: Simon Wood Date: Mon, 2 Nov 2015 07:56:52 -0700 Subject: [PATCH 30/30] HID: logitech: Add support for G29 At present the G29 is mis-identified as a DFGT, this patch ensures that the wheel is correctly detected and allows setting the LEDs and turning range via the '/sys' interface. This wheel can also emulate other types of Logitech wheels. Signed-off-by: Simon Wood Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-lg.c | 9 +++++++ drivers/hid/hid-lg4ff.c | 57 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bcd914a63af2..1ec2bea39d9a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1896,6 +1896,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 5332fb7d072a..c20ac76c0a8c 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -620,6 +620,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, usage->code == ABS_Y || usage->code == ABS_Z || usage->code == ABS_RZ)) { switch (hdev->product) { + case USB_DEVICE_ID_LOGITECH_G29_WHEEL: case USB_DEVICE_ID_LOGITECH_WHEEL: case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: @@ -658,10 +659,18 @@ static int lg_event(struct hid_device *hdev, struct hid_field *field, static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) { + struct usb_interface *iface = to_usb_interface(hdev->dev.parent); + __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; unsigned int connect_mask = HID_CONNECT_DEFAULT; struct lg_drv_data *drv_data; int ret; + /* Only work with the 1st interface (G29 presents multiple) */ + if (iface_num != 0) { + dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num); + return -ENODEV; + } + drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL); if (!drv_data) { hid_err(hdev, "Insufficient memory, cannot allocate driver data\n"); diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index b363d88267c2..fbddcb37ae98 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -45,7 +45,8 @@ #define LG4FF_MODE_G25_IDX 3 #define LG4FF_MODE_DFGT_IDX 4 #define LG4FF_MODE_G27_IDX 5 -#define LG4FF_MODE_MAX_IDX 6 +#define LG4FF_MODE_G29_IDX 6 +#define LG4FF_MODE_MAX_IDX 7 #define LG4FF_MODE_NATIVE BIT(LG4FF_MODE_NATIVE_IDX) #define LG4FF_MODE_DFEX BIT(LG4FF_MODE_DFEX_IDX) @@ -53,6 +54,7 @@ #define LG4FF_MODE_G25 BIT(LG4FF_MODE_G25_IDX) #define LG4FF_MODE_DFGT BIT(LG4FF_MODE_DFGT_IDX) #define LG4FF_MODE_G27 BIT(LG4FF_MODE_G27_IDX) +#define LG4FF_MODE_G29 BIT(LG4FF_MODE_G29_IDX) #define LG4FF_DFEX_TAG "DF-EX" #define LG4FF_DFEX_NAME "Driving Force / Formula EX" @@ -62,6 +64,8 @@ #define LG4FF_G25_NAME "G25 Racing Wheel" #define LG4FF_G27_TAG "G27" #define LG4FF_G27_NAME "G27 Racing Wheel" +#define LG4FF_G29_TAG "G29" +#define LG4FF_G29_NAME "G29 Racing Wheel" #define LG4FF_DFGT_TAG "DFGT" #define LG4FF_DFGT_NAME "Driving Force GT" @@ -140,6 +144,7 @@ static const struct lg4ff_wheel lg4ff_devices[] = { {USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, {USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, + {USB_DEVICE_ID_LOGITECH_G29_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL}, {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL} }; @@ -157,6 +162,9 @@ static const struct lg4ff_multimode_wheel lg4ff_multimode_wheels[] = { {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_MODE_NATIVE | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, LG4FF_G27_TAG, LG4FF_G27_NAME}, + {USB_DEVICE_ID_LOGITECH_G29_WHEEL, + LG4FF_MODE_NATIVE | LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, + LG4FF_G29_TAG, LG4FF_G29_NAME}, }; static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = { @@ -165,7 +173,8 @@ static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = { [LG4FF_MODE_DFP_IDX] = {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, LG4FF_DFP_TAG, LG4FF_DFP_NAME}, [LG4FF_MODE_G25_IDX] = {USB_DEVICE_ID_LOGITECH_G25_WHEEL, LG4FF_G25_TAG, LG4FF_G25_NAME}, [LG4FF_MODE_DFGT_IDX] = {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, LG4FF_DFGT_TAG, LG4FF_DFGT_NAME}, - [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME} + [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME}, + [LG4FF_MODE_G29_IDX] = {USB_DEVICE_ID_LOGITECH_G29_WHEEL, LG4FF_G29_TAG, LG4FF_G29_NAME}, }; /* Multimode wheel identificators */ @@ -197,8 +206,24 @@ static const struct lg4ff_wheel_ident_info lg4ff_dfgt_ident_info = { USB_DEVICE_ID_LOGITECH_DFGT_WHEEL }; +static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info = { + LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, + 0xfff8, + 0x1350, + USB_DEVICE_ID_LOGITECH_G29_WHEEL +}; + +static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info2 = { + LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, + 0xff00, + 0x8900, + USB_DEVICE_ID_LOGITECH_G29_WHEEL +}; + /* Multimode wheel identification checklists */ static const struct lg4ff_wheel_ident_info *lg4ff_main_checklist[] = { + &lg4ff_g29_ident_info, + &lg4ff_g29_ident_info2, &lg4ff_dfgt_ident_info, &lg4ff_g27_ident_info, &lg4ff_g25_ident_info, @@ -237,6 +262,12 @@ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g27 = { 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G27 with detach */ }; +static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g29 = { + 2, + {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */ + 0xf8, 0x09, 0x05, 0x01, 0x01, 0x00, 0x00} /* Switch mode to G29 with detach */ +}; + /* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext01_dfp = { 1, @@ -650,6 +681,23 @@ static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(cons return NULL; } break; + case USB_DEVICE_ID_LOGITECH_G29_WHEEL: + switch (target_product_id) { + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: + return &lg4ff_mode_switch_ext09_dfp; + case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: + return &lg4ff_mode_switch_ext09_dfgt; + case USB_DEVICE_ID_LOGITECH_G25_WHEEL: + return &lg4ff_mode_switch_ext09_g25; + case USB_DEVICE_ID_LOGITECH_G27_WHEEL: + return &lg4ff_mode_switch_ext09_g27; + case USB_DEVICE_ID_LOGITECH_G29_WHEEL: + return &lg4ff_mode_switch_ext09_g29; + /* G29 can only be switched to DF-EX, DFP, DFGT, G25, G27 or its native mode */ + default: + return NULL; + } + break; case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: switch (target_product_id) { case USB_DEVICE_ID_LOGITECH_WHEEL: @@ -1232,12 +1280,13 @@ int lg4ff_init(struct hid_device *hid) entry->wdata.set_range(hid, entry->wdata.range); #ifdef CONFIG_LEDS_CLASS - /* register led subsystem - G27 only */ + /* register led subsystem - G27/G29 only */ entry->wdata.led_state = 0; for (j = 0; j < 5; j++) entry->wdata.led[j] = NULL; - if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL) { + if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL || + lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G29_WHEEL) { struct led_classdev *led; size_t name_sz; char *name;