From e1bc84d0071f59c8b38232e2cb093c47c47e4f9f Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Fri, 7 Oct 2016 12:39:34 -0700 Subject: HID: sony: Fix race condition in sony_probe Early on the sony_probe function calls hid_hw_start to start the hardware. Afterwards it issues some hardware requests, initializes other functionality like Force Feedback, power classes and others. However by the time hid_hw_start returns, the device nodes have already been created, which leads to a race condition by user space applications which may detect the device prior to completion of initialization. We have observed this problem many times, this patch fixes the problem. This patch moves most of sony_probe to sony_input_configured, which is called prior to device registration. This fixes the race condition and the same approach is used in other HID drivers. Signed-off-by: Roderick Colenbrander Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 117 ++++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 59 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index b0bb99a821bd..afa82198defb 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1387,28 +1387,6 @@ static int sony_register_touchpad(struct hid_input *hi, int touch_count, return 0; } -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) { - ret = sony_register_touchpad(hidinput, 2, 1920, 942); - if (ret) { - hid_err(sc->hdev, - "Unable to initialize multi-touch slots: %d\n", - ret); - return ret; - } - } - - return 0; -} /* * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller @@ -2329,45 +2307,12 @@ static inline void sony_cancel_work_sync(struct sony_sc *sc) cancel_work_sync(&sc->state_worker); } -static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) +static int sony_input_configured(struct hid_device *hdev, + struct hid_input *hidinput) { - int ret; + struct sony_sc *sc = hid_get_drvdata(hdev); int append_dev_id; - unsigned long quirks = id->driver_data; - struct sony_sc *sc; - unsigned int connect_mask = HID_CONNECT_DEFAULT; - - if (!strcmp(hdev->name, "FutureMax Dance Mat")) - quirks |= FUTUREMAX_DANCE_MAT; - - sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); - if (sc == NULL) { - hid_err(hdev, "can't alloc sony descriptor\n"); - return -ENOMEM; - } - - spin_lock_init(&sc->lock); - - sc->quirks = quirks; - hid_set_drvdata(hdev, sc); - sc->hdev = hdev; - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "parse failed\n"); - return ret; - } - - if (sc->quirks & VAIO_RDESC_CONSTANT) - connect_mask |= HID_CONNECT_HIDDEV_FORCE; - else if (sc->quirks & SIXAXIS_CONTROLLER) - connect_mask |= HID_CONNECT_HIDDEV_FORCE; - - ret = hid_hw_start(hdev, connect_mask); - if (ret) { - hid_err(hdev, "hw start failed\n"); - return ret; - } + int ret; ret = sony_set_device_id(sc); if (ret < 0) { @@ -2427,6 +2372,18 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) } } + /* + * The Dualshock 4 touchpad supports 2 touches and has a + * resolution of 1920x942 (44.86 dots/mm). + */ + ret = sony_register_touchpad(hidinput, 2, 1920, 942); + if (ret) { + hid_err(sc->hdev, + "Unable to initialize multi-touch slots: %d\n", + ret); + return ret; + } + sony_init_output_report(sc, dualshock4_send_output_report); } else if (sc->quirks & MOTION_CONTROLLER) { sony_init_output_report(sc, motion_send_output_report); @@ -2482,6 +2439,48 @@ err_stop: return ret; } +static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + unsigned long quirks = id->driver_data; + struct sony_sc *sc; + unsigned int connect_mask = HID_CONNECT_DEFAULT; + + if (!strcmp(hdev->name, "FutureMax Dance Mat")) + quirks |= FUTUREMAX_DANCE_MAT; + + sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); + if (sc == NULL) { + hid_err(hdev, "can't alloc sony descriptor\n"); + return -ENOMEM; + } + + spin_lock_init(&sc->lock); + + sc->quirks = quirks; + hid_set_drvdata(hdev, sc); + sc->hdev = hdev; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + return ret; + } + + if (sc->quirks & VAIO_RDESC_CONSTANT) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + else if (sc->quirks & SIXAXIS_CONTROLLER) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + + ret = hid_hw_start(hdev, connect_mask); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + return ret; +} + static void sony_remove(struct hid_device *hdev) { struct sony_sc *sc = hid_get_drvdata(hdev); -- cgit v1.2.3-55-g7522 From 2c159de05082a70d3b3e75d8e167f4b5ca996405 Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Fri, 7 Oct 2016 12:39:35 -0700 Subject: HID: sony: Adjust HID report size name definitions Put the report type (feature / output) in the report size definitions. This prevents name collisions later on for other different reports, which use the same report id, but have a different size. Signed-off-by: Roderick Colenbrander Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index afa82198defb..43bb24cc5fc3 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1019,10 +1019,10 @@ struct motion_output_report_02 { u8 rumble; }; -#define DS4_REPORT_0x02_SIZE 37 -#define DS4_REPORT_0x05_SIZE 32 -#define DS4_REPORT_0x11_SIZE 78 -#define DS4_REPORT_0x81_SIZE 7 +#define DS4_FEATURE_REPORT_0x02_SIZE 37 +#define DS4_FEATURE_REPORT_0x81_SIZE 7 +#define DS4_OUTPUT_REPORT_0x05_SIZE 32 +#define DS4_OUTPUT_REPORT_0x11_SIZE 78 #define SIXAXIS_REPORT_0xF2_SIZE 17 #define SIXAXIS_REPORT_0xF5_SIZE 8 #define MOTION_REPORT_0x02_SIZE 49 @@ -1461,11 +1461,11 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev) u8 *buf; int ret; - buf = kmalloc(DS4_REPORT_0x02_SIZE, GFP_KERNEL); + buf = kmalloc(DS4_FEATURE_REPORT_0x02_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = hid_hw_raw_request(hdev, 0x02, buf, DS4_REPORT_0x02_SIZE, + ret = hid_hw_raw_request(hdev, 0x02, buf, DS4_FEATURE_REPORT_0x02_SIZE, HID_FEATURE_REPORT, HID_REQ_GET_REPORT); kfree(buf); @@ -1870,12 +1870,12 @@ static void dualshock4_send_output_report(struct sony_sc *sc) * 0xD0 - 66hz */ if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { - memset(buf, 0, DS4_REPORT_0x05_SIZE); + memset(buf, 0, DS4_OUTPUT_REPORT_0x05_SIZE); buf[0] = 0x05; buf[1] = 0xFF; offset = 4; } else { - memset(buf, 0, DS4_REPORT_0x11_SIZE); + memset(buf, 0, DS4_OUTPUT_REPORT_0x11_SIZE); buf[0] = 0x11; buf[1] = 0x80; buf[3] = 0x0F; @@ -1903,9 +1903,9 @@ static void dualshock4_send_output_report(struct sony_sc *sc) buf[offset++] = sc->led_delay_off[3]; if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) - hid_hw_output_report(hdev, buf, DS4_REPORT_0x05_SIZE); + hid_hw_output_report(hdev, buf, DS4_OUTPUT_REPORT_0x05_SIZE); else - hid_hw_raw_request(hdev, 0x11, buf, DS4_REPORT_0x11_SIZE, + hid_hw_raw_request(hdev, 0x11, buf, DS4_OUTPUT_REPORT_0x11_SIZE, HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); } @@ -1950,10 +1950,10 @@ static int sony_allocate_output_report(struct sony_sc *sc) kmalloc(sizeof(union sixaxis_output_report_01), GFP_KERNEL); else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) - sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x11_SIZE, + sc->output_report_dmabuf = kmalloc(DS4_OUTPUT_REPORT_0x11_SIZE, GFP_KERNEL); else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) - sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x05_SIZE, + sc->output_report_dmabuf = kmalloc(DS4_OUTPUT_REPORT_0x05_SIZE, GFP_KERNEL); else if (sc->quirks & MOTION_CONTROLLER) sc->output_report_dmabuf = kmalloc(MOTION_REPORT_0x02_SIZE, @@ -2198,7 +2198,7 @@ static int sony_check_add(struct sony_sc *sc) return 0; } } else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { - buf = kmalloc(DS4_REPORT_0x81_SIZE, GFP_KERNEL); + buf = kmalloc(DS4_FEATURE_REPORT_0x81_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -2208,10 +2208,10 @@ static int sony_check_add(struct sony_sc *sc) * offset 1. */ ret = hid_hw_raw_request(sc->hdev, 0x81, buf, - DS4_REPORT_0x81_SIZE, HID_FEATURE_REPORT, + DS4_FEATURE_REPORT_0x81_SIZE, HID_FEATURE_REPORT, HID_REQ_GET_REPORT); - if (ret != DS4_REPORT_0x81_SIZE) { + if (ret != DS4_FEATURE_REPORT_0x81_SIZE) { hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n"); ret = ret < 0 ? ret : -EINVAL; goto out_free; -- cgit v1.2.3-55-g7522 From 49b9ca6c6c361a19d223ff84bd0ff871c01b528a Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Fri, 7 Oct 2016 12:39:36 -0700 Subject: HID: sony: Perform CRC check on bluetooth input packets Signed-off-by: Roderick Colenbrander Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 43bb24cc5fc3..34988ce9434a 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include "hid-ids.h" @@ -1021,6 +1023,7 @@ struct motion_output_report_02 { #define DS4_FEATURE_REPORT_0x02_SIZE 37 #define DS4_FEATURE_REPORT_0x81_SIZE 7 +#define DS4_INPUT_REPORT_0x11_SIZE 78 #define DS4_OUTPUT_REPORT_0x05_SIZE 32 #define DS4_OUTPUT_REPORT_0x11_SIZE 78 #define SIXAXIS_REPORT_0xF2_SIZE 17 @@ -1324,6 +1327,21 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, } else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 && size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) && rd[0] == 0x11 && size == 78)) { + if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) { + /* CRC check */ + u8 bthdr = 0xA1; + u32 crc; + u32 report_crc; + + crc = crc32_le(0xFFFFFFFF, &bthdr, 1); + crc = ~crc32_le(crc, rd, DS4_INPUT_REPORT_0x11_SIZE-4); + report_crc = get_unaligned_le32(&rd[DS4_INPUT_REPORT_0x11_SIZE-4]); + if (crc != report_crc) { + hid_dbg(sc->hdev, "DualShock 4 input report's CRC check failed, received crc 0x%0x != 0x%0x\n", + report_crc, crc); + return -EILSEQ; + } + } dualshock4_parse_report(sc, rd, size); } -- cgit v1.2.3-55-g7522 From e7ef53adbf47734e90f9fd6e2a7a57df6f1fbc6b Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Fri, 7 Oct 2016 12:39:37 -0700 Subject: HID: sony: Send ds4 output reports on output end-point Add a CRC value to each output report. This removes the need for the 'no output reports on interrupt end-point' quirk. Signed-off-by: Roderick Colenbrander Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 34988ce9434a..24f7d1937264 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1895,7 +1895,7 @@ static void dualshock4_send_output_report(struct sony_sc *sc) } else { memset(buf, 0, DS4_OUTPUT_REPORT_0x11_SIZE); buf[0] = 0x11; - buf[1] = 0x80; + buf[1] = 0xC0; /* HID + CRC */ buf[3] = 0x0F; offset = 6; } @@ -1922,9 +1922,16 @@ static void dualshock4_send_output_report(struct sony_sc *sc) if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) hid_hw_output_report(hdev, buf, DS4_OUTPUT_REPORT_0x05_SIZE); - else - hid_hw_raw_request(hdev, 0x11, buf, DS4_OUTPUT_REPORT_0x11_SIZE, - HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); + else { + /* CRC generation */ + u8 bthdr = 0xA2; + u32 crc; + + crc = crc32_le(0xFFFFFFFF, &bthdr, 1); + crc = ~crc32_le(crc, buf, DS4_OUTPUT_REPORT_0x11_SIZE-4); + put_unaligned_le32(crc, &buf[74]); + hid_hw_output_report(hdev, buf, DS4_OUTPUT_REPORT_0x11_SIZE); + } } static void motion_send_output_report(struct sony_sc *sc) @@ -2378,11 +2385,6 @@ static int sony_input_configured(struct hid_device *hdev, sony_init_output_report(sc, sixaxis_send_output_report); } else if (sc->quirks & DUALSHOCK4_CONTROLLER) { if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) { - /* - * The DualShock 4 wants output reports sent on the ctrl - * endpoint when connected via Bluetooth. - */ - hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; ret = dualshock4_set_operational_bt(hdev); if (ret < 0) { hid_err(hdev, "failed to set the Dualshock 4 operational mode\n"); -- cgit v1.2.3-55-g7522 From cdc1c0215ab449077cd160dde4fcd1c5f41dec6e Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Fri, 7 Oct 2016 12:39:38 -0700 Subject: HID: sony: Handle multiple touch events input record Read the touch history field in the HID descriptor and use this value to determine how many touch events to read from the report. As part of this patch, we did a first attempt of making the offset calculation code less magical. Signed-off-by: Roderick Colenbrander Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 78 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 26 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 24f7d1937264..b206d856d351 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1030,6 +1030,12 @@ struct motion_output_report_02 { #define SIXAXIS_REPORT_0xF5_SIZE 8 #define MOTION_REPORT_0x02_SIZE 49 +/* Offsets relative to USB input report (0x1). Bluetooth (0x11) requires an + * additional +2. + */ +#define DS4_INPUT_REPORT_BATTERY_OFFSET 30 +#define DS4_INPUT_REPORT_TOUCHPAD_OFFSET 33 + static DEFINE_SPINLOCK(sony_dev_list_lock); static LIST_HEAD(sony_device_list); static DEFINE_IDA(sony_device_id_allocator); @@ -1226,19 +1232,17 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size) struct hid_input, list); struct input_dev *input_dev = hidinput->input; unsigned long flags; - int n, offset; + int n, m, offset, num_touch_data, max_touch_data; u8 cable_state, battery_capacity, battery_charging; - /* - * Battery and touchpad data starts at byte 30 in the USB report and - * 32 in Bluetooth report. - */ - offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 30 : 32; + /* When using Bluetooth the header is 2 bytes longer, so skip these. */ + int data_offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 0 : 2; /* - * The lower 4 bits of byte 30 contain the battery level + * The lower 4 bits of byte 30 (or 32 for BT) contain the battery level * and the 5th bit contains the USB cable state. */ + offset = data_offset + DS4_INPUT_REPORT_BATTERY_OFFSET; cable_state = (rd[offset] >> 4) & 0x01; battery_capacity = rd[offset] & 0x0F; @@ -1265,30 +1269,52 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size) sc->battery_charging = battery_charging; spin_unlock_irqrestore(&sc->lock, flags); - offset += 5; - /* - * The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB - * and 37 on Bluetooth. - * The first 7 bits of the first byte is a counter and bit 8 is a touch - * indicator that is 0 when pressed and 1 when not pressed. - * The next 3 bytes are two 12 bit touch coordinates, X and Y. - * The data for the second touch is in the same format and immediatly - * follows the data for the first. + * The Dualshock 4 multi-touch trackpad data starts at offset 33 on USB + * and 35 on Bluetooth. + * The first byte indicates the number of touch data in the report. + * Trackpad data starts 2 bytes later (e.g. 35 for USB). */ - for (n = 0; n < 2; n++) { - u16 x, y; + offset = data_offset + DS4_INPUT_REPORT_TOUCHPAD_OFFSET; + max_touch_data = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 3 : 4; + if (rd[offset] > 0 && rd[offset] <= max_touch_data) + num_touch_data = rd[offset]; + else + num_touch_data = 1; + offset += 1; - x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8); - y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4); + for (m = 0; m < num_touch_data; m++) { + /* Skip past timestamp */ + offset += 1; - input_mt_slot(input_dev, n); - input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, - !(rd[offset] >> 7)); - input_report_abs(input_dev, ABS_MT_POSITION_X, x); - input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + /* + * The first 7 bits of the first byte is a counter and bit 8 is + * a touch indicator that is 0 when pressed and 1 when not + * pressed. + * The next 3 bytes are two 12 bit touch coordinates, X and Y. + * The data for the second touch is in the same format and + * immediately follows the data for the first. + */ + for (n = 0; n < 2; n++) { + u16 x, y; + bool active; + + x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8); + y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4); + + active = !(rd[offset] >> 7); + input_mt_slot(input_dev, n); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, active); - offset += 4; + if (active) { + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + } + + offset += 4; + } + input_mt_sync_frame(input_dev); + input_sync(input_dev); } } -- cgit v1.2.3-55-g7522 From bdae9e0e95364123fb7d372872bd7efd1760867c Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Fri, 7 Oct 2016 12:39:39 -0700 Subject: HID: sony: Adjust value range for motion sensors The motion sensor values are 16-bit, so make the value range match. It is hard to reach the upper values, but they can be reached. At least the current accelerometer value of 8192 is very easy to pass. It is still not nice that the motion sensors live in no man's land in between ABS_MISC and ABS_MT_SLOT, but that's something for another time, which the proposed ABS_ACCEL_*/ABS_GYRO_* were meant for. Signed-off-by: Roderick Colenbrander Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index b206d856d351..bd847906c770 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -405,14 +405,14 @@ static u8 dualshock4_usb_rdesc[] = { 0x19, 0x40, /* Usage Minimum (40h), */ 0x29, 0x42, /* Usage Maximum (42h), */ 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */ - 0x26, 0x00, 0x7F, /* Logical Maximum (32767), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ 0x75, 0x10, /* Report Size (16), */ 0x95, 0x03, /* Report Count (3), */ 0x81, 0x02, /* Input (Variable), */ 0x19, 0x43, /* Usage Minimum (43h), */ 0x29, 0x45, /* Usage Maximum (45h), */ - 0x16, 0x00, 0xE0, /* Logical Minimum (-8192), */ - 0x26, 0xFF, 0x1F, /* Logical Maximum (8191), */ + 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ 0x95, 0x03, /* Report Count (3), */ 0x81, 0x02, /* Input (Variable), */ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ @@ -714,14 +714,14 @@ static u8 dualshock4_bt_rdesc[] = { 0x19, 0x40, /* Usage Minimum (40h), */ 0x29, 0x42, /* Usage Maximum (42h), */ 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */ - 0x26, 0x00, 0x7F, /* Logical Maximum (32767), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ 0x75, 0x10, /* Report Size (16), */ 0x95, 0x03, /* Report Count (3), */ 0x81, 0x02, /* Input (Variable), */ 0x19, 0x43, /* Usage Minimum (43h), */ 0x29, 0x45, /* Usage Maximum (45h), */ - 0x16, 0x00, 0xE0, /* Logical Minimum (-8192), */ - 0x26, 0xFF, 0x1F, /* Logical Maximum (8191), */ + 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ 0x95, 0x03, /* Report Count (3), */ 0x81, 0x02, /* Input (Variable), */ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ -- cgit v1.2.3-55-g7522 From cf1015d65d7c8a5504a4c03afb60fb86bff0f032 Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Fri, 7 Oct 2016 12:39:40 -0700 Subject: HID: sony: Update device ids Support additional DS4 model. Signed-off-by: Roderick Colenbrander Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 ++ drivers/hid/hid-ids.h | 1 + drivers/hid/hid-sony.c | 4 ++++ 3 files changed, 7 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2b89c701076f..5ed2f572430f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2059,6 +2059,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index cd59c79eebdd..27f82cc4ada4 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -899,6 +899,7 @@ #define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306 #define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 #define USB_DEVICE_ID_SONY_PS4_CONTROLLER 0x05c4 +#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 0x09cc #define USB_DEVICE_ID_SONY_MOTION_CONTROLLER 0x03d5 #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f #define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002 diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index bd847906c770..14763cdf6393 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -2641,6 +2641,10 @@ static const struct hid_device_id sony_devices[] = { .driver_data = DUALSHOCK4_CONTROLLER_USB }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), .driver_data = DUALSHOCK4_CONTROLLER_BT }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2), + .driver_data = DUALSHOCK4_CONTROLLER_USB }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2), + .driver_data = DUALSHOCK4_CONTROLLER_BT }, /* Nyko Core Controller for PS3 */ { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER), .driver_data = SIXAXIS_CONTROLLER_USB | SINO_LITE_CONTROLLER }, -- cgit v1.2.3-55-g7522 From ba18a9314a94f4a54b4d0c5e0f1dc85af28eca35 Mon Sep 17 00:00:00 2001 From: David Arcari Date: Thu, 13 Oct 2016 11:30:44 +0200 Subject: Revert "HID: i2c-hid: Add support for ACPI GPIO interrupts" This reverts commit a485923efbb8 ("HID: i2c-hid: Add support for ACPI GPIO interrupts") and commit a7d2bf25a483 ("HID: i2c-hid: Do not fail probing if gpiolib is not enabled") at the same time. Since commit c884fbd45214 ("gpio / ACPI: Add support for retrieving GpioInt resources from a device") i2c_core already set the IRQ by looking into the ACPI tree and retrieving the gpioInt. So we just have some boiler-plate here that is not needed anymore. The only downside effect here is that now we are not exiting early enough if the irq is set to -EPROBE_DEFER or any other error, but this is going to be fixed in the following patch. Signed-off-by: David Arcari Signed-off-by: Benjamin Tissoires Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 71 +++++++++++-------------------------------- 1 file changed, 18 insertions(+), 53 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index b3ec4f2de875..4cd606c0d89c 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -37,7 +37,6 @@ #include #include #include -#include #include @@ -145,8 +144,6 @@ struct i2c_hid { unsigned long flags; /* device flags */ wait_queue_head_t wait; /* For waiting the interrupt */ - struct gpio_desc *desc; - int irq; struct i2c_hid_platform_data pdata; @@ -808,16 +805,16 @@ static int i2c_hid_init_irq(struct i2c_client *client) struct i2c_hid *ihid = i2c_get_clientdata(client); int ret; - dev_dbg(&client->dev, "Requesting IRQ: %d\n", ihid->irq); + dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq); - ret = request_threaded_irq(ihid->irq, NULL, i2c_hid_irq, + ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ihid); if (ret < 0) { dev_warn(&client->dev, "Could not register for %s interrupt, irq = %d," " ret = %d\n", - client->name, ihid->irq, ret); + client->name, client->irq, ret); return ret; } @@ -864,14 +861,6 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) } #ifdef CONFIG_ACPI - -/* Default GPIO mapping */ -static const struct acpi_gpio_params i2c_hid_irq_gpio = { 0, 0, true }; -static const struct acpi_gpio_mapping i2c_hid_acpi_gpios[] = { - { "gpios", &i2c_hid_irq_gpio, 1 }, - { }, -}; - static int i2c_hid_acpi_pdata(struct i2c_client *client, struct i2c_hid_platform_data *pdata) { @@ -882,7 +871,6 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client, union acpi_object *obj; struct acpi_device *adev; acpi_handle handle; - int ret; handle = ACPI_HANDLE(&client->dev); if (!handle || acpi_bus_get_device(handle, &adev)) @@ -898,9 +886,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client, pdata->hid_descriptor_address = obj->integer.value; ACPI_FREE(obj); - /* GPIOs are optional */ - ret = acpi_dev_add_driver_gpios(adev, i2c_hid_acpi_gpios); - return ret < 0 && ret != -ENXIO ? ret : 0; + return 0; } static const struct acpi_device_id i2c_hid_acpi_match[] = { @@ -964,6 +950,12 @@ static int i2c_hid_probe(struct i2c_client *client, dbg_hid("HID probe called for i2c 0x%02x\n", client->addr); + if (!client->irq) { + dev_err(&client->dev, + "HID over i2c has not been provided an Int IRQ\n"); + return -EINVAL; + } + ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL); if (!ihid) return -ENOMEM; @@ -983,23 +975,6 @@ static int i2c_hid_probe(struct i2c_client *client, ihid->pdata = *platform_data; } - if (client->irq > 0) { - ihid->irq = client->irq; - } else if (ACPI_COMPANION(&client->dev)) { - ihid->desc = gpiod_get(&client->dev, NULL, GPIOD_IN); - if (IS_ERR(ihid->desc)) { - dev_err(&client->dev, "Failed to get GPIO interrupt\n"); - return PTR_ERR(ihid->desc); - } - - ihid->irq = gpiod_to_irq(ihid->desc); - if (ihid->irq < 0) { - gpiod_put(ihid->desc); - dev_err(&client->dev, "Failed to convert GPIO to IRQ\n"); - return ihid->irq; - } - } - i2c_set_clientdata(client, ihid); ihid->client = client; @@ -1064,16 +1039,13 @@ err_mem_free: hid_destroy_device(hid); err_irq: - free_irq(ihid->irq, ihid); + free_irq(client->irq, ihid); err_pm: pm_runtime_put_noidle(&client->dev); pm_runtime_disable(&client->dev); err: - if (ihid->desc) - gpiod_put(ihid->desc); - i2c_hid_free_buffers(ihid); kfree(ihid); return ret; @@ -1092,18 +1064,13 @@ static int i2c_hid_remove(struct i2c_client *client) hid = ihid->hid; hid_destroy_device(hid); - free_irq(ihid->irq, ihid); + free_irq(client->irq, ihid); if (ihid->bufsize) i2c_hid_free_buffers(ihid); - if (ihid->desc) - gpiod_put(ihid->desc); - kfree(ihid); - acpi_dev_remove_driver_gpios(ACPI_COMPANION(&client->dev)); - return 0; } @@ -1142,11 +1109,11 @@ static int i2c_hid_suspend(struct device *dev) /* Save some power */ i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); - disable_irq(ihid->irq); + disable_irq(client->irq); } if (device_may_wakeup(&client->dev)) { - wake_status = enable_irq_wake(ihid->irq); + wake_status = enable_irq_wake(client->irq); if (!wake_status) ihid->irq_wake_enabled = true; else @@ -1166,7 +1133,7 @@ static int i2c_hid_resume(struct device *dev) int wake_status; if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) { - wake_status = disable_irq_wake(ihid->irq); + wake_status = disable_irq_wake(client->irq); if (!wake_status) ihid->irq_wake_enabled = false; else @@ -1179,7 +1146,7 @@ static int i2c_hid_resume(struct device *dev) pm_runtime_set_active(dev); pm_runtime_enable(dev); - enable_irq(ihid->irq); + enable_irq(client->irq); ret = i2c_hid_hwreset(client); if (ret) return ret; @@ -1197,19 +1164,17 @@ static int i2c_hid_resume(struct device *dev) static int i2c_hid_runtime_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - struct i2c_hid *ihid = i2c_get_clientdata(client); i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); - disable_irq(ihid->irq); + disable_irq(client->irq); return 0; } static int i2c_hid_runtime_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - struct i2c_hid *ihid = i2c_get_clientdata(client); - enable_irq(ihid->irq); + enable_irq(client->irq); i2c_hid_set_power(client, I2C_HID_PWR_ON); return 0; } -- cgit v1.2.3-55-g7522 From 93d26aeab54cc91119760bc4f1f22bff9a987d70 Mon Sep 17 00:00:00 2001 From: David Arcari Date: Thu, 13 Oct 2016 11:30:45 +0200 Subject: HID: i2c-hid: exit if the IRQ is not valid When i2c-core doesn't find the IRQ associated to the GPIO because the gpiochip is not available, it assigns -EPROBE_DEFER to the irq. We need to bail out there and on any other error in an IRQ. Signed-off-by: David Arcari Signed-off-by: Benjamin Tissoires Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 4cd606c0d89c..fe6b4e0eab4a 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -956,6 +956,13 @@ static int i2c_hid_probe(struct i2c_client *client, return -EINVAL; } + if (client->irq < 0) { + if (client->irq != -EPROBE_DEFER) + dev_err(&client->dev, + "HID over i2c doesn't have a valid IRQ\n"); + return client->irq; + } + ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL); if (!ihid) return -ENOMEM; -- cgit v1.2.3-55-g7522 From 8de82280e3f5c4569505f475ce1a0b9ccfbd8de8 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:37 -0700 Subject: HID: wacom: Update vendor-defined usage names to better match standards Our loose use of "pen" and "digitizer" in the naming of several of our vendor-defined usages may be a source of confusion given that the terms have specific meaning within the HID specification. "Pen" specifically refers to "an integrated display that allows the use of a stylus" (e.g. something like a tablet PC or Cintiq) wheras "Digitizer" is a better fit for opaque tablets like an Intuos. While we're at it, go ahead and rename the definitions to make them more distinct and better match up with the convention used by HID (e.g. the use of '_UP_' for usage pages) and make them more distinct. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 12 ++++++------ drivers/hid/wacom_wac.c | 2 +- drivers/hid/wacom_wac.h | 17 +++++++++-------- 3 files changed, 16 insertions(+), 15 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 5e7a5648e708..773fa114b216 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -159,18 +159,18 @@ static void wacom_feature_mapping(struct hid_device *hdev, case HID_UP_DIGITIZER: if (field->report->id == 0x0B && - (field->application == WACOM_G9_DIGITIZER || - field->application == WACOM_G11_DIGITIZER)) { + (field->application == WACOM_HID_G9_PEN || + field->application == WACOM_HID_G11_PEN)) { wacom->wacom_wac.mode_report = field->report->id; wacom->wacom_wac.mode_value = 0; } break; - case WACOM_G9_PAGE: - case WACOM_G11_PAGE: + case WACOM_HID_UP_G9: + case WACOM_HID_UP_G11: if (field->report->id == 0x03 && - (field->application == WACOM_G9_TOUCHSCREEN || - field->application == WACOM_G11_TOUCHSCREEN)) { + (field->application == WACOM_HID_G9_TOUCHSCREEN || + field->application == WACOM_HID_G11_TOUCHSCREEN)) { wacom->wacom_wac.mode_report = field->report->id; wacom->wacom_wac.mode_value = 0; } diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 1cb79925730d..3038954b8c41 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2448,7 +2448,7 @@ void wacom_setup_device_quirks(struct wacom *wacom) /* * Raw Wacom-mode pen and touch events both come from interface * 0, whose HID descriptor has an application usage of 0xFF0D - * (i.e., WACOM_VENDORDEFINED_PEN). We route pen packets back + * (i.e., WACOM_HID_WD_DIGITIZER). We route pen packets back * out through the HID_GENERIC device created for interface 1, * so rewrite this one to be of type WACOM_DEVICETYPE_TOUCH. */ diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 324c40b0c119..f58bbd18209c 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -84,20 +84,21 @@ #define WACOM_DEVICETYPE_WL_MONITOR 0x0008 #define WACOM_DEVICETYPE_DIRECT 0x0010 -#define WACOM_VENDORDEFINED_PEN 0xff0d0001 -#define WACOM_G9_PAGE 0xff090000 -#define WACOM_G9_DIGITIZER (WACOM_G9_PAGE | 0x02) -#define WACOM_G9_TOUCHSCREEN (WACOM_G9_PAGE | 0x11) -#define WACOM_G11_PAGE 0xff110000 -#define WACOM_G11_DIGITIZER (WACOM_G11_PAGE | 0x02) -#define WACOM_G11_TOUCHSCREEN (WACOM_G11_PAGE | 0x11) +#define WACOM_HID_UP_WACOMDIGITIZER 0xff0d0000 +#define WACOM_HID_WD_DIGITIZER (WACOM_HID_UP_WACOMDIGITIZER | 0x01) +#define WACOM_HID_UP_G9 0xff090000 +#define WACOM_HID_G9_PEN (WACOM_HID_UP_G9 | 0x02) +#define WACOM_HID_G9_TOUCHSCREEN (WACOM_HID_UP_G9 | 0x11) +#define WACOM_HID_UP_G11 0xff110000 +#define WACOM_HID_G11_PEN (WACOM_HID_UP_G11 | 0x02) +#define WACOM_HID_G11_TOUCHSCREEN (WACOM_HID_UP_G11 | 0x11) #define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \ ((f)->physical == HID_DG_STYLUS) || \ ((f)->physical == HID_DG_PEN) || \ ((f)->application == HID_DG_PEN) || \ ((f)->application == HID_DG_DIGITIZER) || \ - ((f)->application == WACOM_VENDORDEFINED_PEN)) + ((f)->application == WACOM_HID_WD_DIGITIZER)) #define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \ ((f)->physical == HID_DG_FINGER) || \ ((f)->application == HID_DG_TOUCHSCREEN)) -- cgit v1.2.3-55-g7522 From 1b18b75c7e2452fc6f4cd1bb701efc4438532a07 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:38 -0700 Subject: HID: wacom: Have WACOM_PEN_FIELD and WACOM_FINGER_FIELD recgonize more fields We've defined several new usages (e.g. WACOM_G9_PEN and WACOM_G9_TOUCHSCREEN) which aren't checked by the WACOM_PEN_FIELD and WACOM_FINGER_FIELD macros but probably should be. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index f58bbd18209c..d2629c81c51a 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -98,10 +98,14 @@ ((f)->physical == HID_DG_PEN) || \ ((f)->application == HID_DG_PEN) || \ ((f)->application == HID_DG_DIGITIZER) || \ - ((f)->application == WACOM_HID_WD_DIGITIZER)) + ((f)->application == WACOM_HID_WD_DIGITIZER) || \ + ((f)->application == WACOM_HID_G9_PEN) || \ + ((f)->application == WACOM_HID_G11_PEN)) #define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \ ((f)->physical == HID_DG_FINGER) || \ - ((f)->application == HID_DG_TOUCHSCREEN)) + ((f)->application == HID_DG_TOUCHSCREEN) || \ + ((f)->application == WACOM_HID_G9_TOUCHSCREEN) || \ + ((f)->application == WACOM_HID_G11_TOUCHSCREEN)) enum { PENPARTNER = 0, -- cgit v1.2.3-55-g7522 From 49005b9fd05249d537363ff86cb41f07f48c847a Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:39 -0700 Subject: HID: wacom: Refactor button-to-key translation into function This just centralizes the logic used in both wacom_setup_numbered_buttons and wacom_report_numbered_buttons so that they don't drift out of sync. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 3038954b8c41..8071c18bf9c2 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2769,17 +2769,29 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev, return 0; } +static int wacom_numbered_button_to_key(int n) +{ + if (n < 10) + return BTN_0 + n; + else if (n < 16) + return BTN_A + (n-10); + else if (n < 18) + return BTN_BASE + (n-16); + else + return 0; +} + static void wacom_setup_numbered_buttons(struct input_dev *input_dev, int button_count) { int i; - for (i = 0; i < button_count && i < 10; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - for (i = 10; i < button_count && i < 16; i++) - __set_bit(BTN_A + (i-10), input_dev->keybit); - for (i = 16; i < button_count && i < 18; i++) - __set_bit(BTN_BASE + (i-16), input_dev->keybit); + for (i = 0; i < button_count; i++) { + int key = wacom_numbered_button_to_key(i); + + if (key) + __set_bit(key, input_dev->keybit); + } } static void wacom_24hd_update_leds(struct wacom *wacom, int mask, int group) @@ -2881,12 +2893,12 @@ static void wacom_report_numbered_buttons(struct input_dev *input_dev, for (i = 0; i < wacom->led.count; i++) wacom_update_led(wacom, button_count, mask, i); - for (i = 0; i < button_count && i < 10; i++) - input_report_key(input_dev, BTN_0 + i, mask & (1 << i)); - for (i = 10; i < button_count && i < 16; i++) - input_report_key(input_dev, BTN_A + (i-10), mask & (1 << i)); - for (i = 16; i < button_count && i < 18; i++) - input_report_key(input_dev, BTN_BASE + (i-16), mask & (1 << i)); + for (i = 0; i < button_count; i++) { + int key = wacom_numbered_button_to_key(i); + + if (key) + input_report_key(input_dev, key, mask & (1 << i)); + } } int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, -- cgit v1.2.3-55-g7522 From 6005a13c90a31501f3c21797b567e845c7098f25 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:40 -0700 Subject: HID: wacom: Detect and correct descriptors missing HID_DG_BARRELSWITCH2 ISDv4 devices have long supported reporting data from each of two barrel switches, but HID_DG_BARRELSWITCH2 itself was only recently standardized. Prior to its adoption, ISDv4 devices would associate the bit indicating the state of the second barrel switch with the "Undefined" 0x000D0000 usage. Although most such devices have explicit support, a few use the HID_GENERIC codepath which ignores the "Undefined" usage. This patch adds code which detects the presence of a pre-standard second barrel switch and corrects the usage value so that the HID_GENERIC code will declare its presence and report its state. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 773fa114b216..033cc032f45c 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -240,6 +240,30 @@ static void wacom_usage_mapping(struct hid_device *hdev, features->touch_max = 1; } + /* + * ISDv4 devices which predate HID's adoption of the + * HID_DG_BARELSWITCH2 usage use 0x000D0000 in its + * position instead. We can accurately detect if a + * usage with that value should be HID_DG_BARRELSWITCH2 + * based on the surrounding usages, which have remained + * constant across generations. + */ + if (features->type == HID_GENERIC && + usage->hid == 0x000D0000 && + field->application == HID_DG_PEN && + field->physical == HID_DG_STYLUS) { + int i = usage->usage_index; + + if (i-4 >= 0 && i+1 < field->maxusage && + field->usage[i-4].hid == HID_DG_TIPSWITCH && + field->usage[i-3].hid == HID_DG_BARRELSWITCH && + field->usage[i-2].hid == HID_DG_ERASER && + field->usage[i-1].hid == HID_DG_INVERT && + field->usage[i+1].hid == HID_DG_INRANGE) { + usage->hid = HID_DG_BARRELSWITCH2; + } + } + switch (usage->hid) { case HID_GD_X: features->x_max = field->logical_maximum; -- cgit v1.2.3-55-g7522 From f2209d4aefac246772152c5294af49f2800f646b Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:41 -0700 Subject: HID: wacom: generic: Strip off excessive name prefixing The product name received from the string descriptor in the new MobileStudio Pro line of tablets begins with "Wacom", which leads to unnecessary visual noise in the device name when appended to the vendor name which also includes "Wacom". Look for and fix cases like this. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 033cc032f45c..7b9bff2fad94 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -1940,6 +1940,19 @@ static void wacom_update_name(struct wacom *wacom, const char *suffix) /* shift everything including the terminator */ memmove(gap, gap+1, strlen(gap)); } + + /* strip off excessive prefixing */ + if (strstr(name, "Wacom Co.,Ltd. Wacom ") == name) { + int n = strlen(name); + int x = strlen("Wacom Co.,Ltd. "); + memmove(name, name+x, n-x+1); + } + if (strstr(name, "Wacom Co., Ltd. Wacom ") == name) { + int n = strlen(name); + int x = strlen("Wacom Co., Ltd. "); + memmove(name, name+x, n-x+1); + } + /* get rid of trailing whitespace */ if (name[strlen(name)-1] == ' ') name[strlen(name)-1] = '\0'; -- cgit v1.2.3-55-g7522 From 50066a042da5457ae5b6397425f0a7ca556231e3 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:42 -0700 Subject: HID: wacom: generic: Add support for height, tilt, and twist usages The HID standard defines usages that allow digitizers to report the pen's height, tilt, and rotation and which are used by Wacom's new "MobileStudio Pro" devices. Note that 'hidinput_calc_abs_res' expects ABS_Z (historically used by our driver to report twist) to have linear units. To ensure it calculates a resolution with the actually-angular units provided in the HID descriptor we nedd to lie and tell it we're calculating it for the (rotational) ABS_RZ axis instead. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 28 ++++++++++++++++++++++++++-- include/linux/hid.h | 3 +++ 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 8071c18bf9c2..3f4ba53192c0 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1440,6 +1440,11 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, { int fmin = field->logical_minimum; int fmax = field->logical_maximum; + int resolution_code = code; + + if (usage->hid == HID_DG_TWIST) { + resolution_code = ABS_RZ; + } usage->type = type; usage->code = code; @@ -1450,7 +1455,7 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, case EV_ABS: input_set_abs_params(input, code, fmin, fmax, fuzz, 0); input_abs_set_res(input, code, - hidinput_calc_abs_res(field, code)); + hidinput_calc_abs_res(field, resolution_code)); break; case EV_KEY: input_set_capability(input, EV_KEY, code); @@ -1475,6 +1480,9 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, case HID_GD_Y: wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4); break; + case HID_GD_Z: + wacom_map_usage(input, usage, field, EV_ABS, ABS_DISTANCE, 0); + break; case HID_DG_TIPPRESSURE: wacom_map_usage(input, usage, field, EV_ABS, ABS_PRESSURE, 0); break; @@ -1485,6 +1493,15 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_RUBBER, 0); break; + case HID_DG_TILT_X: + wacom_map_usage(input, usage, field, EV_ABS, ABS_TILT_X, 0); + break; + case HID_DG_TILT_Y: + wacom_map_usage(input, usage, field, EV_ABS, ABS_TILT_Y, 0); + break; + case HID_DG_TWIST: + wacom_map_usage(input, usage, field, EV_ABS, ABS_Z, 0); + break; case HID_DG_ERASER: case HID_DG_TIPSWITCH: wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0); @@ -1508,8 +1525,15 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct input_dev *input = wacom_wac->pen_input; - /* checking which Tool / tip switch to send */ switch (usage->hid) { + case HID_GD_Z: + /* + * HID_GD_Z "should increase as the control's position is + * moved from high to low", while ABS_DISTANCE instead + * increases in value as the tool moves from low to high. + */ + value = field->logical_maximum - value; + break; case HID_DG_INRANGE: wacom_wac->hid_data.inrange_state = value; return 0; diff --git a/include/linux/hid.h b/include/linux/hid.h index b2ec82712baa..e712101a1670 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -232,6 +232,9 @@ struct hid_item { #define HID_DG_TABLETFUNCTIONKEY 0x000d0039 #define HID_DG_PROGRAMCHANGEKEY 0x000d003a #define HID_DG_INVERT 0x000d003c +#define HID_DG_TILT_X 0x000d003d +#define HID_DG_TILT_Y 0x000d003e +#define HID_DG_TWIST 0x000d0041 #define HID_DG_TIPSWITCH 0x000d0042 #define HID_DG_TIPSWITCH2 0x000d0043 #define HID_DG_BARRELSWITCH 0x000d0044 -- cgit v1.2.3-55-g7522 From c9c095874ab4446be6dec6755d8f68862fdeae48 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:43 -0700 Subject: HID: wacom: generic: Support and use 'Custom HID' mode and usages Wacom's new "MobileStudio Pro" tablets are the first devices in their branded product line-up to include a usable HID descriptor for the pen interface. Like prior branded products, the device can operate in one of two modes: 'Standard HID', and 'Wacom Custom HID'. Although the first mode is usable by the HID_GENERIC codepath as-is (huzzah!), it is subject to some restrictions -- most notably pressure being limited to 2048 levels instead of 8192. To ensure tablets that include support for Custom HID mode work optimally, we add support for its usages and switch the device to Custom HID mode if possible. The usages defined for Custom HID mode are often numerically similar to their standard HID equivalents, allowing us to write a simple translation function that takes arbitrary HID usages as input and which returns the corresponding standard HID usage as output (if one exists). Switching on this translated usage instead of the actual usage allows the existing cases to apply to both modes of operation without having to explicitly define every Custom HID usage. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 5 +++++ drivers/hid/wacom_wac.c | 37 +++++++++++++++++++++++++++++++------ drivers/hid/wacom_wac.h | 3 +++ 3 files changed, 39 insertions(+), 6 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 7b9bff2fad94..b2e2471bbef6 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -166,6 +166,11 @@ static void wacom_feature_mapping(struct hid_device *hdev, } break; + case WACOM_HID_WD_DATAMODE: + wacom->wacom_wac.mode_report = field->report->id; + wacom->wacom_wac.mode_value = 2; + break; + case WACOM_HID_UP_G9: case WACOM_HID_UP_G11: if (field->report->id == 0x03 && diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 3f4ba53192c0..6c2f0e4baf7d 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1435,14 +1435,35 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) return 0; } +static int wacom_equivalent_usage(int usage) +{ + if ((usage & HID_USAGE_PAGE) == WACOM_HID_UP_WACOMDIGITIZER) { + int subpage = (usage & 0xFF00) << 8; + int subusage = (usage & 0xFF); + + if (subpage == WACOM_HID_SP_DIGITIZER || + subpage == WACOM_HID_SP_DIGITIZERINFO) { + return usage; + } + + if (subpage == HID_UP_UNDEFINED) + subpage = HID_UP_DIGITIZER; + + return subpage | subusage; + } + + return usage; +} + static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, struct hid_field *field, __u8 type, __u16 code, int fuzz) { int fmin = field->logical_minimum; int fmax = field->logical_maximum; + unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid); int resolution_code = code; - if (usage->hid == HID_DG_TWIST) { + if (equivalent_usage == HID_DG_TWIST) { resolution_code = ABS_RZ; } @@ -1472,8 +1493,9 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct input_dev *input = wacom_wac->pen_input; + unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); - switch (usage->hid) { + switch (equivalent_usage) { case HID_GD_X: wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4); break; @@ -1524,8 +1546,9 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct input_dev *input = wacom_wac->pen_input; + unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); - switch (usage->hid) { + switch (equivalent_usage) { case HID_GD_Z: /* * HID_GD_Z "should increase as the control's position is @@ -1597,8 +1620,9 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct input_dev *input = wacom_wac->touch_input; unsigned touch_max = wacom_wac->features.touch_max; + unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); - switch (usage->hid) { + switch (equivalent_usage) { case HID_GD_X: if (touch_max == 1) wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4); @@ -1673,8 +1697,9 @@ static int wacom_wac_finger_event(struct hid_device *hdev, { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; + unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); - switch (usage->hid) { + switch (equivalent_usage) { case HID_GD_X: wacom_wac->hid_data.x = value; break; @@ -1697,7 +1722,7 @@ static int wacom_wac_finger_event(struct hid_device *hdev, if (usage->usage_index + 1 == field->report_count) { - if (usage->hid == wacom_wac->hid_data.last_slot_field) + if (equivalent_usage == wacom_wac->hid_data.last_slot_field) wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input); } diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index d2629c81c51a..ba914bad971f 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -85,7 +85,10 @@ #define WACOM_DEVICETYPE_DIRECT 0x0010 #define WACOM_HID_UP_WACOMDIGITIZER 0xff0d0000 +#define WACOM_HID_SP_DIGITIZER 0x000d0000 +#define WACOM_HID_SP_DIGITIZERINFO 0x00100000 #define WACOM_HID_WD_DIGITIZER (WACOM_HID_UP_WACOMDIGITIZER | 0x01) +#define WACOM_HID_WD_DATAMODE (WACOM_HID_UP_WACOMDIGITIZER | 0x1002) #define WACOM_HID_UP_G9 0xff090000 #define WACOM_HID_G9_PEN (WACOM_HID_UP_G9 | 0x02) #define WACOM_HID_G9_TOUCHSCREEN (WACOM_HID_UP_G9 | 0x11) -- cgit v1.2.3-55-g7522 From b5c921e6c3916cdadd6e605d792f811abf7a5f82 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:44 -0700 Subject: HID: wacom: generic: Add support for vendor-defined "Distance" usage The vendor-defined 0xFF0D01032 ("Distance") usage is nearly equivalent to HID_GD_Z, except that the axis direction is inverted. Unlike HID_GD_Z which increases in value as the pen-to-surface distance is decreased, this usage decreases. Treat this usage as a special case to ensure we don't invert the scale to be ABS_DISTANCE compatible like we do for HID_GD_Z. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 4 +++- drivers/hid/wacom_wac.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 6c2f0e4baf7d..f78ad77d9ed7 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1442,7 +1442,8 @@ static int wacom_equivalent_usage(int usage) int subusage = (usage & 0xFF); if (subpage == WACOM_HID_SP_DIGITIZER || - subpage == WACOM_HID_SP_DIGITIZERINFO) { + subpage == WACOM_HID_SP_DIGITIZERINFO || + usage == WACOM_HID_WD_DISTANCE) { return usage; } @@ -1502,6 +1503,7 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, case HID_GD_Y: wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4); break; + case WACOM_HID_WD_DISTANCE: case HID_GD_Z: wacom_map_usage(input, usage, field, EV_ABS, ABS_DISTANCE, 0); break; diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index ba914bad971f..63a5a2586707 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -88,6 +88,7 @@ #define WACOM_HID_SP_DIGITIZER 0x000d0000 #define WACOM_HID_SP_DIGITIZERINFO 0x00100000 #define WACOM_HID_WD_DIGITIZER (WACOM_HID_UP_WACOMDIGITIZER | 0x01) +#define WACOM_HID_WD_DISTANCE (WACOM_HID_UP_WACOMDIGITIZER | 0x0132) #define WACOM_HID_WD_DATAMODE (WACOM_HID_UP_WACOMDIGITIZER | 0x1002) #define WACOM_HID_UP_G9 0xff090000 #define WACOM_HID_G9_PEN (WACOM_HID_UP_G9 | 0x02) -- cgit v1.2.3-55-g7522 From 929d6d5d22761fca1100e519cebe66f0fb11828d Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:45 -0700 Subject: HID: wacom: generic: Add support for vendor-defined "Fingerwheel" usage The airbrush fingerwheel does not have a usage that corresponds cleanly with a standard HID usage, so we add explicit support for it via its vendor-defined usage. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 3 +++ drivers/hid/wacom_wac.h | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index f78ad77d9ed7..81467c982734 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1539,6 +1539,9 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, case HID_DG_TOOLSERIALNUMBER: wacom_map_usage(input, usage, field, EV_MSC, MSC_SERIAL, 0); break; + case WACOM_HID_WD_FINGERWHEEL: + wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0); + break; } } diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 63a5a2586707..544755929c2f 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -89,6 +89,7 @@ #define WACOM_HID_SP_DIGITIZERINFO 0x00100000 #define WACOM_HID_WD_DIGITIZER (WACOM_HID_UP_WACOMDIGITIZER | 0x01) #define WACOM_HID_WD_DISTANCE (WACOM_HID_UP_WACOMDIGITIZER | 0x0132) +#define WACOM_HID_WD_FINGERWHEEL (WACOM_HID_UP_WACOMDIGITIZER | 0x0d03) #define WACOM_HID_WD_DATAMODE (WACOM_HID_UP_WACOMDIGITIZER | 0x1002) #define WACOM_HID_UP_G9 0xff090000 #define WACOM_HID_G9_PEN (WACOM_HID_UP_G9 | 0x02) -- cgit v1.2.3-55-g7522 From 61ce346a21a74cc106e76bbaf6a2319d1d74fa6d Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:46 -0700 Subject: HID: wacom: generic: Add support for vendor-defined "Sense" usage Wacom's professional tablets beginning with the Intuos4 are capable of reporting an intermediate degree of proximity where the pen is no longer close enough to communicate with ("in prox"), but still close enough to be sensed ("in range"). This additional state is particularly useful for performing palm rejection as it allows the driver to disable the touch sensor while the pen is a greater distance from the tablet. Like other professional tablets, the new MobileStudio Pro also reports this intermeidate "in range" proximity state. Its descriptor assigns usage 0xff0d0036 to this bit. Normally 'wacom_equivalent_usage' would translate this to the standard HID "Quality" usage, but since this has a different meaning we have it explicitly ignore the usage and define it ourselves as "Sense" (since "In Range" is already defined by the HID standard and interpreted by our driver as meaning "in prox"). Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 26 +++++++++++++++++++++++--- drivers/hid/wacom_wac.h | 3 +++ 2 files changed, 26 insertions(+), 3 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 81467c982734..c0d75aee91e5 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1443,6 +1443,7 @@ static int wacom_equivalent_usage(int usage) if (subpage == WACOM_HID_SP_DIGITIZER || subpage == WACOM_HID_SP_DIGITIZERINFO || + usage == WACOM_HID_WD_SENSE || usage == WACOM_HID_WD_DISTANCE) { return usage; } @@ -1493,6 +1494,7 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct wacom_features *features = &wacom_wac->features; struct input_dev *input = wacom_wac->pen_input; unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); @@ -1539,6 +1541,10 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, case HID_DG_TOOLSERIALNUMBER: wacom_map_usage(input, usage, field, EV_MSC, MSC_SERIAL, 0); break; + case WACOM_HID_WD_SENSE: + features->quirks |= WACOM_QUIRK_SENSE; + wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_PEN, 0); + break; case WACOM_HID_WD_FINGERWHEEL: wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0); break; @@ -1550,6 +1556,7 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct wacom_features *features = &wacom_wac->features; struct input_dev *input = wacom_wac->pen_input; unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); @@ -1564,6 +1571,8 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, break; case HID_DG_INRANGE: wacom_wac->hid_data.inrange_state = value; + if (!(features->quirks & WACOM_QUIRK_SENSE)) + wacom_wac->hid_data.sense_state = value; return 0; case HID_DG_INVERT: wacom_wac->hid_data.invert_state = value; @@ -1572,6 +1581,9 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, case HID_DG_TIPSWITCH: wacom_wac->hid_data.tipswitch |= value; return 0; + case WACOM_HID_WD_SENSE: + wacom_wac->hid_data.sense_state = value; + return 0; } /* send pen events only when touch is up or forced out @@ -1580,6 +1592,10 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, if (!usage->type || delay_pen_events(wacom_wac)) return 0; + /* send pen events only when the pen is in/entering/leaving proximity */ + if (!wacom_wac->hid_data.inrange_state && !wacom_wac->tool[0]) + return 0; + input_event(input, usage->type, usage->code, value); return 0; @@ -1598,16 +1614,17 @@ static void wacom_wac_pen_report(struct hid_device *hdev, struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct input_dev *input = wacom_wac->pen_input; bool prox = wacom_wac->hid_data.inrange_state; + bool range = wacom_wac->hid_data.sense_state; - if (!wacom_wac->shared->stylus_in_proximity) /* first in prox */ + if (!wacom_wac->tool[0] && prox) /* first in prox */ /* Going into proximity select tool */ wacom_wac->tool[0] = wacom_wac->hid_data.invert_state ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; /* keep pen state for touch events */ - wacom_wac->shared->stylus_in_proximity = prox; + wacom_wac->shared->stylus_in_proximity = range; - if (!delay_pen_events(wacom_wac)) { + if (!delay_pen_events(wacom_wac) && wacom_wac->tool[0]) { input_report_key(input, BTN_TOUCH, wacom_wac->hid_data.tipswitch); input_report_key(input, wacom_wac->tool[0], prox); @@ -1616,6 +1633,9 @@ static void wacom_wac_pen_report(struct hid_device *hdev, input_sync(input); } + + if (!prox) + wacom_wac->tool[0] = 0; } static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 544755929c2f..c27b7b40092e 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -74,6 +74,7 @@ /* device quirks */ #define WACOM_QUIRK_BBTOUCH_LOWRES 0x0001 +#define WACOM_QUIRK_SENSE 0x0002 #define WACOM_QUIRK_BATTERY 0x0008 /* device types */ @@ -88,6 +89,7 @@ #define WACOM_HID_SP_DIGITIZER 0x000d0000 #define WACOM_HID_SP_DIGITIZERINFO 0x00100000 #define WACOM_HID_WD_DIGITIZER (WACOM_HID_UP_WACOMDIGITIZER | 0x01) +#define WACOM_HID_WD_SENSE (WACOM_HID_UP_WACOMDIGITIZER | 0x36) #define WACOM_HID_WD_DISTANCE (WACOM_HID_UP_WACOMDIGITIZER | 0x0132) #define WACOM_HID_WD_FINGERWHEEL (WACOM_HID_UP_WACOMDIGITIZER | 0x0d03) #define WACOM_HID_WD_DATAMODE (WACOM_HID_UP_WACOMDIGITIZER | 0x1002) @@ -212,6 +214,7 @@ struct wacom_shared { struct hid_data { __s16 inputmode; /* InputMode HID feature, -1 if non-existent */ __s16 inputmode_index; /* InputMode HID feature index in the report */ + bool sense_state; bool inrange_state; bool invert_state; bool tipswitch; -- cgit v1.2.3-55-g7522 From 82527da319ee41e1e896f6c1290c83ecbc892a69 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:47 -0700 Subject: HID: wacom: Read and internally use corrected Intuos tool IDs The 'wacom_intuos_inout' function incorrectly assmebles tool IDs from the proximity report, shifting the higher values of the ID four bits farther than intended. This problem was not detected until too late, but has not caused any issues since the incorrect IDs still fit in a 32-bit integer and userspace programs have not required the value to match the hardware (just that the values are unique and constant). The tool IDs reported by the new MobileStudio Pro (or any future HID_GENERIC device that supports them) do not suffer from the same assembly issue, however. In order for 'wacom_intuos_get_tool_type' to work for with both codepaths, we correct this issue internally and have 'wacom_intuos_general' only mangle the ID when it is posted to userspace. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index c0d75aee91e5..df9d866b9b3f 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -588,6 +588,11 @@ static int wacom_intuos_pad(struct wacom_wac *wacom) return 1; } +static int wacom_intuos_id_mangle(int tool_id) +{ + return (tool_id & ~0xFFF) << 4 | (tool_id & 0xFFF); +} + static int wacom_intuos_get_tool_type(int tool_id) { int tool_type; @@ -595,7 +600,7 @@ static int wacom_intuos_get_tool_type(int tool_id) switch (tool_id) { case 0x812: /* Inking pen */ case 0x801: /* Intuos3 Inking pen */ - case 0x120802: /* Intuos4/5 Inking Pen */ + case 0x12802: /* Intuos4/5 Inking Pen */ case 0x012: tool_type = BTN_TOOL_PENCIL; break; @@ -610,11 +615,11 @@ static int wacom_intuos_get_tool_type(int tool_id) case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */ case 0x8e2: /* IntuosHT2 pen */ case 0x022: - case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */ - case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */ - case 0x160802: /* Cintiq 13HD Pro Pen */ - case 0x180802: /* DTH2242 Pen */ - case 0x100802: /* Intuos4/5 13HD/24HD General Pen */ + case 0x10804: /* Intuos4/5 13HD/24HD Art Pen */ + case 0x14802: /* Intuos4/5 13HD/24HD Classic Pen */ + case 0x16802: /* Cintiq 13HD Pro Pen */ + case 0x18802: /* DTH2242 Pen */ + case 0x10802: /* Intuos4/5 13HD/24HD General Pen */ tool_type = BTN_TOOL_PEN; break; @@ -648,12 +653,12 @@ static int wacom_intuos_get_tool_type(int tool_id) case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */ case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */ case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ - case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */ - case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ - case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */ - case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */ - case 0x18080a: /* DTH2242 Eraser */ - case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */ + case 0x1480a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */ + case 0x1090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ + case 0x1080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */ + case 0x1680a: /* Cintiq 13HD Pro Pen Eraser */ + case 0x1880a: /* DTH2242 Eraser */ + case 0x1080a: /* Intuos4/5 13HD/24HD General Pen Eraser */ tool_type = BTN_TOOL_RUBBER; break; @@ -662,7 +667,7 @@ static int wacom_intuos_get_tool_type(int tool_id) case 0x112: case 0x913: /* Intuos3 Airbrush */ case 0x902: /* Intuos4/5 13HD/24HD Airbrush */ - case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */ + case 0x10902: /* Intuos4/5 13HD/24HD Airbrush */ tool_type = BTN_TOOL_AIRBRUSH; break; @@ -693,7 +698,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) (data[6] << 4) + (data[7] >> 4); wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) | - ((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12); + ((data[7] & 0x0f) << 16) | ((data[8] & 0xf0) << 8); wacom->tool[idx] = wacom_intuos_get_tool_type(wacom->id[idx]); @@ -923,7 +928,7 @@ static int wacom_intuos_general(struct wacom_wac *wacom) * don't report events for invalid data */ /* older I4 styli don't work with new Cintiqs */ - if ((!((wacom->id[idx] >> 20) & 0x01) && + if ((!((wacom->id[idx] >> 16) & 0x01) && (features->type == WACOM_21UX2)) || /* Only large Intuos support Lense Cursor */ (wacom->tool[idx] == BTN_TOOL_LENS && @@ -1059,7 +1064,8 @@ static int wacom_intuos_general(struct wacom_wac *wacom) break; } - input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */ + input_report_abs(input, ABS_MISC, + wacom_intuos_id_mangle(wacom->id[idx])); /* report tool id */ input_report_key(input, wacom->tool[idx], 1); input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); wacom->reporting_data = true; -- cgit v1.2.3-55-g7522 From f85c9dc678a5e17f49805035f5b327ed134c4237 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:48 -0700 Subject: HID: wacom: generic: Support tool ID and additional tool types Devices following the new Custom HID mode specification (as well as even some recent component sensors which use the same standard HID usage) are capable of reporting tool ID information that we need to relay to userspace. This patch adds support for reading and relaying the tool type information, which is (unfortunately) split across two usages. We also advertise the existence of tool types beyond BTN_TOOL_PEN that might be available. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++--- drivers/hid/wacom_wac.h | 4 ++- 2 files changed, 71 insertions(+), 5 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index df9d866b9b3f..85e1ad102cdc 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1450,6 +1450,8 @@ static int wacom_equivalent_usage(int usage) if (subpage == WACOM_HID_SP_DIGITIZER || subpage == WACOM_HID_SP_DIGITIZERINFO || usage == WACOM_HID_WD_SENSE || + usage == WACOM_HID_WD_SERIALHI || + usage == WACOM_HID_WD_TOOLTYPE || usage == WACOM_HID_WD_DISTANCE) { return usage; } @@ -1551,6 +1553,17 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, features->quirks |= WACOM_QUIRK_SENSE; wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_PEN, 0); break; + case WACOM_HID_WD_SERIALHI: + wacom_map_usage(input, usage, field, EV_ABS, ABS_MISC, 0); + set_bit(EV_KEY, input->evbit); + input_set_capability(input, EV_KEY, BTN_TOOL_PEN); + input_set_capability(input, EV_KEY, BTN_TOOL_RUBBER); + input_set_capability(input, EV_KEY, BTN_TOOL_BRUSH); + input_set_capability(input, EV_KEY, BTN_TOOL_PENCIL); + input_set_capability(input, EV_KEY, BTN_TOOL_AIRBRUSH); + input_set_capability(input, EV_KEY, BTN_TOOL_MOUSE); + input_set_capability(input, EV_KEY, BTN_TOOL_LENS); + break; case WACOM_HID_WD_FINGERWHEEL: wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0); break; @@ -1587,9 +1600,35 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, case HID_DG_TIPSWITCH: wacom_wac->hid_data.tipswitch |= value; return 0; + case HID_DG_TOOLSERIALNUMBER: + wacom_wac->serial[0] = (wacom_wac->serial[0] & ~0xFFFFFFFF); + wacom_wac->serial[0] |= value; + return 0; case WACOM_HID_WD_SENSE: wacom_wac->hid_data.sense_state = value; return 0; + case WACOM_HID_WD_SERIALHI: + wacom_wac->serial[0] = (wacom_wac->serial[0] & 0xFFFFFFFF); + wacom_wac->serial[0] |= ((__u64)value) << 32; + /* + * Non-USI EMR devices may contain additional tool type + * information here. See WACOM_HID_WD_TOOLTYPE case for + * more details. + */ + if (value >> 20 == 1) { + wacom_wac->id[0] |= value & 0xFFFFF; + } + return 0; + case WACOM_HID_WD_TOOLTYPE: + /* + * Some devices (MobileStudio Pro, and possibly later + * devices as well) do not return the complete tool + * type in their WACOM_HID_WD_TOOLTYPE usage. Use a + * bitwise OR so the complete value can be built + * up over time :( + */ + wacom_wac->id[0] |= value; + return 0; } /* send pen events only when touch is up or forced out @@ -1622,26 +1661,51 @@ static void wacom_wac_pen_report(struct hid_device *hdev, bool prox = wacom_wac->hid_data.inrange_state; bool range = wacom_wac->hid_data.sense_state; - if (!wacom_wac->tool[0] && prox) /* first in prox */ + if (!wacom_wac->tool[0] && prox) { /* first in prox */ /* Going into proximity select tool */ - wacom_wac->tool[0] = wacom_wac->hid_data.invert_state ? - BTN_TOOL_RUBBER : BTN_TOOL_PEN; + if (wacom_wac->hid_data.invert_state) + wacom_wac->tool[0] = BTN_TOOL_RUBBER; + else if (wacom_wac->id[0]) + wacom_wac->tool[0] = wacom_intuos_get_tool_type(wacom_wac->id[0]); + else + wacom_wac->tool[0] = BTN_TOOL_PEN; + } /* keep pen state for touch events */ wacom_wac->shared->stylus_in_proximity = range; if (!delay_pen_events(wacom_wac) && wacom_wac->tool[0]) { + int id = wacom_wac->id[0]; + + /* + * Non-USI EMR tools should have their IDs mangled to + * match the legacy behavior of wacom_intuos_general + */ + if (wacom_wac->serial[0] >> 52 == 1) + id = wacom_intuos_id_mangle(id); + + /* + * To ensure compatibility with xf86-input-wacom, we should + * report the BTN_TOOL_* event prior to the ABS_MISC or + * MSC_SERIAL events. + */ input_report_key(input, BTN_TOUCH, wacom_wac->hid_data.tipswitch); input_report_key(input, wacom_wac->tool[0], prox); + if (wacom_wac->serial[0]) { + input_event(input, EV_MSC, MSC_SERIAL, wacom_wac->serial[0]); + input_report_abs(input, ABS_MISC, id); + } wacom_wac->hid_data.tipswitch = false; input_sync(input); } - if (!prox) + if (!prox) { wacom_wac->tool[0] = 0; + wacom_wac->id[0] = 0; + } } static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index c27b7b40092e..83ddb2a062a1 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -90,6 +90,8 @@ #define WACOM_HID_SP_DIGITIZERINFO 0x00100000 #define WACOM_HID_WD_DIGITIZER (WACOM_HID_UP_WACOMDIGITIZER | 0x01) #define WACOM_HID_WD_SENSE (WACOM_HID_UP_WACOMDIGITIZER | 0x36) +#define WACOM_HID_WD_SERIALHI (WACOM_HID_UP_WACOMDIGITIZER | 0x5c) +#define WACOM_HID_WD_TOOLTYPE (WACOM_HID_UP_WACOMDIGITIZER | 0x77) #define WACOM_HID_WD_DISTANCE (WACOM_HID_UP_WACOMDIGITIZER | 0x0132) #define WACOM_HID_WD_FINGERWHEEL (WACOM_HID_UP_WACOMDIGITIZER | 0x0d03) #define WACOM_HID_WD_DATAMODE (WACOM_HID_UP_WACOMDIGITIZER | 0x1002) @@ -247,7 +249,7 @@ struct wacom_wac { unsigned char data[WACOM_PKGLEN_MAX]; int tool[2]; int id[2]; - __u32 serial[2]; + __u64 serial[2]; bool reporting_data; struct wacom_features features; struct wacom_shared *shared; -- cgit v1.2.3-55-g7522 From e779ef23104869d74f56fbd199a90e76699eb699 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:49 -0700 Subject: HID: wacom: Fix sensor outbounds and redefine as offsets from each edge Many of Wacom's display tablets include an "outbound" area where pen digitizing is possible but outside of the display area. To ensure that pen coordinates are mapped to the correct on-screen location, the driver sets the minimum and maximum axis values of X and Y to those coordinates which coincide with the screen edge. These values are simply the hardware minimum/maximum plus/minus the outbound size for a particular edge. When outbound support was added/updated in ac414da, fa77034, and ecd618d, we decided to have the wacom_features structs store the desired minimum and maximum values directly. In hindsight, this was perhaps not the best choice since it has allowed minor errors to crop up unnoticed. Some tablets have had their coordinates over-corrected (e.g. most of the devices "fixed" in ecd618d were already adjusted in ac414da), while others never had a correction applied (e.g. the ISDv5 325, whose declared maximum the hardware maximum instead of the outbound maximum). A less error-prone method of handling the outbound is to let the driver calculate the correct minimum/maximum values by providing it with both the actual hardware maximums and the size of the outbound on each edge. These values are more easy to verify as correct since the values can be trivially compared against specifications. This patch reverts the declared maximum values to the actual hardware maximums, e.g. as declared prior to ac414da (values for these and other display tablets that were subsuquently introduced have been verified against specs). Per-edge outbound sizes are stored in the wacom_features struct as offset_{left,right,top,bottom} and used in combination with the hardware ranges to calculate effective axis ranges for ABS_X and ABS_Y. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 62 ++++++++++++++++++++++++++++++++----------------- drivers/hid/wacom_wac.h | 6 +++-- 2 files changed, 45 insertions(+), 23 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 85e1ad102cdc..5046071f2e9e 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2674,10 +2674,12 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(ABS_MISC, input_dev->absbit); - input_set_abs_params(input_dev, ABS_X, features->x_min, - features->x_max, features->x_fuzz, 0); - input_set_abs_params(input_dev, ABS_Y, features->y_min, - features->y_max, features->y_fuzz, 0); + input_set_abs_params(input_dev, ABS_X, 0 + features->offset_left, + features->x_max - features->offset_right, + features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_Y, 0 + features->offset_top, + features->y_max - features->offset_bottom, + features->y_fuzz, 0); input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, features->pressure_fuzz, 0); @@ -3389,26 +3391,30 @@ static const struct wacom_features wacom_features_0x317 = INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 16, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0xF4 = - { "Wacom Cintiq 24HD", 104080, 65200, 2047, 63, + { "Wacom Cintiq 24HD", 104480, 65600, 2047, 63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 16, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0xF8 = - { "Wacom Cintiq 24HD touch", 104080, 65200, 2047, 63, /* Pen */ + { "Wacom Cintiq 24HD touch", 104480, 65600, 2047, 63, /* Pen */ WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 16, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 }; static const struct wacom_features wacom_features_0xF6 = { "Wacom Cintiq 24HD touch", .type = WACOM_24HDT, /* Touch */ .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf8, .touch_max = 10, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x32A = - { "Wacom Cintiq 27QHD", 119740, 67520, 2047, 63, + { "Wacom Cintiq 27QHD", 120140, 67920, 2047, 63, WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 0, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0x32B = - { "Wacom Cintiq 27QHD touch", 119740, 67520, 2047, 63, + { "Wacom Cintiq 27QHD touch", 120140, 67920, 2047, 63, WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 0, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x32C }; static const struct wacom_features wacom_features_0x32C = { "Wacom Cintiq 27QHD touch", .type = WACOM_27QHDT, @@ -3423,13 +3429,15 @@ static const struct wacom_features wacom_features_0xC6 = { "Wacom Cintiq 12WX", 53020, 33440, 1023, 63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 10 }; static const struct wacom_features wacom_features_0x304 = - { "Wacom Cintiq 13HD", 59152, 33448, 1023, 63, + { "Wacom Cintiq 13HD", 59552, 33848, 1023, 63, WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0x333 = - { "Wacom Cintiq 13HD touch", 59152, 33448, 2047, 63, + { "Wacom Cintiq 13HD touch", 59552, 33848, 2047, 63, WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x335 }; static const struct wacom_features wacom_features_0x335 = { "Wacom Cintiq 13HD touch", .type = WACOM_24HDT, /* Touch */ @@ -3446,42 +3454,50 @@ static const struct wacom_features wacom_features_0xF0 = { "Wacom DTU1631", 34623, 19553, 511, 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xFB = - { "Wacom DTU1031", 21896, 13760, 511, 0, + { "Wacom DTU1031", 22096, 13960, 511, 0, DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4, + WACOM_DTU_OFFSET, WACOM_DTU_OFFSET, WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; static const struct wacom_features wacom_features_0x32F = - { "Wacom DTU1031X", 22472, 12728, 511, 0, + { "Wacom DTU1031X", 22672, 12928, 511, 0, DTUSX, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 0, + WACOM_DTU_OFFSET, WACOM_DTU_OFFSET, WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; static const struct wacom_features wacom_features_0x336 = - { "Wacom DTU1141", 23472, 13203, 1023, 0, + { "Wacom DTU1141", 23672, 13403, 1023, 0, DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4, + WACOM_DTU_OFFSET, WACOM_DTU_OFFSET, WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; static const struct wacom_features wacom_features_0x57 = - { "Wacom DTK2241", 95640, 54060, 2047, 63, + { "Wacom DTK2241", 95840, 54260, 2047, 63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 6, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0x59 = /* Pen */ - { "Wacom DTH2242", 95640, 54060, 2047, 63, + { "Wacom DTH2242", 95840, 54260, 2047, 63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 6, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D }; static const struct wacom_features wacom_features_0x5D = /* Touch */ { "Wacom DTH2242", .type = WACOM_24HDT, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0xCC = - { "Wacom Cintiq 21UX2", 86800, 65200, 2047, 63, + { "Wacom Cintiq 21UX2", 87200, 65600, 2047, 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0xFA = - { "Wacom Cintiq 22HD", 95440, 53860, 2047, 63, + { "Wacom Cintiq 22HD", 95840, 54260, 2047, 63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0x5B = - { "Wacom Cintiq 22HDT", 95440, 53860, 2047, 63, + { "Wacom Cintiq 22HDT", 95840, 54260, 2047, 63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e }; static const struct wacom_features wacom_features_0x5E = { "Wacom Cintiq 22HDT", .type = WACOM_24HDT, @@ -3625,18 +3641,20 @@ static const struct wacom_features wacom_features_0x6004 = { "ISD-V4", 12800, 8000, 255, 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x307 = - { "Wacom ISDv5 307", 59152, 33448, 2047, 63, + { "Wacom ISDv5 307", 59552, 33848, 2047, 63, CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 }; static const struct wacom_features wacom_features_0x309 = { "Wacom ISDv5 309", .type = WACOM_24HDT, /* Touch */ .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x30A = - { "Wacom ISDv5 30A", 59152, 33448, 2047, 63, + { "Wacom ISDv5 30A", 59552, 33848, 2047, 63, CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30C }; static const struct wacom_features wacom_features_0x30C = { "Wacom ISDv5 30C", .type = WACOM_24HDT, /* Touch */ @@ -3652,6 +3670,7 @@ 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, + 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, @@ -3681,8 +3700,9 @@ static const struct wacom_features wacom_features_0x33E = 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_0x343 = - { "Wacom DTK1651", 34616, 19559, 1023, 0, + { "Wacom DTK1651", 34816, 19759, 1023, 0, DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4, + WACOM_DTU_OFFSET, WACOM_DTU_OFFSET, WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; 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 83ddb2a062a1..5c5c6891b832 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -181,8 +181,10 @@ struct wacom_features { int x_resolution; int y_resolution; int numbered_buttons; - int x_min; - int y_min; + int offset_left; + int offset_right; + int offset_top; + int offset_bottom; int device_type; int x_phy; int y_phy; -- cgit v1.2.3-55-g7522 From 345857bb493fbff15632a6bdf04713163ccd6fe6 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:50 -0700 Subject: HID: wacom: generic: Add support for sensor offsets Many of Wacom's display tablets include an "outbound" area where pen digitizing is possible but outside of the display area. To accommodate such sensors in the HID_GENERIC codepath, we add support for the necessary vendor-defined HID feature usages and adjust the min/max values of the X and Y axes accordingly, similar to what is done in the non-generic codepath. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 38 ++++++++++++++++++++++++++++++-------- drivers/hid/wacom_wac.c | 40 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/wacom_wac.h | 4 ++++ 3 files changed, 74 insertions(+), 8 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index b2e2471bbef6..b9779bcbd140 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -122,6 +122,7 @@ static void wacom_feature_mapping(struct hid_device *hdev, struct hid_data *hid_data = &wacom->wacom_wac.hid_data; u8 *data; int ret; + int n; switch (usage->hid) { case HID_DG_CONTACTMAX: @@ -180,6 +181,27 @@ static void wacom_feature_mapping(struct hid_device *hdev, wacom->wacom_wac.mode_value = 0; } break; + case WACOM_HID_WD_OFFSETLEFT: + case WACOM_HID_WD_OFFSETTOP: + case WACOM_HID_WD_OFFSETRIGHT: + case WACOM_HID_WD_OFFSETBOTTOM: + /* read manually */ + n = hid_report_len(field->report); + data = hid_alloc_report_buf(field->report, GFP_KERNEL); + if (!data) + break; + data[0] = field->report->id; + ret = wacom_get_report(hdev, HID_FEATURE_REPORT, + data, n, WAC_CMD_RETRIES); + if (ret == n) { + ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, + data, n, 0); + } else { + hid_warn(hdev, "%s: could not retrieve sensor offsets\n", + __func__); + } + kfree(data); + break; } } @@ -718,11 +740,6 @@ static int wacom_add_shared_data(struct hid_device *hdev) return retval; } - if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) - wacom_wac->shared->touch = hdev; - else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN) - wacom_wac->shared->pen = hdev; - out: mutex_unlock(&wacom_udev_list_lock); return retval; @@ -2019,6 +2036,10 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) if (error) goto fail; + error = wacom_add_shared_data(hdev); + if (error) + goto fail; + /* * Bamboo Pad has a generic hid handling for the Pen, and we switch it * into debug mode for the touch part. @@ -2059,9 +2080,10 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) wacom_update_name(wacom, wireless ? " (WL)" : ""); - error = wacom_add_shared_data(hdev); - if (error) - goto fail; + if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) + wacom_wac->shared->touch = hdev; + else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN) + wacom_wac->shared->pen = hdev; if (!(features->device_type & WACOM_DEVICETYPE_WL_MONITOR) && (features->quirks & WACOM_QUIRK_BATTERY)) { diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 5046071f2e9e..3bb6dd6e4eea 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1468,6 +1468,9 @@ static int wacom_equivalent_usage(int usage) static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, struct hid_field *field, __u8 type, __u16 code, int fuzz) { + struct wacom *wacom = input_get_drvdata(input); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct wacom_features *features = &wacom_wac->features; int fmin = field->logical_minimum; int fmax = field->logical_maximum; unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid); @@ -1477,6 +1480,15 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, resolution_code = ABS_RZ; } + if (equivalent_usage == HID_GD_X) { + fmin += features->offset_left; + fmax -= features->offset_right; + } + if (equivalent_usage == HID_GD_Y) { + fmin += features->offset_top; + fmax -= features->offset_bottom; + } + usage->type = type; usage->code = code; @@ -1629,6 +1641,34 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, */ wacom_wac->id[0] |= value; return 0; + case WACOM_HID_WD_OFFSETLEFT: + if (features->offset_left && value != features->offset_left) + hid_warn(hdev, "%s: overriding exising left offset " + "%d -> %d\n", __func__, value, + features->offset_left); + features->offset_left = value; + return 0; + case WACOM_HID_WD_OFFSETRIGHT: + if (features->offset_right && value != features->offset_right) + hid_warn(hdev, "%s: overriding exising right offset " + "%d -> %d\n", __func__, value, + features->offset_right); + features->offset_right = value; + return 0; + case WACOM_HID_WD_OFFSETTOP: + if (features->offset_top && value != features->offset_top) + hid_warn(hdev, "%s: overriding exising top offset " + "%d -> %d\n", __func__, value, + features->offset_top); + features->offset_top = value; + return 0; + case WACOM_HID_WD_OFFSETBOTTOM: + if (features->offset_bottom && value != features->offset_bottom) + hid_warn(hdev, "%s: overriding exising bottom offset " + "%d -> %d\n", __func__, value, + features->offset_bottom); + features->offset_bottom = value; + return 0; } /* send pen events only when touch is up or forced out diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 5c5c6891b832..b4c3c6425b85 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -94,6 +94,10 @@ #define WACOM_HID_WD_TOOLTYPE (WACOM_HID_UP_WACOMDIGITIZER | 0x77) #define WACOM_HID_WD_DISTANCE (WACOM_HID_UP_WACOMDIGITIZER | 0x0132) #define WACOM_HID_WD_FINGERWHEEL (WACOM_HID_UP_WACOMDIGITIZER | 0x0d03) +#define WACOM_HID_WD_OFFSETLEFT (WACOM_HID_UP_WACOMDIGITIZER | 0x0d30) +#define WACOM_HID_WD_OFFSETTOP (WACOM_HID_UP_WACOMDIGITIZER | 0x0d31) +#define WACOM_HID_WD_OFFSETRIGHT (WACOM_HID_UP_WACOMDIGITIZER | 0x0d32) +#define WACOM_HID_WD_OFFSETBOTTOM (WACOM_HID_UP_WACOMDIGITIZER | 0x0d33) #define WACOM_HID_WD_DATAMODE (WACOM_HID_UP_WACOMDIGITIZER | 0x1002) #define WACOM_HID_UP_G9 0xff090000 #define WACOM_HID_G9_PEN (WACOM_HID_UP_G9 | 0x02) -- cgit v1.2.3-55-g7522 From 5922e613256f159f8d53d99b17379f362e544541 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:51 -0700 Subject: HID: wacom: generic: Introduce pad support As with usages for the pen, the Custom HID specificiation includes usages for the pad. Here we add functions to map and handle most of the pad usages present on the MobileStudio Pro. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 143 ++++++++++++++++++++++++++++++++++++++++++------ drivers/hid/wacom_wac.h | 19 +++++++ 2 files changed, 146 insertions(+), 16 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 3bb6dd6e4eea..70de1fa930cc 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -41,6 +41,8 @@ MODULE_PARM_DESC(touch_arbitration, " on (Y) off (N)"); static void wacom_report_numbered_buttons(struct input_dev *input_dev, int button_count, int mask); +static int wacom_numbered_button_to_key(int n); + /* * Percent of battery capacity for Graphire. * 8th value means AC online and show 100% capacity. @@ -1447,12 +1449,16 @@ static int wacom_equivalent_usage(int usage) int subpage = (usage & 0xFF00) << 8; int subusage = (usage & 0xFF); - if (subpage == WACOM_HID_SP_DIGITIZER || + if (subpage == WACOM_HID_SP_PAD || + subpage == WACOM_HID_SP_BUTTON || + subpage == WACOM_HID_SP_DIGITIZER || subpage == WACOM_HID_SP_DIGITIZERINFO || usage == WACOM_HID_WD_SENSE || usage == WACOM_HID_WD_SERIALHI || usage == WACOM_HID_WD_TOOLTYPE || - usage == WACOM_HID_WD_DISTANCE) { + usage == WACOM_HID_WD_DISTANCE || + usage == WACOM_HID_WD_TOUCHRING || + usage == WACOM_HID_WD_TOUCHRINGSTATUS) { return usage; } @@ -1509,6 +1515,98 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, } } +static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct wacom_features *features = &wacom_wac->features; + struct input_dev *input = wacom_wac->pad_input; + unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); + + switch (equivalent_usage) { + case WACOM_HID_WD_ACCELEROMETER_X: + __set_bit(INPUT_PROP_ACCELEROMETER, input->propbit); + wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 0); + break; + case WACOM_HID_WD_ACCELEROMETER_Y: + __set_bit(INPUT_PROP_ACCELEROMETER, input->propbit); + wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 0); + break; + case WACOM_HID_WD_ACCELEROMETER_Z: + __set_bit(INPUT_PROP_ACCELEROMETER, input->propbit); + wacom_map_usage(input, usage, field, EV_ABS, ABS_Z, 0); + break; + case WACOM_HID_WD_BUTTONHOME: + case WACOM_HID_WD_BUTTONUP: + case WACOM_HID_WD_BUTTONDOWN: + case WACOM_HID_WD_BUTTONLEFT: + case WACOM_HID_WD_BUTTONRIGHT: + wacom_map_usage(input, usage, field, EV_KEY, + wacom_numbered_button_to_key(features->numbered_buttons), + 0); + features->numbered_buttons++; + break; + case WACOM_HID_WD_TOUCHRING: + wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0); + break; + } + + switch (equivalent_usage & 0xfffffff0) { + case WACOM_HID_WD_EXPRESSKEY00: + wacom_map_usage(input, usage, field, EV_KEY, + wacom_numbered_button_to_key(features->numbered_buttons), + 0); + features->numbered_buttons++; + break; + } +} + +static int wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct input_dev *input = wacom_wac->pad_input; + unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); + + if (wacom_equivalent_usage(field->physical) == HID_DG_TABLETFUNCTIONKEY) { + wacom_wac->hid_data.inrange_state |= value; + } + + if (equivalent_usage != WACOM_HID_WD_TOUCHRINGSTATUS) + input_event(input, usage->type, usage->code, value); + + return 0; +} + +static void wacom_wac_pad_pre_report(struct hid_device *hdev, + struct hid_report *report) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + + wacom_wac->hid_data.inrange_state = 0; +} + +static void wacom_wac_pad_report(struct hid_device *hdev, + struct hid_report *report) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct input_dev *input = wacom_wac->pad_input; + bool active = wacom_wac->hid_data.inrange_state != 0; + + /* + * don't report prox for events like accelerometer + * or battery status + */ + if (wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) + input_event(input, EV_ABS, ABS_MISC, active ? PAD_DEVICE_ID : 0); + + input_sync(input); +} + static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { @@ -1946,10 +2044,11 @@ void wacom_wac_usage_mapping(struct hid_device *hdev, /* currently, only direct devices have proper hid report descriptors */ features->device_type |= WACOM_DEVICETYPE_DIRECT; - if (WACOM_PEN_FIELD(field)) + if (WACOM_PAD_FIELD(field)) + return wacom_wac_pad_usage_mapping(hdev, field, usage); + else if (WACOM_PEN_FIELD(field)) return wacom_wac_pen_usage_mapping(hdev, field, usage); - - if (WACOM_FINGER_FIELD(field)) + else if (WACOM_FINGER_FIELD(field)) return wacom_wac_finger_usage_mapping(hdev, field, usage); } @@ -1961,10 +2060,11 @@ int wacom_wac_event(struct hid_device *hdev, struct hid_field *field, if (wacom->wacom_wac.features.type != HID_GENERIC) return 0; - if (WACOM_PEN_FIELD(field)) + if (WACOM_PAD_FIELD(field)) + return wacom_wac_pad_event(hdev, field, usage, value); + else if (WACOM_PEN_FIELD(field)) return wacom_wac_pen_event(hdev, field, usage, value); - - if (WACOM_FINGER_FIELD(field)) + else if (WACOM_FINGER_FIELD(field)) return wacom_wac_finger_event(hdev, field, usage, value); return 0; @@ -1998,18 +2098,20 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report) if (wacom_wac->features.type != HID_GENERIC) return; - if (WACOM_PEN_FIELD(field)) + if (WACOM_PAD_FIELD(field)) + wacom_wac_pad_pre_report(hdev, report); + else if (WACOM_PEN_FIELD(field)) wacom_wac_pen_pre_report(hdev, report); - - if (WACOM_FINGER_FIELD(field)) + else if (WACOM_FINGER_FIELD(field)) wacom_wac_finger_pre_report(hdev, report); wacom_report_events(hdev, report); - if (WACOM_PEN_FIELD(field)) + if (WACOM_PAD_FIELD(field)) + return wacom_wac_pad_report(hdev, report); + else if (WACOM_PEN_FIELD(field)) return wacom_wac_pen_report(hdev, report); - - if (WACOM_FINGER_FIELD(field)) + else if (WACOM_FINGER_FIELD(field)) return wacom_wac_finger_report(hdev, report); } @@ -2583,6 +2685,8 @@ void wacom_setup_device_quirks(struct wacom *wacom) struct wacom_features *features = &wacom->wacom_wac.features; /* The pen and pad share the same interface on most devices */ + if (features->numbered_buttons > 0) + features->device_type |= WACOM_DEVICETYPE_PAD; if (features->type == GRAPHIRE_BT || features->type == WACOM_G4 || features->type == DTUS || (features->type >= INTUOS3S && features->type <= WACOM_MO)) { @@ -3104,8 +3208,12 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, __set_bit(ABS_MISC, input_dev->absbit); /* kept for making legacy xf86-input-wacom accepting the pad */ - input_set_abs_params(input_dev, ABS_X, 0, 1, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 0, 1, 0, 0); + if (!(input_dev->absinfo && (input_dev->absinfo[ABS_X].minimum || + input_dev->absinfo[ABS_X].maximum))) + input_set_abs_params(input_dev, ABS_X, 0, 1, 0, 0); + if (!(input_dev->absinfo && (input_dev->absinfo[ABS_Y].minimum || + input_dev->absinfo[ABS_Y].maximum))) + input_set_abs_params(input_dev, ABS_Y, 0, 1, 0, 0); /* kept for making udev and libwacom accepting the pad */ __set_bit(BTN_STYLUS, input_dev->keybit); @@ -3225,6 +3333,9 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); break; + case HID_GENERIC: + break; + default: /* no pad supported */ return -ENODEV; diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index b4c3c6425b85..1f7c4a86d91b 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -86,19 +86,34 @@ #define WACOM_DEVICETYPE_DIRECT 0x0010 #define WACOM_HID_UP_WACOMDIGITIZER 0xff0d0000 +#define WACOM_HID_SP_PAD 0x00040000 +#define WACOM_HID_SP_BUTTON 0x00090000 #define WACOM_HID_SP_DIGITIZER 0x000d0000 #define WACOM_HID_SP_DIGITIZERINFO 0x00100000 #define WACOM_HID_WD_DIGITIZER (WACOM_HID_UP_WACOMDIGITIZER | 0x01) #define WACOM_HID_WD_SENSE (WACOM_HID_UP_WACOMDIGITIZER | 0x36) +#define WACOM_HID_WD_DIGITIZERFNKEYS (WACOM_HID_UP_WACOMDIGITIZER | 0x39) #define WACOM_HID_WD_SERIALHI (WACOM_HID_UP_WACOMDIGITIZER | 0x5c) #define WACOM_HID_WD_TOOLTYPE (WACOM_HID_UP_WACOMDIGITIZER | 0x77) #define WACOM_HID_WD_DISTANCE (WACOM_HID_UP_WACOMDIGITIZER | 0x0132) +#define WACOM_HID_WD_TOUCHRING (WACOM_HID_UP_WACOMDIGITIZER | 0x0138) +#define WACOM_HID_WD_TOUCHRINGSTATUS (WACOM_HID_UP_WACOMDIGITIZER | 0x0139) +#define WACOM_HID_WD_ACCELEROMETER_X (WACOM_HID_UP_WACOMDIGITIZER | 0x0401) +#define WACOM_HID_WD_ACCELEROMETER_Y (WACOM_HID_UP_WACOMDIGITIZER | 0x0402) +#define WACOM_HID_WD_ACCELEROMETER_Z (WACOM_HID_UP_WACOMDIGITIZER | 0x0403) +#define WACOM_HID_WD_EXPRESSKEY00 (WACOM_HID_UP_WACOMDIGITIZER | 0x0910) +#define WACOM_HID_WD_BUTTONHOME (WACOM_HID_UP_WACOMDIGITIZER | 0x0990) +#define WACOM_HID_WD_BUTTONUP (WACOM_HID_UP_WACOMDIGITIZER | 0x0991) +#define WACOM_HID_WD_BUTTONDOWN (WACOM_HID_UP_WACOMDIGITIZER | 0x0992) +#define WACOM_HID_WD_BUTTONLEFT (WACOM_HID_UP_WACOMDIGITIZER | 0x0993) +#define WACOM_HID_WD_BUTTONRIGHT (WACOM_HID_UP_WACOMDIGITIZER | 0x0994) #define WACOM_HID_WD_FINGERWHEEL (WACOM_HID_UP_WACOMDIGITIZER | 0x0d03) #define WACOM_HID_WD_OFFSETLEFT (WACOM_HID_UP_WACOMDIGITIZER | 0x0d30) #define WACOM_HID_WD_OFFSETTOP (WACOM_HID_UP_WACOMDIGITIZER | 0x0d31) #define WACOM_HID_WD_OFFSETRIGHT (WACOM_HID_UP_WACOMDIGITIZER | 0x0d32) #define WACOM_HID_WD_OFFSETBOTTOM (WACOM_HID_UP_WACOMDIGITIZER | 0x0d33) #define WACOM_HID_WD_DATAMODE (WACOM_HID_UP_WACOMDIGITIZER | 0x1002) +#define WACOM_HID_WD_DIGITIZERINFO (WACOM_HID_UP_WACOMDIGITIZER | 0x1013) #define WACOM_HID_UP_G9 0xff090000 #define WACOM_HID_G9_PEN (WACOM_HID_UP_G9 | 0x02) #define WACOM_HID_G9_TOUCHSCREEN (WACOM_HID_UP_G9 | 0x11) @@ -106,6 +121,10 @@ #define WACOM_HID_G11_PEN (WACOM_HID_UP_G11 | 0x02) #define WACOM_HID_G11_TOUCHSCREEN (WACOM_HID_UP_G11 | 0x11) +#define WACOM_PAD_FIELD(f) (((f)->physical == HID_DG_TABLETFUNCTIONKEY) || \ + ((f)->physical == WACOM_HID_WD_DIGITIZERFNKEYS) || \ + ((f)->physical == WACOM_HID_WD_DIGITIZERINFO)) + #define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \ ((f)->physical == HID_DG_STYLUS) || \ ((f)->physical == HID_DG_PEN) || \ -- cgit v1.2.3-55-g7522 From 93aab7fa4f8091d8fe2aed7e79a650fc1c084512 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:52 -0700 Subject: HID: wacom: generic: Add support for battery status on pen and pad interfaces Adds support for usages that may appear on the pen or pad interface which report the state of the tablet battery. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 41 ++++++++++++++++++++++++++++++++++++++++- drivers/hid/wacom_wac.h | 6 ++++++ include/linux/hid.h | 1 + 3 files changed, 47 insertions(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 70de1fa930cc..f3edecf52c06 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1525,6 +1525,10 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); switch (equivalent_usage) { + case WACOM_HID_WD_BATTERY_LEVEL: + case WACOM_HID_WD_BATTERY_CHARGING: + features->quirks |= WACOM_QUIRK_BATTERY; + break; case WACOM_HID_WD_ACCELEROMETER_X: __set_bit(INPUT_PROP_ACCELEROMETER, input->propbit); wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 0); @@ -1574,8 +1578,25 @@ static int wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field, wacom_wac->hid_data.inrange_state |= value; } - if (equivalent_usage != WACOM_HID_WD_TOUCHRINGSTATUS) + switch (equivalent_usage) { + case WACOM_HID_WD_BATTERY_LEVEL: + wacom_wac->hid_data.battery_capacity = value; + wacom_wac->hid_data.bat_connected = 1; + return 0; + + case WACOM_HID_WD_BATTERY_CHARGING: + wacom_wac->hid_data.bat_charging = value; + wacom_wac->hid_data.ps_connected = value; + wacom_wac->hid_data.bat_connected = 1; + return 0; + + case WACOM_HID_WD_TOUCHRINGSTATUS: + return 0; + + default: input_event(input, usage->type, usage->code, value); + break; + } return 0; } @@ -1594,6 +1615,7 @@ static void wacom_wac_pad_report(struct hid_device *hdev, { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct wacom_features *features = &wacom_wac->features; struct input_dev *input = wacom_wac->pad_input; bool active = wacom_wac->hid_data.inrange_state != 0; @@ -1604,6 +1626,16 @@ static void wacom_wac_pad_report(struct hid_device *hdev, if (wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) input_event(input, EV_ABS, ABS_MISC, active ? PAD_DEVICE_ID : 0); + if (features->quirks & WACOM_QUIRK_BATTERY) { + int capacity = wacom_wac->hid_data.battery_capacity; + bool charging = wacom_wac->hid_data.bat_charging; + bool connected = wacom_wac->hid_data.bat_connected; + bool powered = wacom_wac->hid_data.ps_connected; + + wacom_notify_battery(wacom_wac, capacity, charging, + connected, powered); + } + input_sync(input); } @@ -1633,6 +1665,9 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, case HID_DG_INRANGE: wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_PEN, 0); break; + case HID_DG_BATTERYSTRENGTH: + features->quirks |= WACOM_QUIRK_BATTERY; + break; case HID_DG_INVERT: wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_RUBBER, 0); @@ -1703,6 +1738,10 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, if (!(features->quirks & WACOM_QUIRK_SENSE)) wacom_wac->hid_data.sense_state = value; return 0; + case HID_DG_BATTERYSTRENGTH: + wacom_wac->hid_data.battery_capacity = value; + wacom_wac->hid_data.bat_connected = 1; + break; case HID_DG_INVERT: wacom_wac->hid_data.invert_state = value; return 0; diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 1f7c4a86d91b..7418c9715d31 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -101,6 +101,8 @@ #define WACOM_HID_WD_ACCELEROMETER_X (WACOM_HID_UP_WACOMDIGITIZER | 0x0401) #define WACOM_HID_WD_ACCELEROMETER_Y (WACOM_HID_UP_WACOMDIGITIZER | 0x0402) #define WACOM_HID_WD_ACCELEROMETER_Z (WACOM_HID_UP_WACOMDIGITIZER | 0x0403) +#define WACOM_HID_WD_BATTERY_CHARGING (WACOM_HID_UP_WACOMDIGITIZER | 0x0404) +#define WACOM_HID_WD_BATTERY_LEVEL (WACOM_HID_UP_WACOMDIGITIZER | 0x043b) #define WACOM_HID_WD_EXPRESSKEY00 (WACOM_HID_UP_WACOMDIGITIZER | 0x0910) #define WACOM_HID_WD_BUTTONHOME (WACOM_HID_UP_WACOMDIGITIZER | 0x0990) #define WACOM_HID_WD_BUTTONUP (WACOM_HID_UP_WACOMDIGITIZER | 0x0991) @@ -257,6 +259,10 @@ struct hid_data { int last_slot_field; int num_expected; int num_received; + int battery_capacity; + int bat_charging; + int bat_connected; + int ps_connected; }; struct wacom_remote_data { diff --git a/include/linux/hid.h b/include/linux/hid.h index e712101a1670..3baa2f962e48 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -231,6 +231,7 @@ struct hid_item { #define HID_DG_TAP 0x000d0035 #define HID_DG_TABLETFUNCTIONKEY 0x000d0039 #define HID_DG_PROGRAMCHANGEKEY 0x000d003a +#define HID_DG_BATTERYSTRENGTH 0x000d003b #define HID_DG_INVERT 0x000d003c #define HID_DG_TILT_X 0x000d003d #define HID_DG_TILT_Y 0x000d003e -- cgit v1.2.3-55-g7522 From bf78adcb6dc681624015a4390f0e5f656cb0270b Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:53 -0700 Subject: HID: wacom: generic: Extend pad support The HID specification that the MobileStudio Pro follows includes usages for several values that would be good to support so that future devices "just work" out of the box. Extend the HID_GENERIC pad codepath to handle these usages. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 15 +++++++++++++++ drivers/hid/wacom_wac.h | 5 +++++ 2 files changed, 20 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index f3edecf52c06..0723ba8906fa 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1457,6 +1457,8 @@ static int wacom_equivalent_usage(int usage) usage == WACOM_HID_WD_SERIALHI || usage == WACOM_HID_WD_TOOLTYPE || usage == WACOM_HID_WD_DISTANCE || + usage == WACOM_HID_WD_TOUCHSTRIP || + usage == WACOM_HID_WD_TOUCHSTRIP2 || usage == WACOM_HID_WD_TOUCHRING || usage == WACOM_HID_WD_TOUCHRINGSTATUS) { return usage; @@ -1512,6 +1514,9 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, case EV_MSC: input_set_capability(input, EV_MSC, code); break; + case EV_SW: + input_set_capability(input, EV_SW, code); + break; } } @@ -1546,11 +1551,21 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, case WACOM_HID_WD_BUTTONDOWN: case WACOM_HID_WD_BUTTONLEFT: case WACOM_HID_WD_BUTTONRIGHT: + case WACOM_HID_WD_BUTTONCENTER: wacom_map_usage(input, usage, field, EV_KEY, wacom_numbered_button_to_key(features->numbered_buttons), 0); features->numbered_buttons++; break; + case WACOM_HID_WD_TOUCHONOFF: + wacom_map_usage(input, usage, field, EV_SW, SW_MUTE_DEVICE, 0); + break; + case WACOM_HID_WD_TOUCHSTRIP: + wacom_map_usage(input, usage, field, EV_ABS, ABS_RX, 0); + break; + case WACOM_HID_WD_TOUCHSTRIP2: + wacom_map_usage(input, usage, field, EV_ABS, ABS_RY, 0); + break; case WACOM_HID_WD_TOUCHRING: wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0); break; diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 7418c9715d31..a54a3017a23f 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -96,6 +96,8 @@ #define WACOM_HID_WD_SERIALHI (WACOM_HID_UP_WACOMDIGITIZER | 0x5c) #define WACOM_HID_WD_TOOLTYPE (WACOM_HID_UP_WACOMDIGITIZER | 0x77) #define WACOM_HID_WD_DISTANCE (WACOM_HID_UP_WACOMDIGITIZER | 0x0132) +#define WACOM_HID_WD_TOUCHSTRIP (WACOM_HID_UP_WACOMDIGITIZER | 0x0136) +#define WACOM_HID_WD_TOUCHSTRIP2 (WACOM_HID_UP_WACOMDIGITIZER | 0x0137) #define WACOM_HID_WD_TOUCHRING (WACOM_HID_UP_WACOMDIGITIZER | 0x0138) #define WACOM_HID_WD_TOUCHRINGSTATUS (WACOM_HID_UP_WACOMDIGITIZER | 0x0139) #define WACOM_HID_WD_ACCELEROMETER_X (WACOM_HID_UP_WACOMDIGITIZER | 0x0401) @@ -104,11 +106,14 @@ #define WACOM_HID_WD_BATTERY_CHARGING (WACOM_HID_UP_WACOMDIGITIZER | 0x0404) #define WACOM_HID_WD_BATTERY_LEVEL (WACOM_HID_UP_WACOMDIGITIZER | 0x043b) #define WACOM_HID_WD_EXPRESSKEY00 (WACOM_HID_UP_WACOMDIGITIZER | 0x0910) +#define WACOM_HID_WD_EXPRESSKEYCAP00 (WACOM_HID_UP_WACOMDIGITIZER | 0x0950) #define WACOM_HID_WD_BUTTONHOME (WACOM_HID_UP_WACOMDIGITIZER | 0x0990) #define WACOM_HID_WD_BUTTONUP (WACOM_HID_UP_WACOMDIGITIZER | 0x0991) #define WACOM_HID_WD_BUTTONDOWN (WACOM_HID_UP_WACOMDIGITIZER | 0x0992) #define WACOM_HID_WD_BUTTONLEFT (WACOM_HID_UP_WACOMDIGITIZER | 0x0993) #define WACOM_HID_WD_BUTTONRIGHT (WACOM_HID_UP_WACOMDIGITIZER | 0x0994) +#define WACOM_HID_WD_BUTTONCENTER (WACOM_HID_UP_WACOMDIGITIZER | 0x0995) +#define WACOM_HID_WD_TOUCHONOFF (WACOM_HID_UP_WACOMDIGITIZER | 0x0996) #define WACOM_HID_WD_FINGERWHEEL (WACOM_HID_UP_WACOMDIGITIZER | 0x0d03) #define WACOM_HID_WD_OFFSETLEFT (WACOM_HID_UP_WACOMDIGITIZER | 0x0d30) #define WACOM_HID_WD_OFFSETTOP (WACOM_HID_UP_WACOMDIGITIZER | 0x0d31) -- cgit v1.2.3-55-g7522 From c0bf57411b6b03dd72d4d20f362ba1ca72244834 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:54 -0700 Subject: HID: input: Recognize ABS_WHEEL in hidinput_calc_abs_res The "Steering" usage (HID_UP_SIMULATION | 0xc8) is defined in HUT 1.12 as follows: "A steering wheel is a single degree-of-freedom device that rotates about an axis. The zero position is always the neutral or 'straight ahead' position, with positive values turning clockwise and negative values turning counterclockwise. If the Coordinate Values Wrap attribute is set, the steering wheel can be turned past 360 degrees." The hidinput_configure_usage function canonically maps this usage to the ABS_WHEEL axis, but hidinput_calc_abs_res does not recognize this axis as one for which it can calculate a resolution. This effectively prevents wheels from being assigned a proper resolution that userspace can use to determine the precise angle of input. This commit adds ABS_WHEEL as a rotational axis to hidinput_calc_abs_res. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index fb9ace1cef8b..c4e935b5fe74 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -253,6 +253,7 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) case ABS_RX: case ABS_RY: case ABS_RZ: + case ABS_WHEEL: case ABS_TILT_X: case ABS_TILT_Y: if (field->unit == 0x14) { /* If degrees */ -- cgit v1.2.3-55-g7522 From b2554000f5b5d2a3a368d09c6debf7da64901fcf Mon Sep 17 00:00:00 2001 From: Marcel Hasler Date: Thu, 3 Nov 2016 19:47:26 +0100 Subject: HID: usbhid: Add quirks for Mayflash/Dragonrise GameCube and PS3 adapters All known gamepad adapters by Mayflash (identified as Dragonrise) need HID_QUIRK_MULTI_INPUT to split them up into four input devices. Without this quirk those adapters are falsely recognized as tablets. Fixes bug 115841 (https://bugzilla.kernel.org/show_bug.cgi?id=115841). Signed-off-by: Marcel Hasler Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 6 ++++-- drivers/hid/usbhid/hid-quirks.c | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 6cfb5cacc253..642e64861654 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -314,8 +314,10 @@ #define USB_VENDOR_ID_DMI 0x0c0b #define USB_DEVICE_ID_DMI_ENC 0x5fab -#define USB_VENDOR_ID_DRAGONRISE 0x0079 -#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800 +#define USB_VENDOR_ID_DRAGONRISE 0x0079 +#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800 +#define USB_DEVICE_ID_DRAGONRISE_PS3 0x1801 +#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE 0x1843 #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 354d49ea36dd..05f6f61f0213 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -81,6 +81,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ELAN, HID_ANY_ID, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, -- cgit v1.2.3-55-g7522 From f8690450f3d0d415f42c94f7e69258d8ba54ff29 Mon Sep 17 00:00:00 2001 From: Marcel Hasler Date: Thu, 3 Nov 2016 19:47:42 +0100 Subject: HID: Add new force feedback driver for Mayflash game controller adapters Add a new module named hid-mf that implements force feedback for game controller adapters manufactured by Mayflash. Currently only the PS3 adapter is supported, other adapters still need to be tested. Signed-off-by: Marcel Hasler Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 8 +++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 3 + drivers/hid/hid-mf.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 drivers/hid/hid-mf.c (limited to 'drivers/hid') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index cd4599c0523b..1530d28ecc61 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -512,6 +512,14 @@ config HID_MAGICMOUSE Say Y here if you want support for the multi-touch features of the Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad. +config HID_MAYFLASH + tristate "Mayflash game controller adapter force feedback" + depends on HID + select INPUT_FF_MEMLESS + ---help--- + Say Y here if you have HJZ Mayflash PS3 game controller adapters + and want to enable force feedback support. + config HID_MICROSOFT tristate "Microsoft non-fully HID-compliant devices" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 86b2b5785fd2..c0453f196c06 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o +obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2b89c701076f..39694a514726 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1883,6 +1883,9 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DELCOM, USB_DEVICE_ID_DELCOM_VISUAL_IND) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, +#if IS_ENABLED(CONFIG_HID_MAYFLASH) + { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3) }, +#endif { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_WN) }, { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_FA) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, diff --git a/drivers/hid/hid-mf.c b/drivers/hid/hid-mf.c new file mode 100644 index 000000000000..d9090765a6e5 --- /dev/null +++ b/drivers/hid/hid-mf.c @@ -0,0 +1,166 @@ +/* + * Force feedback support for Mayflash game controller adapters. + * + * These devices are manufactured by Mayflash but identify themselves + * using the vendor ID of DragonRise Inc. + * + * Tested with: + * 0079:1801 "DragonRise Inc. Mayflash PS3 Game Controller Adapter" + * + * The following adapters probably work too, but need to be tested: + * 0079:1800 "DragonRise Inc. Mayflash WIIU Game Controller Adapter" + * 0079:1843 "DragonRise Inc. Mayflash GameCube Game Controller Adapter" + * + * Copyright (c) 2016 Marcel Hasler + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +struct mf_device { + struct hid_report *report; +}; + +static int mf_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct mf_device *mf = data; + int strong, weak; + + strong = effect->u.rumble.strong_magnitude; + weak = effect->u.rumble.weak_magnitude; + + dbg_hid("Called with 0x%04x 0x%04x.\n", strong, weak); + + strong = strong * 0xff / 0xffff; + weak = weak * 0xff / 0xffff; + + dbg_hid("Running with 0x%02x 0x%02x.\n", strong, weak); + + mf->report->field[0]->value[0] = weak; + mf->report->field[0]->value[1] = strong; + hid_hw_request(hid, mf->report, HID_REQ_SET_REPORT); + + return 0; +} + +static int mf_init(struct hid_device *hid) +{ + struct mf_device *mf; + + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + + struct list_head *report_ptr; + struct hid_report *report; + + struct list_head *input_ptr = &hid->inputs; + struct hid_input *input; + + struct input_dev *dev; + + int error; + + /* Setup each of the four inputs */ + list_for_each(report_ptr, report_list) { + report = list_entry(report_ptr, struct hid_report, list); + + if (report->maxfield < 1 || report->field[0]->report_count < 2) { + hid_err(hid, "Invalid report, this should never happen!\n"); + return -ENODEV; + } + + if (list_is_last(input_ptr, &hid->inputs)) { + hid_err(hid, "Missing input, this should never happen!\n"); + return -ENODEV; + } + + input_ptr = input_ptr->next; + input = list_entry(input_ptr, struct hid_input, list); + + mf = kzalloc(sizeof(struct mf_device), GFP_KERNEL); + if (!mf) + return -ENOMEM; + + dev = input->input; + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, mf, mf_play); + if (error) { + kfree(mf); + return error; + } + + mf->report = report; + mf->report->field[0]->value[0] = 0x00; + mf->report->field[0]->value[1] = 0x00; + hid_hw_request(hid, mf->report, HID_REQ_SET_REPORT); + } + + hid_info(hid, "Force feedback for HJZ Mayflash game controller " + "adapters by Marcel Hasler \n"); + + return 0; +} + +static int mf_probe(struct hid_device *hid, const struct hid_device_id *id) +{ + int error; + + dev_dbg(&hid->dev, "Mayflash HID hardware probe...\n"); + + /* Split device into four inputs */ + hid->quirks |= HID_QUIRK_MULTI_INPUT; + + error = hid_parse(hid); + if (error) { + hid_err(hid, "HID parse failed.\n"); + return error; + } + + error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (error) { + hid_err(hid, "HID hw start failed\n"); + return error; + } + + error = mf_init(hid); + if (error) { + hid_err(hid, "Force feedback init failed.\n"); + hid_hw_stop(hid); + return error; + } + + return 0; +} + +static const struct hid_device_id mf_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3), }, + { } +}; +MODULE_DEVICE_TABLE(hid, mf_devices); + +static struct hid_driver mf_driver = { + .name = "hid_mf", + .id_table = mf_devices, + .probe = mf_probe, +}; +module_hid_driver(mf_driver); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3-55-g7522 From 15607a3ad4272ed6f781a5250479c87746746dda Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Sat, 5 Nov 2016 16:15:03 +0100 Subject: HID: intel-ish-hid: initialize ts_format.reserved ts_format.reserved is not used anywhere yet, but the compiler generates a warning when the struct's (uninitialized) field is being copied around drivers/hid/intel-ish-hid/ipc/ipc.c: In function ‘write_ipc_from_queue’: drivers/hid/intel-ish-hid/ipc/ipc.c:316: warning: ‘ts_format.reserved’ may be used uninitialized in this function Avoid this by force-initializing the field to zero. Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/ipc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hid') diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index e2517c11e0ee..37f069749672 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -310,6 +310,7 @@ static int write_ipc_from_queue(struct ishtp_device *dev) ((uint32_t)tv_utc.tv_usec); ts_format.ts1_source = HOST_SYSTEM_TIME_USEC; ts_format.ts2_source = HOST_UTC_TIME_USEC; + ts_format.reserved = 0; time_update.primary_host_time = usec_system; time_update.secondary_host_time = usec_utc; -- cgit v1.2.3-55-g7522 From 71af01a8c85ad89449209594133bdfdfaa9f1e2a Mon Sep 17 00:00:00 2001 From: HungNien Chen Date: Thu, 10 Nov 2016 11:47:13 +0800 Subject: HID: i2c-hid: add a simple quirk to fix device defects Certain devices produced by Weida Tech need to have a wakeup command sent to them before powering on. The call itself will come back with error, but the device can be powered on afterwards. [jkosina@suse.cz: rewrite changelog] [jkosina@suse.cz: remove unused device ID addition] Signed-off-by: HungNien Chen Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 4 +++ drivers/hid/i2c-hid/i2c-hid.c | 57 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index cd59c79eebdd..c36ec6d01b8b 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1030,6 +1030,10 @@ #define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH 0x0500 #define USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET 0x0502 +#define USB_VENDOR_ID_WEIDA 0x2575 +#define USB_DEVICE_ID_WEIDA_8752 0xC300 +#define USB_DEVICE_ID_WEIDA_8755 0xC301 + #define USB_VENDOR_ID_WISEGROUP 0x0925 #define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005 #define USB_DEVICE_ID_SUPER_JOY_BOX_3 0x8888 diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index fe6b4e0eab4a..ce518795cb30 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -40,6 +40,11 @@ #include +#include "../hid-ids.h" + +/* quirks to control the device */ +#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0) + /* flags */ #define I2C_HID_STARTED 0 #define I2C_HID_RESET_PENDING 1 @@ -142,6 +147,7 @@ struct i2c_hid { char *argsbuf; /* Command arguments buffer */ unsigned long flags; /* device flags */ + unsigned long quirks; /* Various quirks */ wait_queue_head_t wait; /* For waiting the interrupt */ @@ -151,6 +157,39 @@ struct i2c_hid { struct mutex reset_lock; }; +static const struct i2c_hid_quirks { + __u16 idVendor; + __u16 idProduct; + __u32 quirks; +} i2c_hid_quirks[] = { + { USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8752, + I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV }, + { USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8755, + I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV }, + { 0, 0 } +}; + +/* + * i2c_hid_lookup_quirk: return any quirks associated with a I2C HID device + * @idVendor: the 16-bit vendor ID + * @idProduct: the 16-bit product ID + * + * Returns: a u32 quirks value. + */ +static u32 i2c_hid_lookup_quirk(const u16 idVendor, const u16 idProduct) +{ + u32 quirks = 0; + int n; + + for (n = 0; i2c_hid_quirks[n].idVendor; n++) + if (i2c_hid_quirks[n].idVendor == idVendor && + (i2c_hid_quirks[n].idProduct == (__u16)HID_ANY_ID || + i2c_hid_quirks[n].idProduct == idProduct)) + quirks = i2c_hid_quirks[n].quirks; + + return quirks; +} + static int __i2c_hid_command(struct i2c_client *client, const struct i2c_hid_cmd *command, u8 reportID, u8 reportType, u8 *args, int args_len, @@ -343,11 +382,27 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state) i2c_hid_dbg(ihid, "%s\n", __func__); + /* + * Some devices require to send a command to wakeup before power on. + * The call will get a return value (EREMOTEIO) but device will be + * triggered and activated. After that, it goes like a normal device. + */ + if (power_state == I2C_HID_PWR_ON && + ihid->quirks & I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV) { + ret = i2c_hid_command(client, &hid_set_power_cmd, NULL, 0); + + /* Device was already activated */ + if (!ret) + goto set_pwr_exit; + } + ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state, 0, NULL, 0, NULL, 0); + if (ret) dev_err(&client->dev, "failed to change power setting.\n"); +set_pwr_exit: return ret; } @@ -1032,6 +1087,8 @@ static int i2c_hid_probe(struct i2c_client *client, client->name, hid->vendor, hid->product); strlcpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys)); + ihid->quirks = i2c_hid_lookup_quirk(hid->vendor, hid->product); + ret = hid_add_device(hid); if (ret) { if (ret != -ENODEV) -- cgit v1.2.3-55-g7522 From a35f09b84941fa186acbdd9b7266c7ef4660003c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 10 Nov 2016 22:25:39 +0300 Subject: HID: wacom: Don't clear bits unintentionally This is trying to clear the lower 32 bits but the type is wrong so it clears everything. Signed-off-by: Dan Carpenter Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 0723ba8906fa..1cf4608ae929 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1765,7 +1765,7 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, wacom_wac->hid_data.tipswitch |= value; return 0; case HID_DG_TOOLSERIALNUMBER: - wacom_wac->serial[0] = (wacom_wac->serial[0] & ~0xFFFFFFFF); + wacom_wac->serial[0] = (wacom_wac->serial[0] & ~0xFFFFFFFFULL); wacom_wac->serial[0] |= value; return 0; case WACOM_HID_WD_SENSE: -- cgit v1.2.3-55-g7522 From 9ce9a123d9e70b764be3a3df92c61c2639b7acff Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 11 Nov 2016 15:05:52 -0800 Subject: HID: wacom: Declare tool ID 0x84a as an Intuos eraser The eraser end of the 8K pen available for the MobileStudio Pro has a tool ID of 0x84a. The 'wacom_intuos_get_tool_type' function does not currently recognize this ID, causing it to return BTN_TOOL_PEN rather than BTN_TOOL_RUBBER. This does not cause a problem for the MobileStudio Pro since, as a HID_GENERIC device, the driver relies on the state of the HID_DG_INVERT usage instead. It would, however, cause problems if the pen is used with devices that use the traditional 'wacom_intuos_irq' codepath instead. Signed-off-by: Jason Gerecke Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 1cf4608ae929..f1ef3e153765 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -645,6 +645,7 @@ static int wacom_intuos_get_tool_type(int tool_id) break; case 0x82a: /* Eraser */ + case 0x84a: case 0x85a: case 0x91a: case 0xd1a: -- cgit v1.2.3-55-g7522 From 0edffe655a52d7ce7c093212bc0cce6576084a8e Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Tue, 15 Nov 2016 13:02:05 +0100 Subject: HID: udraw-ps3: Add support for the uDraw tablet for PS3 This adds support for the THQ uDraw tablet for the PS3, as 4 separate device nodes, so that user-space can easily consume events coming from the hardware. Note that the touchpad two-finger support is fairly unreliable, and a right-click can only be achieved with a two-finger tap with the two fingers slightly apart (about 1cm should be enough). Tested-by: Bastien Nocera Signed-off-by: Bastien Nocera Signed-off-by: Jiri Kosina --- MAINTAINERS | 6 + drivers/hid/Kconfig | 7 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 + drivers/hid/hid-udraw-ps3.c | 474 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 492 insertions(+) create mode 100644 drivers/hid/hid-udraw-ps3.c (limited to 'drivers/hid') diff --git a/MAINTAINERS b/MAINTAINERS index 411e3b87b8c2..cbc1533f5b82 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12340,6 +12340,12 @@ S: Maintained F: Documentation/filesystems/udf.txt F: fs/udf/ +UDRAW TABLET +M: Bastien Nocera +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/hid/hid-udraw.c + UFS FILESYSTEM M: Evgeniy Dushistov S: Maintained diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index cd4599c0523b..91025b3ff18d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -861,6 +861,13 @@ config THRUSTMASTER_FF a THRUSTMASTER Dual Trigger 3-in-1 or a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel. +config HID_UDRAW_PS3 + tristate "THQ PS3 uDraw tablet" + depends on HID + ---help--- + Say Y here if you want to use the THQ uDraw gaming tablet for + the PS3. + config HID_WACOM tristate "Wacom Intuos/Graphire tablet support (USB)" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 86b2b5785fd2..b4ed502050b7 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -96,6 +96,7 @@ obj-$(CONFIG_HID_TIVO) += hid-tivo.o obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o +obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o obj-$(CONFIG_HID_LED) += hid-led.o obj-$(CONFIG_HID_XINMO) += hid-xinmo.o obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2b89c701076f..3611ec77ddb9 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2086,6 +2086,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 575aa65436d1..e8166568a900 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -959,6 +959,9 @@ #define USB_VENDOR_ID_THINGM 0x27b8 #define USB_DEVICE_ID_BLINK1 0x01ed +#define USB_VENDOR_ID_THQ 0x20d6 +#define USB_DEVICE_ID_THQ_PS3_UDRAW 0xcb17 + #define USB_VENDOR_ID_THRUSTMASTER 0x044f #define USB_VENDOR_ID_TIVO 0x150a diff --git a/drivers/hid/hid-udraw-ps3.c b/drivers/hid/hid-udraw-ps3.c new file mode 100644 index 000000000000..1f68b0b5f12e --- /dev/null +++ b/drivers/hid/hid-udraw-ps3.c @@ -0,0 +1,474 @@ +/* + * HID driver for THQ PS3 uDraw tablet + * + * Copyright (C) 2016 Red Hat Inc. All Rights Reserved + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include "hid-ids.h" + +MODULE_AUTHOR("Bastien Nocera "); +MODULE_DESCRIPTION("PS3 uDraw tablet driver"); +MODULE_LICENSE("GPL"); + +/* + * Protocol information from: + * http://brandonw.net/udraw/ + * and the source code of: + * https://vvvv.org/contribution/udraw-hid + */ + +/* + * The device is setup with multiple input devices: + * - the touch area which works as a touchpad + * - the tablet area which works as a touchpad/drawing tablet + * - a joypad with a d-pad, and 7 buttons + * - an accelerometer device + */ + +enum { + TOUCH_NONE, + TOUCH_PEN, + TOUCH_FINGER, + TOUCH_TWOFINGER +}; + +enum { + AXIS_X, + AXIS_Y, + AXIS_Z +}; + +/* + * Accelerometer min/max values + * in order, X, Y and Z + */ +struct { + int min; + int max; +} accel_limits[] = { + [AXIS_X] = { 490, 534 }, + [AXIS_Y] = { 490, 534 }, + [AXIS_Z] = { 492, 536 } +}; + +#define DEVICE_NAME "THQ uDraw Game Tablet for PS3" +/* resolution in pixels */ +#define RES_X 1920 +#define RES_Y 1080 +/* size in mm */ +#define WIDTH 160 +#define HEIGHT 90 +#define PRESSURE_OFFSET 113 +#define MAX_PRESSURE (255 - PRESSURE_OFFSET) + +struct udraw { + struct input_dev *joy_input_dev; + struct input_dev *touch_input_dev; + struct input_dev *pen_input_dev; + struct input_dev *accel_input_dev; + struct hid_device *hdev; + + /* + * The device's two-finger support is pretty unreliable, as + * the device could report a single touch when the two fingers + * are too close together, and the distance between fingers, even + * though reported is not in the same unit as the touches. + * + * We'll make do without it, and try to report the first touch + * as reliably as possible. + */ + int last_one_finger_x; + int last_one_finger_y; + int last_two_finger_x; + int last_two_finger_y; +}; + +static int clamp_accel(int axis, int offset) +{ + axis = clamp(axis, + accel_limits[offset].min, + accel_limits[offset].max); + axis = (axis - accel_limits[offset].min) / + ((accel_limits[offset].max - + accel_limits[offset].min) * 0xFF); + return axis; +} + +static int udraw_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int len) +{ + struct udraw *udraw = hid_get_drvdata(hdev); + int touch; + int x, y, z; + + if (len != 27) + return 0; + + if (data[11] == 0x00) + touch = TOUCH_NONE; + else if (data[11] == 0x40) + touch = TOUCH_PEN; + else if (data[11] == 0x80) + touch = TOUCH_FINGER; + else + touch = TOUCH_TWOFINGER; + + /* joypad */ + input_report_key(udraw->joy_input_dev, BTN_WEST, data[0] & 1); + input_report_key(udraw->joy_input_dev, BTN_SOUTH, !!(data[0] & 2)); + input_report_key(udraw->joy_input_dev, BTN_EAST, !!(data[0] & 4)); + input_report_key(udraw->joy_input_dev, BTN_NORTH, !!(data[0] & 8)); + + input_report_key(udraw->joy_input_dev, BTN_SELECT, !!(data[1] & 1)); + input_report_key(udraw->joy_input_dev, BTN_START, !!(data[1] & 2)); + input_report_key(udraw->joy_input_dev, BTN_MODE, !!(data[1] & 16)); + + x = y = 0; + switch (data[2]) { + case 0x0: + y = -127; + break; + case 0x1: + y = -127; + x = 127; + break; + case 0x2: + x = 127; + break; + case 0x3: + y = 127; + x = 127; + break; + case 0x4: + y = 127; + break; + case 0x5: + y = 127; + x = -127; + break; + case 0x6: + x = -127; + break; + case 0x7: + y = -127; + x = -127; + break; + default: + break; + } + + input_report_abs(udraw->joy_input_dev, ABS_X, x); + input_report_abs(udraw->joy_input_dev, ABS_Y, y); + + input_sync(udraw->joy_input_dev); + + /* For pen and touchpad */ + x = y = 0; + if (touch != TOUCH_NONE) { + if (data[15] != 0x0F) + x = data[15] * 256 + data[17]; + if (data[16] != 0x0F) + y = data[16] * 256 + data[18]; + } + + if (touch == TOUCH_FINGER) { + /* Save the last one-finger touch */ + udraw->last_one_finger_x = x; + udraw->last_one_finger_y = y; + udraw->last_two_finger_x = -1; + udraw->last_two_finger_y = -1; + } else if (touch == TOUCH_TWOFINGER) { + /* + * We have a problem because x/y is the one for the + * second finger but we want the first finger given + * to user-space otherwise it'll look as if it jumped. + * + * See the udraw struct definition for why this was + * implemented this way. + */ + if (udraw->last_two_finger_x == -1) { + /* Save the position of the 2nd finger */ + udraw->last_two_finger_x = x; + udraw->last_two_finger_y = y; + + x = udraw->last_one_finger_x; + y = udraw->last_one_finger_y; + } else { + /* + * Offset the 2-finger coords using the + * saved data from the first finger + */ + x = x - (udraw->last_two_finger_x + - udraw->last_one_finger_x); + y = y - (udraw->last_two_finger_y + - udraw->last_one_finger_y); + } + } + + /* touchpad */ + if (touch == TOUCH_FINGER || touch == TOUCH_TWOFINGER) { + input_report_key(udraw->touch_input_dev, BTN_TOUCH, 1); + input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER, + touch == TOUCH_FINGER); + input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, + touch == TOUCH_TWOFINGER); + + input_report_abs(udraw->touch_input_dev, ABS_X, x); + input_report_abs(udraw->touch_input_dev, ABS_Y, y); + } else { + input_report_key(udraw->touch_input_dev, BTN_TOUCH, 0); + input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER, 0); + input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, 0); + } + input_sync(udraw->touch_input_dev); + + /* pen */ + if (touch == TOUCH_PEN) { + int level; + + level = clamp(data[13] - PRESSURE_OFFSET, + 0, MAX_PRESSURE); + + input_report_key(udraw->pen_input_dev, BTN_TOUCH, (level != 0)); + input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 1); + input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, level); + input_report_abs(udraw->pen_input_dev, ABS_X, x); + input_report_abs(udraw->pen_input_dev, ABS_Y, y); + } else { + input_report_key(udraw->pen_input_dev, BTN_TOUCH, 0); + input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 0); + input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, 0); + } + input_sync(udraw->pen_input_dev); + + /* accel */ + x = (data[19] + (data[20] << 8)); + x = clamp_accel(x, AXIS_X); + y = (data[21] + (data[22] << 8)); + y = clamp_accel(y, AXIS_Y); + z = (data[23] + (data[24] << 8)); + z = clamp_accel(z, AXIS_Z); + input_report_abs(udraw->accel_input_dev, ABS_X, x); + input_report_abs(udraw->accel_input_dev, ABS_Y, y); + input_report_abs(udraw->accel_input_dev, ABS_Z, z); + input_sync(udraw->accel_input_dev); + + /* let hidraw and hiddev handle the report */ + return 0; +} + +static int udraw_open(struct input_dev *dev) +{ + struct udraw *udraw = input_get_drvdata(dev); + + return hid_hw_open(udraw->hdev); +} + +static void udraw_close(struct input_dev *dev) +{ + struct udraw *udraw = input_get_drvdata(dev); + + hid_hw_close(udraw->hdev); +} + +static struct input_dev *allocate_and_setup(struct hid_device *hdev, + const char *name) +{ + struct input_dev *input_dev; + + input_dev = devm_input_allocate_device(&hdev->dev); + if (!input_dev) + return NULL; + + input_dev->name = name; + input_dev->phys = hdev->phys; + input_dev->dev.parent = &hdev->dev; + input_dev->open = udraw_open; + input_dev->close = udraw_close; + input_dev->uniq = hdev->uniq; + input_dev->id.bustype = hdev->bus; + input_dev->id.vendor = hdev->vendor; + input_dev->id.product = hdev->product; + input_dev->id.version = hdev->version; + input_set_drvdata(input_dev, hid_get_drvdata(hdev)); + + return input_dev; +} + +static bool udraw_setup_touch(struct udraw *udraw, + struct hid_device *hdev) +{ + struct input_dev *input_dev; + + input_dev = allocate_and_setup(hdev, DEVICE_NAME " Touchpad"); + if (!input_dev) + return false; + + input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); + + input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0); + input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH); + input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0); + input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT); + + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(BTN_TOOL_FINGER, input_dev->keybit); + set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + + set_bit(INPUT_PROP_POINTER, input_dev->propbit); + + udraw->touch_input_dev = input_dev; + + return true; +} + +static bool udraw_setup_pen(struct udraw *udraw, + struct hid_device *hdev) +{ + struct input_dev *input_dev; + + input_dev = allocate_and_setup(hdev, DEVICE_NAME " Pen"); + if (!input_dev) + return false; + + input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); + + input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0); + input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH); + input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0); + input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT); + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, MAX_PRESSURE, 0, 0); + + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(BTN_TOOL_PEN, input_dev->keybit); + + set_bit(INPUT_PROP_POINTER, input_dev->propbit); + + udraw->pen_input_dev = input_dev; + + return true; +} + +static bool udraw_setup_accel(struct udraw *udraw, + struct hid_device *hdev) +{ + struct input_dev *input_dev; + + input_dev = allocate_and_setup(hdev, DEVICE_NAME " Accelerometer"); + if (!input_dev) + return false; + + input_dev->evbit[0] = BIT(EV_ABS); + + /* 1G accel is reported as ~256, so clamp to 2G */ + input_set_abs_params(input_dev, ABS_X, -512, 512, 0, 0); + input_set_abs_params(input_dev, ABS_Y, -512, 512, 0, 0); + input_set_abs_params(input_dev, ABS_Z, -512, 512, 0, 0); + + set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit); + + udraw->accel_input_dev = input_dev; + + return true; +} + +static bool udraw_setup_joypad(struct udraw *udraw, + struct hid_device *hdev) +{ + struct input_dev *input_dev; + + input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad"); + if (!input_dev) + return false; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + set_bit(BTN_SOUTH, input_dev->keybit); + set_bit(BTN_NORTH, input_dev->keybit); + set_bit(BTN_EAST, input_dev->keybit); + set_bit(BTN_WEST, input_dev->keybit); + set_bit(BTN_SELECT, input_dev->keybit); + set_bit(BTN_START, input_dev->keybit); + set_bit(BTN_MODE, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_X, -127, 127, 0, 0); + input_set_abs_params(input_dev, ABS_Y, -127, 127, 0, 0); + + udraw->joy_input_dev = input_dev; + + return true; +} + +static int udraw_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct udraw *udraw; + int ret; + + udraw = devm_kzalloc(&hdev->dev, sizeof(struct udraw), GFP_KERNEL); + if (!udraw) + return -ENOMEM; + + udraw->hdev = hdev; + udraw->last_two_finger_x = -1; + udraw->last_two_finger_y = -1; + + hid_set_drvdata(hdev, udraw); + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + return ret; + } + + if (!udraw_setup_joypad(udraw, hdev) || + !udraw_setup_touch(udraw, hdev) || + !udraw_setup_pen(udraw, hdev) || + !udraw_setup_accel(udraw, hdev)) { + hid_err(hdev, "could not allocate interfaces\n"); + return -ENOMEM; + } + + ret = input_register_device(udraw->joy_input_dev) || + input_register_device(udraw->touch_input_dev) || + input_register_device(udraw->pen_input_dev) || + input_register_device(udraw->accel_input_dev); + if (ret) { + hid_err(hdev, "failed to register interfaces\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + return 0; +} + +static const struct hid_device_id udraw_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) }, + { } +}; +MODULE_DEVICE_TABLE(hid, udraw_devices); + +static struct hid_driver udraw_driver = { + .name = "hid-udraw", + .id_table = udraw_devices, + .raw_event = udraw_raw_event, + .probe = udraw_probe, +}; +module_hid_driver(udraw_driver); -- cgit v1.2.3-55-g7522 From 9c5dcd723171ad96f9e25ebf69514b74577cade7 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 15 Nov 2016 14:23:17 +0100 Subject: HID: udraw-ps3: accel_limits is local to the driver And as such should be marked static to avoid global namespace pollution. Signed-off-by: Jiri Kosina --- drivers/hid/hid-udraw-ps3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-udraw-ps3.c b/drivers/hid/hid-udraw-ps3.c index 1f68b0b5f12e..88ea390c10ad 100644 --- a/drivers/hid/hid-udraw-ps3.c +++ b/drivers/hid/hid-udraw-ps3.c @@ -54,7 +54,7 @@ enum { * Accelerometer min/max values * in order, X, Y and Z */ -struct { +static struct { int min; int max; } accel_limits[] = { -- cgit v1.2.3-55-g7522 From fa39baa9700c86a73b2203eab92dbba7eb2d4d69 Mon Sep 17 00:00:00 2001 From: Even Xu Date: Fri, 11 Nov 2016 09:40:13 +0800 Subject: HID: intel-ish-hid: ipc: remove unused macro The macro timed_wait_for() in utils.h isn't used in current ipc driver, so remove it for avoiding confusion. Signed-off-by: Even Xu Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/utils.h | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/intel-ish-hid/ipc/utils.h b/drivers/hid/intel-ish-hid/ipc/utils.h index 5a82123dc7b4..dc39dfe2464e 100644 --- a/drivers/hid/intel-ish-hid/ipc/utils.h +++ b/drivers/hid/intel-ish-hid/ipc/utils.h @@ -18,27 +18,6 @@ #define WAIT_FOR_SEND_SLICE (HZ / 10) #define WAIT_FOR_CONNECT_SLICE (HZ / 10) -/* - * Waits for specified event when a thread that triggers event can't signal - * Also, waits *at_least* `timeinc` after condition is satisfied - */ -#define timed_wait_for(timeinc, condition) \ - do { \ - int completed = 0; \ - do { \ - unsigned long j; \ - int done = 0; \ - \ - completed = (condition); \ - for (j = jiffies, done = 0; !done; ) { \ - schedule_timeout(timeinc); \ - if (time_is_before_eq_jiffies(j + timeinc)) \ - done = 1; \ - } \ - } while (!(completed)); \ - } while (0) - - /* * Waits for specified event when a thread that triggers event * can't signal with timeout (use whenever we may hang) -- cgit v1.2.3-55-g7522 From 7ede704d746e7c02219cdd44abe22c0b606e70db Mon Sep 17 00:00:00 2001 From: Even Xu Date: Fri, 11 Nov 2016 09:40:23 +0800 Subject: HID: intel-ish-hid: ipc: change timed_wait_for_timeout() to be a function The macro timed_wait_for_timeout() only be used in one function, so move this marco from header file and change it to a function in ipc.c, where it is used. Signed-off-by: Even Xu Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/ipc.c | 62 ++++++++++++++++++++++++++++++++--- drivers/hid/intel-ish-hid/ipc/utils.h | 43 ------------------------ 2 files changed, 57 insertions(+), 48 deletions(-) delete mode 100644 drivers/hid/intel-ish-hid/ipc/utils.h (limited to 'drivers/hid') diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index 0c9ac4d5d850..3d46cc0eec70 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -19,7 +19,6 @@ #include #include "client.h" #include "hw-ish.h" -#include "utils.h" #include "hbm.h" /* For FW reset flow */ @@ -427,6 +426,59 @@ static int ipc_send_mng_msg(struct ishtp_device *dev, uint32_t msg_code, sizeof(uint32_t) + size); } +#define WAIT_FOR_FW_RDY 0x1 +#define WAIT_FOR_INPUT_RDY 0x2 + +/** + * timed_wait_for_timeout() - wait special event with timeout + * @dev: ISHTP device pointer + * @condition: indicate the condition for waiting + * @timeinc: time slice for every wait cycle, in ms + * @timeout: time in ms for timeout + * + * This function will check special event to be ready in a loop, the loop + * period is specificd in timeinc. Wait timeout will causes failure. + * + * Return: 0 for success else failure code + */ +static int timed_wait_for_timeout(struct ishtp_device *dev, int condition, + unsigned int timeinc, unsigned int timeout) +{ + bool complete = false; + int ret; + + do { + if (condition == WAIT_FOR_FW_RDY) { + complete = ishtp_fw_is_ready(dev); + } else if (condition == WAIT_FOR_INPUT_RDY) { + complete = ish_is_input_ready(dev); + } else { + ret = -EINVAL; + goto out; + } + + if (!complete) { + unsigned long left_time; + + left_time = msleep_interruptible(timeinc); + timeout -= (timeinc - left_time); + } + } while (!complete && timeout > 0); + + if (complete) + ret = 0; + else + ret = -EBUSY; + +out: + return ret; +} + +#define TIME_SLICE_FOR_FW_RDY_MS 100 +#define TIME_SLICE_FOR_INPUT_RDY_MS 100 +#define TIMEOUT_FOR_FW_RDY_MS 2000 +#define TIMEOUT_FOR_INPUT_RDY_MS 2000 + /** * ish_fw_reset_handler() - FW reset handler * @dev: ishtp device pointer @@ -456,8 +508,8 @@ static int ish_fw_reset_handler(struct ishtp_device *dev) ishtp_reset_handler(dev); if (!ish_is_input_ready(dev)) - timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, - ish_is_input_ready(dev), (2 * HZ)); + timed_wait_for_timeout(dev, WAIT_FOR_INPUT_RDY, + TIME_SLICE_FOR_INPUT_RDY_MS, TIMEOUT_FOR_INPUT_RDY_MS); /* ISH FW is dead */ if (!ish_is_input_ready(dev)) @@ -472,8 +524,8 @@ static int ish_fw_reset_handler(struct ishtp_device *dev) sizeof(uint32_t)); /* Wait for ISH FW'es ILUP and ISHTP_READY */ - timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, ishtp_fw_is_ready(dev), - (2 * HZ)); + timed_wait_for_timeout(dev, WAIT_FOR_FW_RDY, + TIME_SLICE_FOR_FW_RDY_MS, TIMEOUT_FOR_FW_RDY_MS); if (!ishtp_fw_is_ready(dev)) { /* ISH FW is dead */ uint32_t ish_status; diff --git a/drivers/hid/intel-ish-hid/ipc/utils.h b/drivers/hid/intel-ish-hid/ipc/utils.h deleted file mode 100644 index dc39dfe2464e..000000000000 --- a/drivers/hid/intel-ish-hid/ipc/utils.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Utility macros of ISH - * - * Copyright (c) 2014-2016, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ -#ifndef UTILS__H -#define UTILS__H - -#define WAIT_FOR_SEND_SLICE (HZ / 10) -#define WAIT_FOR_CONNECT_SLICE (HZ / 10) - -/* - * Waits for specified event when a thread that triggers event - * can't signal with timeout (use whenever we may hang) - */ -#define timed_wait_for_timeout(timeinc, condition, timeout) \ - do { \ - int t = timeout; \ - do { \ - unsigned long j; \ - int done = 0; \ - \ - for (j = jiffies, done = 0; !done; ) { \ - schedule_timeout(timeinc); \ - if (time_is_before_eq_jiffies(j + timeinc)) \ - done = 1; \ - } \ - t -= timeinc; \ - if (t <= 0) \ - break; \ - } while (!(condition)); \ - } while (0) - -#endif /* UTILS__H */ -- cgit v1.2.3-55-g7522 From e5b56aa7906d6995007ffe11ecc181dd876dabd3 Mon Sep 17 00:00:00 2001 From: Even Xu Date: Fri, 11 Nov 2016 09:40:33 +0800 Subject: HID: intel-ish-hid: ipc: use msleep_interrupt() for wait set_current_task() must be called before schedule_timeout(), for this driver, in order to avoid incorrect usage, use msleep_interrupt() instead. Signed-off-by: Even Xu Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/ipc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index 3d46cc0eec70..b9bf04a3f267 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -539,6 +539,8 @@ static int ish_fw_reset_handler(struct ishtp_device *dev) return 0; } +#define TIMEOUT_FOR_HW_RDY_MS 300 + /** * ish_fw_reset_work_fn() - FW reset worker function * @unused: not used @@ -552,7 +554,7 @@ static void fw_reset_work_fn(struct work_struct *unused) rv = ish_fw_reset_handler(ishtp_dev); if (!rv) { /* ISH is ILUP & ISHTP-ready. Restart ISHTP */ - schedule_timeout(HZ / 3); + msleep_interruptible(TIMEOUT_FOR_HW_RDY_MS); ishtp_dev->recvd_hw_ready = 1; wake_up_interruptible(&ishtp_dev->wait_hw_ready); -- cgit v1.2.3-55-g7522 From 2ae3986b84e9d325bc92a1efbcf0c6b0f5016b35 Mon Sep 17 00:00:00 2001 From: Daniel Keller Date: Tue, 22 Nov 2016 16:24:05 +0100 Subject: HID: microsoft: Add Surface 4 type cover pro 4 not JP versions Adding support for not JP versions of the Microsoft Surface 4 Type Cover Pro [jkosina@suse.cz: The identical patch has been sent by Jeff Farthing, so I am including his signoff as well] Signed-off-by: Jeff Farthing Signed-off-by: Daniel Keller Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 4 ++++ drivers/hid/hid-ids.h | 2 ++ drivers/hid/hid-microsoft.c | 4 ++++ drivers/hid/usbhid/hid-quirks.c | 2 ++ 4 files changed, 12 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2b89c701076f..1bbe32966d21 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -727,6 +727,8 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP || + hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 || + hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2 || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 || hid->product == USB_DEVICE_ID_MS_POWER_COVER) && @@ -1983,6 +1985,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 6cfb5cacc253..5198a4525466 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -717,6 +717,8 @@ #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd +#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 0x07e4 +#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2 0x07e8 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP 0x07e9 #define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de #define USB_DEVICE_ID_MS_POWER_COVER 0x07da diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index c6cd392e9f99..5e592f04095b 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -280,6 +280,10 @@ static const struct hid_device_id ms_devices[] = { .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4), + .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2), + .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP), .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 354d49ea36dd..a67e90a8b5be 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -100,6 +100,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS }, -- cgit v1.2.3-55-g7522 From da809197a919942ab6ee0d008c20a011872181b1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 25 Nov 2016 11:03:43 +0100 Subject: HID: sensor-hub add quirk for Microsoft Surface 3 One more device requiring a quirk :/ [jkosina@suse.cz: update comment based on Bastien's remark] Signed-off-by: Benjamin Tissoires Tested-by: Bastien Nocera Signed-off-by: Jiri Kosina --- drivers/hid/hid-sensor-hub.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 60875625cbdf..0646278db7b3 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -795,6 +795,9 @@ static const struct hid_device_id sensor_hub_devices[] = { { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2), .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, + 0x07bd), /* Microsoft Surface 3 */ + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0, USB_DEVICE_ID_STM_HID_SENSOR), .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, -- cgit v1.2.3-55-g7522 From 5cc5084dd9afa2f9bf953b0217bdb1b7c2158be1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 25 Nov 2016 11:03:44 +0100 Subject: HID: sensor-hub: add quirk for Microchip MM7150 One more device requiring a quirk :/ Reported-by: Christian-Nils Boda Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sensor-hub.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 0646278db7b3..5c925228847c 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -798,6 +798,9 @@ static const struct hid_device_id sensor_hub_devices[] = { { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, 0x07bd), /* Microsoft Surface 3 */ .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROCHIP, + 0x0f01), /* MM7150 */ + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0, USB_DEVICE_ID_STM_HID_SENSOR), .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, -- cgit v1.2.3-55-g7522 From 594312b88b0f451912c964c7ff2c0eaa71ad41b4 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 25 Nov 2016 11:08:10 +0100 Subject: HID: multitouch: handle external buttons for Precision Touchpads According to https://msdn.microsoft.com/en-us/library/windows/hardware/mt604195(v=vs.85).aspx external buttons have some weird usage mapping: - Button 2 Indicates Button State for external button for primary (default left) clicking. - Button 3 Indicates Button State for external button for secondary (default right) clicking. So in the current state, the buttons are mapped to right and middle. Move the usage by one to correctly map the external buttons. Tested-by: Chris Chiu Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index fb6f1f447279..a65a4c5894a5 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -567,6 +567,14 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_UP_BUTTON: code = BTN_MOUSE + ((usage->hid - 1) & HID_USAGE); + /* + * MS PTP spec says that external buttons left and right have + * usages 2 and 3. + */ + if (cls->name == MT_CLS_WIN_8 && + field->application == HID_DG_TOUCHPAD && + (usage->hid & HID_USAGE) > 1) + code--; hid_map_usage(hi, usage, bit, max, EV_KEY, code); input_set_capability(hi->input, EV_KEY, code); return 1; -- cgit v1.2.3-55-g7522 From 72d19459d7919f966594576bb042d15a451f27ea Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 25 Nov 2016 14:27:21 +0100 Subject: HID: input: rework HID_QUIRK_MULTI_INPUT The purpose of HID_QUIRK_MULTI_INPUT is to have an input device per report id. This is useful when the HID device presents several HID collections of different device types. The current implementation of hid-input creates one input node per id per type (input or output). This is problematic for the LEDs of a keyboard as they are often set through an output report. The current code creates one input node with all the keyboard keys, and one other with only the LEDs. To solve this, we use a two-passes way: - first, we initialize all input nodes and associate one per report id - then, we register all the input nodes Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 95 ++++++++++++++++++++++++++++--------------------- include/linux/hid.h | 1 + 2 files changed, 55 insertions(+), 41 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index fb9ace1cef8b..55db58459531 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1468,6 +1468,31 @@ static void hidinput_cleanup_hidinput(struct hid_device *hid, kfree(hidinput); } +static struct hid_input *hidinput_match(struct hid_report *report) +{ + struct hid_device *hid = report->device; + struct hid_input *hidinput; + + list_for_each_entry(hidinput, &hid->inputs, list) { + if (hidinput->report && + hidinput->report->id == report->id) + return hidinput; + } + + return NULL; +} + +static inline void hidinput_configure_usages(struct hid_input *hidinput, + struct hid_report *report) +{ + int i, j; + + for (i = 0; i < report->maxfield; i++) + for (j = 0; j < report->field[i]->maxusage; j++) + hidinput_configure_usage(hidinput, report->field[i], + report->field[i]->usage + j); +} + /* * Register the input device; print a message. * Configure the input layer interface @@ -1478,8 +1503,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) { struct hid_driver *drv = hid->driver; struct hid_report *report; - struct hid_input *hidinput = NULL; - int i, j, k; + struct hid_input *next, *hidinput = NULL; + int i, k; INIT_LIST_HEAD(&hid->inputs); INIT_WORK(&hid->led_work, hidinput_led_worker); @@ -1509,43 +1534,40 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) if (!report->maxfield) continue; + /* + * Find the previous hidinput report attached + * to this report id. + */ + if (hid->quirks & HID_QUIRK_MULTI_INPUT) + hidinput = hidinput_match(report); + if (!hidinput) { hidinput = hidinput_allocate(hid); if (!hidinput) goto out_unwind; } - for (i = 0; i < report->maxfield; i++) - for (j = 0; j < report->field[i]->maxusage; j++) - hidinput_configure_usage(hidinput, report->field[i], - report->field[i]->usage + j); - - if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) && - !hidinput_has_been_populated(hidinput)) - continue; + hidinput_configure_usages(hidinput, report); - if (hid->quirks & HID_QUIRK_MULTI_INPUT) { - /* This will leave hidinput NULL, so that it - * allocates another one if we have more inputs on - * the same interface. Some devices (e.g. Happ's - * UGCI) cram a lot of unrelated inputs into the - * same interface. */ + if (hid->quirks & HID_QUIRK_MULTI_INPUT) hidinput->report = report; - if (drv->input_configured && - drv->input_configured(hid, hidinput)) - goto out_cleanup; - if (input_register_device(hidinput->input)) - goto out_cleanup; - hidinput = NULL; - } } } - if (hidinput && (hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) && - !hidinput_has_been_populated(hidinput)) { - /* no need to register an input device not populated */ - hidinput_cleanup_hidinput(hid, hidinput); - hidinput = NULL; + list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { + if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) && + !hidinput_has_been_populated(hidinput)) { + /* no need to register an input device not populated */ + hidinput_cleanup_hidinput(hid, hidinput); + continue; + } + + if (drv->input_configured && + drv->input_configured(hid, hidinput)) + goto out_unwind; + if (input_register_device(hidinput->input)) + goto out_unwind; + hidinput->registered = true; } if (list_empty(&hid->inputs)) { @@ -1553,20 +1575,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) goto out_unwind; } - if (hidinput) { - if (drv->input_configured && - drv->input_configured(hid, hidinput)) - goto out_cleanup; - if (input_register_device(hidinput->input)) - goto out_cleanup; - } - return 0; -out_cleanup: - list_del(&hidinput->list); - input_free_device(hidinput->input); - kfree(hidinput); out_unwind: /* unwind the ones we already registered */ hidinput_disconnect(hid); @@ -1583,7 +1593,10 @@ void hidinput_disconnect(struct hid_device *hid) list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { list_del(&hidinput->list); - input_unregister_device(hidinput->input); + if (hidinput->registered) + input_unregister_device(hidinput->input); + else + input_free_device(hidinput->input); kfree(hidinput); } diff --git a/include/linux/hid.h b/include/linux/hid.h index b2ec82712baa..596b9232c19e 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -479,6 +479,7 @@ struct hid_input { struct list_head list; struct hid_report *report; struct input_dev *input; + bool registered; }; enum hid_type { -- cgit v1.2.3-55-g7522 From 8fe89ef076fa104f514da6ef61d90f5bf93488e3 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 25 Nov 2016 14:27:23 +0100 Subject: HID: multitouch: enable the Surface 3 Type Cover to report multitouch data There is no reasons to filter out keyboard and consumer control collections in hid-multitouch. With the previous hid-input fix, there is now a full support of the Type Cover and we can remove all specific bits from hid-core and hid-microsoft. hid-multitouch will automatically set HID_QUIRK_NO_INIT_REPORTS so we can also remove it from the list of ushbid quirks. Signed-off-by: Benjamin Tissoires 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/hid-multitouch.c | 4 +++- drivers/hid/usbhid/hid-quirks.c | 1 - 5 files changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2b89c701076f..a5dd7e63ada3 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -728,7 +728,6 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP || - hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 || hid->product == USB_DEVICE_ID_MS_POWER_COVER) && hid->group == HID_GROUP_MULTITOUCH) hid->group = HID_GROUP_GENERIC; @@ -1984,7 +1983,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP) }, - { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 575aa65436d1..10d15359cbae 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -719,7 +719,6 @@ #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP 0x07e9 -#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de #define USB_DEVICE_ID_MS_POWER_COVER 0x07da #define USB_VENDOR_ID_MOJO 0x8282 diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index c6cd392e9f99..ba02667beb80 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -282,8 +282,6 @@ static const struct hid_device_id ms_devices[] = { .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP), .driver_data = MS_HIDINPUT }, - { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), - .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER), .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD), diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index fb6f1f447279..84c56e645fe8 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -842,7 +842,9 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, if (!td->mtclass.export_all_inputs && field->application != HID_DG_TOUCHSCREEN && field->application != HID_DG_PEN && - field->application != HID_DG_TOUCHPAD) + field->application != HID_DG_TOUCHPAD && + field->application != HID_GD_KEYBOARD && + field->application != HID_CP_CONSUMER_CONTROL) return -1; /* diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index e6cfd323babc..18ae71503309 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -102,7 +102,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP, HID_QUIRK_NO_INIT_REPORTS }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS }, -- cgit v1.2.3-55-g7522 From b897f6db3ae2cd9a42377f8b1865450f34ceff0e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 25 Nov 2016 14:27:22 +0100 Subject: HID: multitouch: do not retrieve all reports for all devices We already have in place a quirk for Windows 8 devices, but it looks like the Surface Cover are not conforming to it. Given that we are only interested in 3 feature reports (the ones that the Windows driver retrieves), we should be safe to unconditionally apply the quirk to everybody. In case there is an issue with a controller, we can always mark it as such in the transport driver, and hid-multitouch won't try to retrieve the feature report. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 76 +++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 36 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 84c56e645fe8..89e9032ab1e7 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -108,6 +108,7 @@ struct mt_device { int cc_value_index; /* contact count value index in the field */ unsigned last_slot_field; /* the last field of a slot */ unsigned mt_report_id; /* the report ID of the multitouch device */ + unsigned long initial_quirks; /* initial quirks state */ __s16 inputmode; /* InputMode HID feature, -1 if non-existent */ __s16 inputmode_index; /* InputMode HID feature index in the report */ __s16 maxcontact_report_id; /* Maximum Contact Number HID feature, @@ -318,13 +319,10 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *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. + * Do not fetch the feature report if the device has been explicitly + * marked as non-capable. */ - if (!(hdev->quirks & HID_QUIRK_NO_INIT_REPORTS)) - return; - if (td->mtclass.name != MT_CLS_WIN_8) + if (td->initial_quirks & HID_QUIRK_NO_INIT_REPORTS) return; buf = hid_alloc_report_buf(report, GFP_KERNEL); @@ -1085,36 +1083,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) } } - /* This allows the driver to correctly support devices - * that emit events over several HID messages. - */ - hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; - - /* - * This allows the driver to handle different input sensors - * that emits events through different reports on the same HID - * device. - */ - hdev->quirks |= HID_QUIRK_MULTI_INPUT; - hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT; - - /* - * Handle special quirks for Windows 8 certified devices. - */ - if (id->group == HID_GROUP_MULTITOUCH_WIN_8) - /* - * Some multitouch screens do not like to be polled for input - * 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_REPORTS; - td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL); if (!td) { dev_err(&hdev->dev, "cannot allocate multitouch data\n"); @@ -1138,6 +1106,39 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID) td->serial_maybe = true; + /* + * Store the initial quirk state + */ + td->initial_quirks = hdev->quirks; + + /* This allows the driver to correctly support devices + * that emit events over several HID messages. + */ + hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; + + /* + * This allows the driver to handle different input sensors + * that emits events through different reports on the same HID + * device. + */ + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT; + + /* + * Some multitouch screens do not like to be polled for input + * reports. Fortunately, the Win8 spec says that all touches + * should be sent during each report, making the initialization + * of input reports unnecessary. For Win7 devices, well, let's hope + * they will still be happy (this is only be a problem if a touch + * was already there while probing the device). + * + * 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_REPORTS; + ret = hid_parse(hdev); if (ret != 0) return ret; @@ -1206,8 +1207,11 @@ static int mt_resume(struct hid_device *hdev) static void mt_remove(struct hid_device *hdev) { + struct mt_device *td = hid_get_drvdata(hdev); + sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); hid_hw_stop(hdev); + hdev->quirks = td->initial_quirks; } /* -- cgit v1.2.3-55-g7522 From 00f7fea5da4986af93457b625a379d5e3ee51754 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 21 Nov 2016 15:21:27 +0100 Subject: HID: i2c-hid: force the IRQ level trigger only when not set Instead of forcing the level trigger of the IRQ, we can count on ACPI or OF to set it up for us. The first release of the HID over I2C specification mentioned that the level trigger needed to be active low. In the latest version of the specification, there is no such explicit mention, so it's better to not assume one. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index ce518795cb30..8d53efe04dad 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -858,13 +859,16 @@ static struct hid_ll_driver i2c_hid_ll_driver = { static int i2c_hid_init_irq(struct i2c_client *client) { struct i2c_hid *ihid = i2c_get_clientdata(client); + unsigned long irqflags = 0; int ret; dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq); + if (!irq_get_trigger_type(client->irq)) + irqflags = IRQF_TRIGGER_LOW; + ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - client->name, ihid); + irqflags | IRQF_ONESHOT, client->name, ihid); if (ret < 0) { dev_warn(&client->dev, "Could not register for %s interrupt, irq = %d," -- cgit v1.2.3-55-g7522 From 13de9cca514ed63604263cad87ca8cb36e9b6489 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 21 Nov 2016 12:07:54 +0100 Subject: HID: cp2112: add IRQ chip handling The GPIO part doesn't provide interrupts when GPIO are toggled. So use a polling mechanism if someone requests a GPIO as an IRQ. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-cp2112.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 202 insertions(+), 3 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index 60d30203a5fa..f31a778b0851 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c @@ -24,6 +24,7 @@ * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN495.pdf */ +#include #include #include #include @@ -168,6 +169,12 @@ struct cp2112_device { struct gpio_chip gc; u8 *in_out_buffer; spinlock_t lock; + + struct gpio_desc *desc[8]; + bool gpio_poll; + struct delayed_work gpio_poll_worker; + unsigned long irq_mask; + u8 gpio_prev_state; }; static int gpio_push_pull = 0xFF; @@ -233,7 +240,7 @@ static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value) spin_unlock_irqrestore(&dev->lock, flags); } -static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset) +static int cp2112_gpio_get_all(struct gpio_chip *chip) { struct cp2112_device *dev = gpiochip_get_data(chip); struct hid_device *hdev = dev->hdev; @@ -252,7 +259,7 @@ static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset) goto exit; } - ret = (buf[1] >> offset) & 1; + ret = buf[1]; exit: spin_unlock_irqrestore(&dev->lock, flags); @@ -260,6 +267,17 @@ exit: return ret; } +static int cp2112_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + int ret; + + ret = cp2112_gpio_get_all(chip); + if (ret < 0) + return ret; + + return (ret >> offset) & 1; +} + static int cp2112_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { @@ -1041,6 +1059,166 @@ static void chmod_sysfs_attrs(struct hid_device *hdev) } } +static void cp2112_gpio_irq_ack(struct irq_data *d) +{ +} + +static void cp2112_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct cp2112_device *dev = gpiochip_get_data(gc); + + __clear_bit(d->hwirq, &dev->irq_mask); +} + +static void cp2112_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct cp2112_device *dev = gpiochip_get_data(gc); + + __set_bit(d->hwirq, &dev->irq_mask); +} + +static void cp2112_gpio_poll_callback(struct work_struct *work) +{ + struct cp2112_device *dev = container_of(work, struct cp2112_device, + gpio_poll_worker.work); + struct irq_data *d; + u8 gpio_mask; + u8 virqs = (u8)dev->irq_mask; + u32 irq_type; + int irq, virq, ret; + + ret = cp2112_gpio_get_all(&dev->gc); + if (ret == -ENODEV) /* the hardware has been disconnected */ + return; + if (ret < 0) + goto exit; + + gpio_mask = ret; + + while (virqs) { + virq = ffs(virqs) - 1; + virqs &= ~BIT(virq); + + if (!dev->gc.to_irq) + break; + + irq = dev->gc.to_irq(&dev->gc, virq); + + d = irq_get_irq_data(irq); + if (!d) + continue; + + irq_type = irqd_get_trigger_type(d); + + if (gpio_mask & BIT(virq)) { + /* Level High */ + + if (irq_type & IRQ_TYPE_LEVEL_HIGH) + handle_nested_irq(irq); + + if ((irq_type & IRQ_TYPE_EDGE_RISING) && + !(dev->gpio_prev_state & BIT(virq))) + handle_nested_irq(irq); + } else { + /* Level Low */ + + if (irq_type & IRQ_TYPE_LEVEL_LOW) + handle_nested_irq(irq); + + if ((irq_type & IRQ_TYPE_EDGE_FALLING) && + (dev->gpio_prev_state & BIT(virq))) + handle_nested_irq(irq); + } + } + + dev->gpio_prev_state = gpio_mask; + +exit: + if (dev->gpio_poll) + schedule_delayed_work(&dev->gpio_poll_worker, 10); +} + + +static unsigned int cp2112_gpio_irq_startup(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct cp2112_device *dev = gpiochip_get_data(gc); + + INIT_DELAYED_WORK(&dev->gpio_poll_worker, cp2112_gpio_poll_callback); + + cp2112_gpio_direction_input(gc, d->hwirq); + + if (!dev->gpio_poll) { + dev->gpio_poll = true; + schedule_delayed_work(&dev->gpio_poll_worker, 0); + } + + cp2112_gpio_irq_unmask(d); + return 0; +} + +static void cp2112_gpio_irq_shutdown(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct cp2112_device *dev = gpiochip_get_data(gc); + + cancel_delayed_work_sync(&dev->gpio_poll_worker); +} + +static int cp2112_gpio_irq_type(struct irq_data *d, unsigned int type) +{ + return 0; +} + +static struct irq_chip cp2112_gpio_irqchip = { + .name = "cp2112-gpio", + .irq_startup = cp2112_gpio_irq_startup, + .irq_shutdown = cp2112_gpio_irq_shutdown, + .irq_ack = cp2112_gpio_irq_ack, + .irq_mask = cp2112_gpio_irq_mask, + .irq_unmask = cp2112_gpio_irq_unmask, + .irq_set_type = cp2112_gpio_irq_type, +}; + +static int __maybe_unused cp2112_allocate_irq(struct cp2112_device *dev, + int pin) +{ + int ret; + + if (dev->desc[pin]) + return -EINVAL; + + dev->desc[pin] = gpiochip_request_own_desc(&dev->gc, pin, + "HID/I2C:Event"); + if (IS_ERR(dev->desc[pin])) { + dev_err(dev->gc.parent, "Failed to request GPIO\n"); + return PTR_ERR(dev->desc[pin]); + } + + ret = gpiochip_lock_as_irq(&dev->gc, pin); + if (ret) { + dev_err(dev->gc.parent, "Failed to lock GPIO as interrupt\n"); + goto err_desc; + } + + ret = gpiod_to_irq(dev->desc[pin]); + if (ret < 0) { + dev_err(dev->gc.parent, "Failed to translate GPIO to IRQ\n"); + goto err_lock; + } + + return ret; + +err_lock: + gpiochip_unlock_as_irq(&dev->gc, pin); +err_desc: + gpiochip_free_own_desc(dev->desc[pin]); + dev->desc[pin] = NULL; + return ret; +} + static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id) { struct cp2112_device *dev; @@ -1163,8 +1341,17 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id) chmod_sysfs_attrs(hdev); hid_hw_power(hdev, PM_HINT_NORMAL); + ret = gpiochip_irqchip_add(&dev->gc, &cp2112_gpio_irqchip, 0, + handle_simple_irq, IRQ_TYPE_NONE); + if (ret) { + dev_err(dev->gc.parent, "failed to add IRQ chip\n"); + goto err_sysfs_remove; + } + return ret; +err_sysfs_remove: + sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group); err_gpiochip_remove: gpiochip_remove(&dev->gc); err_free_i2c: @@ -1181,10 +1368,22 @@ err_hid_stop: static void cp2112_remove(struct hid_device *hdev) { struct cp2112_device *dev = hid_get_drvdata(hdev); + int i; sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group); - gpiochip_remove(&dev->gc); i2c_del_adapter(&dev->adap); + + if (dev->gpio_poll) { + dev->gpio_poll = false; + cancel_delayed_work_sync(&dev->gpio_poll_worker); + } + + for (i = 0; i < ARRAY_SIZE(dev->desc); i++) { + gpiochip_unlock_as_irq(&dev->gc, i); + gpiochip_free_own_desc(dev->desc[i]); + } + + gpiochip_remove(&dev->gc); /* i2c_del_adapter has finished removing all i2c devices from our * adapter. Well behaved devices should no longer call our cp2112_xfer * and should have waited for any pending calls to finish. It has also -- cgit v1.2.3-55-g7522 From 4f967f6d73746f66514528cc1191025f0b5d69b3 Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Wed, 23 Nov 2016 14:07:06 -0800 Subject: HID: sony: Fix memory issue when connecting device using both Bluetooth and USB A previous patch moved most input initialization from sony_probe to sony_input_configured to avoid some race conditions. The driver has some special logic to prevent the device to get registered twice in case the user connects it both over Bluetooth and USB. When this condition happens sony_input_configured returns a failure, but sony_probe continues as hid_hw_start doesn't fail. As was discussed on linux-input, it is acceptable for this function to fail. This patch adds a check for the HID_CLAIMED_INPUT flag within sony_probe to determine whether initialization succeeded correctly. The flag is not set by the HID layer when sony_input_configured fails. Signed-off-by: Roderick Colenbrander Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 14763cdf6393..3385006accc0 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -2524,6 +2524,19 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } + /* sony_input_configured can fail, but this doesn't result + * in hid_hw_start failures (intended). Check whether + * the HID layer claimed the device else fail. + * We don't know the actual reason for the failure, most + * likely it is due to EEXIST in case of double connection + * of USB and Bluetooth, but could have been due to ENOMEM + * or other reasons as well. + */ + if (!(hdev->claimed & HID_CLAIMED_INPUT)) { + hid_err(hdev, "failed to claim input\n"); + return -ENODEV; + } + return ret; } -- cgit v1.2.3-55-g7522 From ac797b95f53276c132c51d53437e38dd912413d7 Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Wed, 23 Nov 2016 14:07:07 -0800 Subject: HID: sony: Make the DS4 touchpad a separate device The dualshock 4 supports both analog sticks of which one uses ABS_X/_Y and a touchpad. In a recent discussion with Dmitry about some input-mt changes we proposed for disabling pointer emulation from input_mt_sync_frame, Dmitry mentioned ABS_X/_Y should report the same data as ABS_MT_POSITION_X/_Y. The current driver is mixing axes for different subdevices. It was suggested to make the touchpad its own sub-device. This patch turns the touchpad into its own device. In addition this patch also moves the button underneath the touchpad into the new device. It felt like this button should be part of the device. No known user space application (not even SDL2) seems to be using it. Signed-off-by: Roderick Colenbrander Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 105 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 84 insertions(+), 21 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 3385006accc0..995b5cf1afc9 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -376,7 +376,7 @@ static u8 dualshock4_usb_rdesc[] = { 0x65, 0x00, /* Unit, */ 0x05, 0x09, /* Usage Page (Button), */ 0x19, 0x01, /* Usage Minimum (01h), */ - 0x29, 0x0E, /* Usage Maximum (0Eh), */ + 0x29, 0x0D, /* Usage Maximum (0Dh), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x25, 0x01, /* Logical Maximum (1), */ 0x75, 0x01, /* Report Size (1), */ @@ -689,7 +689,7 @@ static u8 dualshock4_bt_rdesc[] = { 0x81, 0x42, /* Input (Variable, Null State), */ 0x05, 0x09, /* Usage Page (Button), */ 0x19, 0x01, /* Usage Minimum (01h), */ - 0x29, 0x0E, /* Usage Maximum (0Eh), */ + 0x29, 0x0D, /* Usage Maximum (0Dh), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x25, 0x01, /* Logical Maximum (1), */ 0x75, 0x01, /* Report Size (1), */ @@ -1033,9 +1033,12 @@ struct motion_output_report_02 { /* Offsets relative to USB input report (0x1). Bluetooth (0x11) requires an * additional +2. */ +#define DS4_INPUT_REPORT_BUTTON_OFFSET 5 #define DS4_INPUT_REPORT_BATTERY_OFFSET 30 #define DS4_INPUT_REPORT_TOUCHPAD_OFFSET 33 +#define DS4_TOUCHPAD_SUFFIX " Touchpad" + static DEFINE_SPINLOCK(sony_dev_list_lock); static LIST_HEAD(sony_device_list); static DEFINE_IDA(sony_device_id_allocator); @@ -1044,6 +1047,7 @@ struct sony_sc { spinlock_t lock; struct list_head list_node; struct hid_device *hdev; + struct input_dev *touchpad; struct led_classdev *leds[MAX_LEDS]; unsigned long quirks; struct work_struct state_worker; @@ -1228,9 +1232,6 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size) static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size) { - struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, - struct hid_input, list); - struct input_dev *input_dev = hidinput->input; unsigned long flags; int n, m, offset, num_touch_data, max_touch_data; u8 cable_state, battery_capacity, battery_charging; @@ -1238,6 +1239,10 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size) /* When using Bluetooth the header is 2 bytes longer, so skip these. */ int data_offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 0 : 2; + /* Second bit of third button byte is for the touchpad button. */ + offset = data_offset + DS4_INPUT_REPORT_BUTTON_OFFSET; + input_report_key(sc->touchpad, BTN_LEFT, rd[offset+2] & 0x2); + /* * The lower 4 bits of byte 30 (or 32 for BT) contain the battery level * and the 5th bit contains the USB cable state. @@ -1303,18 +1308,18 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size) y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4); active = !(rd[offset] >> 7); - input_mt_slot(input_dev, n); - input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, active); + input_mt_slot(sc->touchpad, n); + input_mt_report_slot_state(sc->touchpad, MT_TOOL_FINGER, active); if (active) { - input_report_abs(input_dev, ABS_MT_POSITION_X, x); - input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(sc->touchpad, ABS_MT_POSITION_X, x); + input_report_abs(sc->touchpad, ABS_MT_POSITION_Y, y); } offset += 4; } - input_mt_sync_frame(input_dev); - input_sync(input_dev); + input_mt_sync_frame(sc->touchpad); + input_sync(sc->touchpad); } } @@ -1415,22 +1420,77 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } -static int sony_register_touchpad(struct hid_input *hi, int touch_count, +static int sony_register_touchpad(struct sony_sc *sc, int touch_count, int w, int h) { - struct input_dev *input_dev = hi->input; + size_t name_sz; + char *name; int ret; - ret = input_mt_init_slots(input_dev, touch_count, 0); + sc->touchpad = input_allocate_device(); + if (!sc->touchpad) + return -ENOMEM; + + input_set_drvdata(sc->touchpad, sc); + sc->touchpad->dev.parent = &sc->hdev->dev; + sc->touchpad->phys = sc->hdev->phys; + sc->touchpad->uniq = sc->hdev->uniq; + sc->touchpad->id.bustype = sc->hdev->bus; + sc->touchpad->id.vendor = sc->hdev->vendor; + sc->touchpad->id.product = sc->hdev->product; + sc->touchpad->id.version = sc->hdev->version; + + /* Append a suffix to the controller name as there are various + * DS4 compatible non-Sony devices with different names. + */ + name_sz = strlen(sc->hdev->name) + sizeof(DS4_TOUCHPAD_SUFFIX); + name = kzalloc(name_sz, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto err; + } + snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->name); + sc->touchpad->name = name; + + ret = input_mt_init_slots(sc->touchpad, touch_count, 0); if (ret < 0) - return ret; + goto err; + + /* We map the button underneath the touchpad to BTN_LEFT. */ + __set_bit(EV_KEY, sc->touchpad->evbit); + __set_bit(BTN_LEFT, sc->touchpad->keybit); + __set_bit(INPUT_PROP_BUTTONPAD, sc->touchpad->propbit); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0); + input_set_abs_params(sc->touchpad, ABS_MT_POSITION_X, 0, w, 0, 0); + input_set_abs_params(sc->touchpad, ABS_MT_POSITION_Y, 0, h, 0, 0); + + ret = input_register_device(sc->touchpad); + if (ret < 0) + goto err; return 0; + +err: + kfree(sc->touchpad->name); + sc->touchpad->name = NULL; + + input_free_device(sc->touchpad); + sc->touchpad = NULL; + + return ret; } +static void sony_unregister_touchpad(struct sony_sc *sc) +{ + if (!sc->touchpad) + return; + + kfree(sc->touchpad->name); + sc->touchpad->name = NULL; + + input_unregister_device(sc->touchpad); + sc->touchpad = NULL; +} /* * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller @@ -2422,7 +2482,7 @@ static int sony_input_configured(struct hid_device *hdev, * The Dualshock 4 touchpad supports 2 touches and has a * resolution of 1920x942 (44.86 dots/mm). */ - ret = sony_register_touchpad(hidinput, 2, 1920, 942); + ret = sony_register_touchpad(sc, 2, 1920, 942); if (ret) { hid_err(sc->hdev, "Unable to initialize multi-touch slots: %d\n", @@ -2544,13 +2604,16 @@ static void sony_remove(struct hid_device *hdev) { struct sony_sc *sc = hid_get_drvdata(hdev); + hid_hw_close(hdev); + if (sc->quirks & SONY_LED_SUPPORT) sony_leds_remove(sc); - if (sc->quirks & SONY_BATTERY_SUPPORT) { - hid_hw_close(hdev); + if (sc->quirks & SONY_BATTERY_SUPPORT) sony_battery_remove(sc); - } + + if (sc->touchpad) + sony_unregister_touchpad(sc); sony_cancel_work_sync(sc); -- cgit v1.2.3-55-g7522 From 9131f8cc2b4eaf7c08d402243429e0bfba9aa0d6 Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Wed, 23 Nov 2016 14:07:08 -0800 Subject: HID: sony: Comply to Linux gamepad spec for DS4 The DS4 side of hid-sony used the hid-core layer to assign buttons and axes based on the HID report descriptors. The default mapping was strange e.g. right stick using ABS_Z/ABS_RZ or the physical 'south button' being reported as BTN_EAST etcetera. This patch makes the DS4 side ofi the hid-sony driver comply to the Linux game controller spec as suggested in a discussion with Dmitry on the linux-input list. Currently the main user of the DS4 is the SDL2 library, which has a mapping table using vendor/device/version as a key. In order to not break SDL2 we discussed adjusting the version number, so it can have both mappings. This was discust on linux-input and we discussed privately with SDL2 developers. Signed-off-by: Roderick Colenbrander Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 995b5cf1afc9..d8889d681687 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -977,6 +977,32 @@ static const unsigned int buzz_keymap[] = { [20] = BTN_TRIGGER_HAPPY20, }; +static const unsigned int ds4_absmap[] = { + [0x30] = ABS_X, + [0x31] = ABS_Y, + [0x32] = ABS_RX, /* right stick X */ + [0x33] = ABS_Z, /* L2 */ + [0x34] = ABS_RZ, /* R2 */ + [0x35] = ABS_RY, /* right stick Y */ +}; + +static const unsigned int ds4_keymap[] = { + [0x1] = BTN_WEST, /* Square */ + [0x2] = BTN_SOUTH, /* Cross */ + [0x3] = BTN_EAST, /* Circle */ + [0x4] = BTN_NORTH, /* Triangle */ + [0x5] = BTN_TL, /* L1 */ + [0x6] = BTN_TR, /* R1 */ + [0x7] = BTN_TL2, /* L2 */ + [0x8] = BTN_TR2, /* R2 */ + [0x9] = BTN_SELECT, /* Share */ + [0xa] = BTN_START, /* Options */ + [0xb] = BTN_THUMBL, /* L3 */ + [0xc] = BTN_THUMBR, /* R3 */ + [0xd] = BTN_MODE, /* PS */ +}; + + static enum power_supply_property sony_battery_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CAPACITY, @@ -1143,6 +1169,37 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi, return 1; } +static int ds4_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) { + unsigned int key = usage->hid & HID_USAGE; + + if (key >= ARRAY_SIZE(ds4_keymap)) + return -1; + + key = ds4_keymap[key]; + hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); + return 1; + } else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) { + unsigned int abs = usage->hid & HID_USAGE; + + /* Let the HID parser deal with the HAT. */ + if (usage->hid == HID_GD_HATSWITCH) + return 0; + + if (abs >= ARRAY_SIZE(ds4_absmap)) + return -1; + + abs = ds4_absmap[abs]; + hid_map_usage_clear(hi, usage, bit, max, EV_ABS, abs); + return 1; + } + + return 0; +} + static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *rsize) { @@ -1416,6 +1473,10 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, if (sc->quirks & PS3REMOTE) return ps3remote_mapping(hdev, hi, field, usage, bit, max); + + if (sc->quirks & DUALSHOCK4_CONTROLLER) + return ds4_mapping(hdev, hi, field, usage, bit, max); + /* Let hid-core decide for the others */ return 0; } @@ -2578,6 +2639,15 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) else if (sc->quirks & SIXAXIS_CONTROLLER) connect_mask |= HID_CONNECT_HIDDEV_FORCE; + /* Patch the hw version on DS4 compatible devices, so applications can + * distinguish between the default HID mappings and the mappings defined + * by the Linux game controller spec. This is important for the SDL2 + * library, which has a game controller database, which uses device ids + * in combination with version as a key. + */ + if (sc->quirks & DUALSHOCK4_CONTROLLER) + hdev->version |= 0x8000; + ret = hid_hw_start(hdev, connect_mask); if (ret) { hid_err(hdev, "hw start failed\n"); -- cgit v1.2.3-55-g7522 From de66a1a04c25f2560a8dca7a95e2a150b0d5e17e Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Wed, 23 Nov 2016 14:07:11 -0800 Subject: HID: sony: Support DS4 dongle Add support for USB based DS4 dongle device, which allows connecting a DS4 through Bluetooth, but hides Bluetooth from the host system. Signed-off-by: Roderick Colenbrander Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-sony.c | 2 ++ 3 files changed, 4 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5ed2f572430f..1ed841b149e8 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2061,6 +2061,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 27f82cc4ada4..dc7e39432320 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -900,6 +900,7 @@ #define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 #define USB_DEVICE_ID_SONY_PS4_CONTROLLER 0x05c4 #define USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 0x09cc +#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE 0x0ba0 #define USB_DEVICE_ID_SONY_MOTION_CONTROLLER 0x03d5 #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f #define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002 diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index d8889d681687..7687c0875395 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -2791,6 +2791,8 @@ static const struct hid_device_id sony_devices[] = { .driver_data = DUALSHOCK4_CONTROLLER_USB }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2), .driver_data = DUALSHOCK4_CONTROLLER_BT }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE), + .driver_data = DUALSHOCK4_CONTROLLER_USB }, /* Nyko Core Controller for PS3 */ { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER), .driver_data = SIXAXIS_CONTROLLER_USB | SINO_LITE_CONTROLLER }, -- cgit v1.2.3-55-g7522 From 6d290391be9233349345d49f4539a87f8cb31c30 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 28 Nov 2016 11:23:35 -0800 Subject: HID: intel-ish-hid: Fix potential race condition Although unlikely but it is possible that when a connect or disconnect request is issued to the firmware, before the response comes, user terminates the client session. In this case when the response is arrived there is no matching client instance in the list of currently active clients. In this case, don't issue call to wake up a waiting client. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ishtp/hbm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.c b/drivers/hid/intel-ish-hid/ishtp/hbm.c index 74bffee60774..59460b66e689 100644 --- a/drivers/hid/intel-ish-hid/ishtp/hbm.c +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.c @@ -378,11 +378,10 @@ static void ishtp_hbm_cl_disconnect_res(struct ishtp_device *dev, list_for_each_entry(cl, &dev->cl_list, link) { if (!rs->status && ishtp_hbm_cl_addr_equal(cl, rs)) { cl->state = ISHTP_CL_DISCONNECTED; + wake_up_interruptible(&cl->wait_ctrl_res); break; } } - if (cl) - wake_up_interruptible(&cl->wait_ctrl_res); spin_unlock_irqrestore(&dev->cl_list_lock, flags); } @@ -431,11 +430,10 @@ static void ishtp_hbm_cl_connect_res(struct ishtp_device *dev, cl->state = ISHTP_CL_DISCONNECTED; cl->status = -ENODEV; } + wake_up_interruptible(&cl->wait_ctrl_res); break; } } - if (cl) - wake_up_interruptible(&cl->wait_ctrl_res); spin_unlock_irqrestore(&dev->cl_list_lock, flags); } -- cgit v1.2.3-55-g7522 From 13c28b029708d934dc80f77f83f85790f264080a Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 30 Nov 2016 10:04:51 +0100 Subject: HID: cp2112: explicitly require irqchip support in gpiolib Since the gpio-as-irq support has been added, the driver now depends on proper support being available in gpiolib. Fixes: 13de9cca514e ("HID: cp2112: add IRQ chip handling") Reported-by: kbuild test robot Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index cd4599c0523b..a5b3902fb266 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -214,7 +214,7 @@ config HID_CMEDIA config HID_CP2112 tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support" - depends on USB_HID && I2C && GPIOLIB + depends on USB_HID && I2C && GPIOLIB && GPIOLIB_IRQCHIP ---help--- Support for Silicon Labs CP2112 HID USB to SMBus Master Bridge. This is a HID device driver which registers as an i2c adapter -- cgit v1.2.3-55-g7522 From 608ad1848b417f4b5771573ca9bfb811fcda3536 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 30 Nov 2016 23:48:46 +0100 Subject: HID: intel_ish-hid: use %pUL for uuid formatting We have the %pU printf extension for doing exactly this. Saves some .text, and is likely also a little faster. Signed-off-by: Rasmus Villemoes Reviewed-by: Benjamin Tissoires Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ishtp/bus.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.c b/drivers/hid/intel-ish-hid/ishtp/bus.c index 256521509d20..f4cbc744e657 100644 --- a/drivers/hid/intel-ish-hid/ishtp/bus.c +++ b/drivers/hid/intel-ish-hid/ishtp/bus.c @@ -585,14 +585,7 @@ int ishtp_bus_new_client(struct ishtp_device *dev) */ i = dev->fw_client_presentation_num - 1; device_uuid = dev->fw_clients[i].props.protocol_name; - dev_name = kasprintf(GFP_KERNEL, - "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}", - device_uuid.b[3], device_uuid.b[2], device_uuid.b[1], - device_uuid.b[0], device_uuid.b[5], device_uuid.b[4], - device_uuid.b[7], device_uuid.b[6], device_uuid.b[8], - device_uuid.b[9], device_uuid.b[10], device_uuid.b[11], - device_uuid.b[12], device_uuid.b[13], device_uuid.b[14], - device_uuid.b[15]); + dev_name = kasprintf(GFP_KERNEL, "{%pUL}", device_uuid.b); if (!dev_name) return -ENOMEM; -- cgit v1.2.3-55-g7522 From 354a32985a863c9eecd381a5f9eea480d5abc6dc Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Thu, 8 Dec 2016 22:03:27 -0800 Subject: HID: wacom: generic: Don't return a value for wacom_wac_event It is unnecessary to return a value since nothing is expecting a value from it. Signed-off-by: Ping Cheng Reviewed-By: Jason Gerecke Signed-off-by: Jiri Kosina --- drivers/hid/wacom.h | 2 +- drivers/hid/wacom_wac.c | 62 +++++++++++++++++++++---------------------------- 2 files changed, 28 insertions(+), 36 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h index b4800ea891cb..d303e413306d 100644 --- a/drivers/hid/wacom.h +++ b/drivers/hid/wacom.h @@ -210,7 +210,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac); void wacom_wac_usage_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage); -int wacom_wac_event(struct hid_device *hdev, struct hid_field *field, +void wacom_wac_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value); void wacom_wac_report(struct hid_device *hdev, struct hid_report *report); void wacom_battery_work(struct work_struct *work); diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index f1ef3e153765..f1c49a44c06a 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1582,7 +1582,7 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, } } -static int wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field, +static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct wacom *wacom = hid_get_drvdata(hdev); @@ -1598,23 +1598,21 @@ static int wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field, case WACOM_HID_WD_BATTERY_LEVEL: wacom_wac->hid_data.battery_capacity = value; wacom_wac->hid_data.bat_connected = 1; - return 0; + break; case WACOM_HID_WD_BATTERY_CHARGING: wacom_wac->hid_data.bat_charging = value; wacom_wac->hid_data.ps_connected = value; wacom_wac->hid_data.bat_connected = 1; - return 0; + break; case WACOM_HID_WD_TOUCHRINGSTATUS: - return 0; + break; default: input_event(input, usage->type, usage->code, value); break; } - - return 0; } static void wacom_wac_pad_pre_report(struct hid_device *hdev, @@ -1731,7 +1729,7 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, } } -static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, +static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct wacom *wacom = hid_get_drvdata(hdev); @@ -1753,25 +1751,25 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, wacom_wac->hid_data.inrange_state = value; if (!(features->quirks & WACOM_QUIRK_SENSE)) wacom_wac->hid_data.sense_state = value; - return 0; + return; case HID_DG_BATTERYSTRENGTH: wacom_wac->hid_data.battery_capacity = value; wacom_wac->hid_data.bat_connected = 1; break; case HID_DG_INVERT: wacom_wac->hid_data.invert_state = value; - return 0; + return; case HID_DG_ERASER: case HID_DG_TIPSWITCH: wacom_wac->hid_data.tipswitch |= value; - return 0; + return; case HID_DG_TOOLSERIALNUMBER: wacom_wac->serial[0] = (wacom_wac->serial[0] & ~0xFFFFFFFFULL); wacom_wac->serial[0] |= value; - return 0; + return; case WACOM_HID_WD_SENSE: wacom_wac->hid_data.sense_state = value; - return 0; + return; case WACOM_HID_WD_SERIALHI: wacom_wac->serial[0] = (wacom_wac->serial[0] & 0xFFFFFFFF); wacom_wac->serial[0] |= ((__u64)value) << 32; @@ -1783,7 +1781,7 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, if (value >> 20 == 1) { wacom_wac->id[0] |= value & 0xFFFFF; } - return 0; + return; case WACOM_HID_WD_TOOLTYPE: /* * Some devices (MobileStudio Pro, and possibly later @@ -1793,50 +1791,48 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, * up over time :( */ wacom_wac->id[0] |= value; - return 0; + return; case WACOM_HID_WD_OFFSETLEFT: if (features->offset_left && value != features->offset_left) hid_warn(hdev, "%s: overriding exising left offset " "%d -> %d\n", __func__, value, features->offset_left); features->offset_left = value; - return 0; + return; case WACOM_HID_WD_OFFSETRIGHT: if (features->offset_right && value != features->offset_right) hid_warn(hdev, "%s: overriding exising right offset " "%d -> %d\n", __func__, value, features->offset_right); features->offset_right = value; - return 0; + return; case WACOM_HID_WD_OFFSETTOP: if (features->offset_top && value != features->offset_top) hid_warn(hdev, "%s: overriding exising top offset " "%d -> %d\n", __func__, value, features->offset_top); features->offset_top = value; - return 0; + return; case WACOM_HID_WD_OFFSETBOTTOM: if (features->offset_bottom && value != features->offset_bottom) hid_warn(hdev, "%s: overriding exising bottom offset " "%d -> %d\n", __func__, value, features->offset_bottom); features->offset_bottom = value; - return 0; + return; } /* send pen events only when touch is up or forced out * or touch arbitration is off */ if (!usage->type || delay_pen_events(wacom_wac)) - return 0; + return; /* send pen events only when the pen is in/entering/leaving proximity */ if (!wacom_wac->hid_data.inrange_state && !wacom_wac->tool[0]) - return 0; + return; input_event(input, usage->type, usage->code, value); - - return 0; } static void wacom_wac_pen_pre_report(struct hid_device *hdev, @@ -1980,7 +1976,7 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac, } } -static int wacom_wac_finger_event(struct hid_device *hdev, +static void wacom_wac_finger_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct wacom *wacom = hid_get_drvdata(hdev); @@ -2013,8 +2009,6 @@ static int wacom_wac_finger_event(struct hid_device *hdev, if (equivalent_usage == wacom_wac->hid_data.last_slot_field) wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input); } - - return 0; } static void wacom_wac_finger_pre_report(struct hid_device *hdev, @@ -2100,29 +2094,27 @@ void wacom_wac_usage_mapping(struct hid_device *hdev, features->device_type |= WACOM_DEVICETYPE_DIRECT; if (WACOM_PAD_FIELD(field)) - return wacom_wac_pad_usage_mapping(hdev, field, usage); + wacom_wac_pad_usage_mapping(hdev, field, usage); else if (WACOM_PEN_FIELD(field)) - return wacom_wac_pen_usage_mapping(hdev, field, usage); + wacom_wac_pen_usage_mapping(hdev, field, usage); else if (WACOM_FINGER_FIELD(field)) - return wacom_wac_finger_usage_mapping(hdev, field, usage); + wacom_wac_finger_usage_mapping(hdev, field, usage); } -int wacom_wac_event(struct hid_device *hdev, struct hid_field *field, +void wacom_wac_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct wacom *wacom = hid_get_drvdata(hdev); if (wacom->wacom_wac.features.type != HID_GENERIC) - return 0; + return; if (WACOM_PAD_FIELD(field)) - return wacom_wac_pad_event(hdev, field, usage, value); + wacom_wac_pad_event(hdev, field, usage, value); else if (WACOM_PEN_FIELD(field)) - return wacom_wac_pen_event(hdev, field, usage, value); + wacom_wac_pen_event(hdev, field, usage, value); else if (WACOM_FINGER_FIELD(field)) - return wacom_wac_finger_event(hdev, field, usage, value); - - return 0; + wacom_wac_finger_event(hdev, field, usage, value); } static void wacom_report_events(struct hid_device *hdev, struct hid_report *report) -- cgit v1.2.3-55-g7522 From 6f46cf9b40df7d181f11989bab6f4cc08787cdfe Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Thu, 8 Dec 2016 22:04:52 -0800 Subject: HID: wacom: generic: Send data only when the interface is defined Sometime valid events may not be supported by the driver yet. Make sure we don't process them when the code is not ready. This fix prevents a kernel panic due to unsupported HID events. Signed-off-by: Ping Cheng Reviewed-By: Jason Gerecke Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index f1c49a44c06a..29d261042fb2 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2109,11 +2109,11 @@ void wacom_wac_event(struct hid_device *hdev, struct hid_field *field, if (wacom->wacom_wac.features.type != HID_GENERIC) return; - if (WACOM_PAD_FIELD(field)) + if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input) wacom_wac_pad_event(hdev, field, usage, value); - else if (WACOM_PEN_FIELD(field)) + else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input) wacom_wac_pen_event(hdev, field, usage, value); - else if (WACOM_FINGER_FIELD(field)) + else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input) wacom_wac_finger_event(hdev, field, usage, value); } @@ -2145,20 +2145,20 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report) if (wacom_wac->features.type != HID_GENERIC) return; - if (WACOM_PAD_FIELD(field)) + if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input) wacom_wac_pad_pre_report(hdev, report); - else if (WACOM_PEN_FIELD(field)) + else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input) wacom_wac_pen_pre_report(hdev, report); - else if (WACOM_FINGER_FIELD(field)) + else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input) wacom_wac_finger_pre_report(hdev, report); wacom_report_events(hdev, report); - if (WACOM_PAD_FIELD(field)) + if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input) return wacom_wac_pad_report(hdev, report); - else if (WACOM_PEN_FIELD(field)) + else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input) return wacom_wac_pen_report(hdev, report); - else if (WACOM_FINGER_FIELD(field)) + else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input) return wacom_wac_finger_report(hdev, report); } -- cgit v1.2.3-55-g7522 From f3f24e7b69841214b62cb0669527b7121d637a9e Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Thu, 8 Dec 2016 22:05:44 -0800 Subject: HID: wacom: generic: Pad supports more than buttons Make sure everything reported from pad are registered Signed-off-by: Ping Cheng Reviewed-By: Jason Gerecke Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 29d261042fb2..7a748a71d149 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1538,14 +1538,17 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, case WACOM_HID_WD_ACCELEROMETER_X: __set_bit(INPUT_PROP_ACCELEROMETER, input->propbit); wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 0); + features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_ACCELEROMETER_Y: __set_bit(INPUT_PROP_ACCELEROMETER, input->propbit); wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 0); + features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_ACCELEROMETER_Z: __set_bit(INPUT_PROP_ACCELEROMETER, input->propbit); wacom_map_usage(input, usage, field, EV_ABS, ABS_Z, 0); + features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_BUTTONHOME: case WACOM_HID_WD_BUTTONUP: @@ -1557,18 +1560,23 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, wacom_numbered_button_to_key(features->numbered_buttons), 0); features->numbered_buttons++; + features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_TOUCHONOFF: wacom_map_usage(input, usage, field, EV_SW, SW_MUTE_DEVICE, 0); + features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_TOUCHSTRIP: wacom_map_usage(input, usage, field, EV_ABS, ABS_RX, 0); + features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_TOUCHSTRIP2: wacom_map_usage(input, usage, field, EV_ABS, ABS_RY, 0); + features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_TOUCHRING: wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0); + features->device_type |= WACOM_DEVICETYPE_PAD; break; } @@ -1578,6 +1586,7 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, wacom_numbered_button_to_key(features->numbered_buttons), 0); features->numbered_buttons++; + features->device_type |= WACOM_DEVICETYPE_PAD; break; } } -- cgit v1.2.3-55-g7522 From c9cfb2aca26505729fc37716038c692208b2a8c9 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Thu, 8 Dec 2016 22:06:15 -0800 Subject: HID: wacom: generic: Don't sync input on empty input packets post input_sync only when there are input events posted Signed-off-by: Ping Cheng Reviewed-By: Jason Gerecke Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 76 +++++++++++++++++++++++++++++++++---------------- drivers/hid/wacom_wac.h | 1 + 2 files changed, 52 insertions(+), 25 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 7a748a71d149..b1a9a3ca6d56 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1591,18 +1591,13 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, } } -static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field, +static void wacom_wac_pad_battery_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; - struct input_dev *input = wacom_wac->pad_input; unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); - if (wacom_equivalent_usage(field->physical) == HID_DG_TABLETFUNCTIONKEY) { - wacom_wac->hid_data.inrange_state |= value; - } - switch (equivalent_usage) { case WACOM_HID_WD_BATTERY_LEVEL: wacom_wac->hid_data.battery_capacity = value; @@ -1614,11 +1609,28 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field wacom_wac->hid_data.ps_connected = value; wacom_wac->hid_data.bat_connected = 1; break; + } +} +static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct input_dev *input = wacom_wac->pad_input; + struct wacom_features *features = &wacom_wac->features; + unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); + + if (wacom_equivalent_usage(field->physical) == HID_DG_TABLETFUNCTIONKEY) { + wacom_wac->hid_data.inrange_state |= value; + } + + switch (equivalent_usage) { case WACOM_HID_WD_TOUCHRINGSTATUS: break; default: + features->input_event_flag = true; input_event(input, usage->type, usage->code, value); break; } @@ -1633,21 +1645,12 @@ static void wacom_wac_pad_pre_report(struct hid_device *hdev, wacom_wac->hid_data.inrange_state = 0; } -static void wacom_wac_pad_report(struct hid_device *hdev, +static void wacom_wac_pad_battery_report(struct hid_device *hdev, struct hid_report *report) { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct wacom_features *features = &wacom_wac->features; - struct input_dev *input = wacom_wac->pad_input; - bool active = wacom_wac->hid_data.inrange_state != 0; - - /* - * don't report prox for events like accelerometer - * or battery status - */ - if (wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) - input_event(input, EV_ABS, ABS_MISC, active ? PAD_DEVICE_ID : 0); if (features->quirks & WACOM_QUIRK_BATTERY) { int capacity = wacom_wac->hid_data.battery_capacity; @@ -1658,8 +1661,27 @@ static void wacom_wac_pad_report(struct hid_device *hdev, wacom_notify_battery(wacom_wac, capacity, charging, connected, powered); } +} - input_sync(input); +static void wacom_wac_pad_report(struct hid_device *hdev, + struct hid_report *report) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct wacom_features *features = &wacom_wac->features; + struct input_dev *input = wacom_wac->pad_input; + bool active = wacom_wac->hid_data.inrange_state != 0; + + /* report prox for expresskey events */ + if (wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) { + features->input_event_flag = true; + input_event(input, EV_ABS, ABS_MISC, active ? PAD_DEVICE_ID : 0); + } + + if (features->input_event_flag) { + features->input_event_flag = false; + input_sync(input); + } } static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, @@ -2118,9 +2140,11 @@ void wacom_wac_event(struct hid_device *hdev, struct hid_field *field, if (wacom->wacom_wac.features.type != HID_GENERIC) return; - if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input) - wacom_wac_pad_event(hdev, field, usage, value); - else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input) + if (WACOM_PAD_FIELD(field)) { + wacom_wac_pad_battery_event(hdev, field, usage, value); + if (wacom->wacom_wac.pad_input) + wacom_wac_pad_event(hdev, field, usage, value); + } else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input) wacom_wac_pen_event(hdev, field, usage, value); else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input) wacom_wac_finger_event(hdev, field, usage, value); @@ -2163,12 +2187,14 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report) wacom_report_events(hdev, report); - if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input) - return wacom_wac_pad_report(hdev, report); - else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input) - return wacom_wac_pen_report(hdev, report); + if (WACOM_PAD_FIELD(field)) { + wacom_wac_pad_battery_report(hdev, report); + if (wacom->wacom_wac.pad_input) + wacom_wac_pad_report(hdev, report); + } else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input) + wacom_wac_pen_report(hdev, report); else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input) - return wacom_wac_finger_report(hdev, report); + wacom_wac_finger_report(hdev, report); } static int wacom_bpt_touch(struct wacom_wac *wacom) diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index a54a3017a23f..fb0e50acb10d 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -232,6 +232,7 @@ struct wacom_features { int pktlen; bool check_for_hid_type; int hid_type; + bool input_event_flag; }; struct wacom_shared { -- cgit v1.2.3-55-g7522 From c60fa555b11b25386b355c141d90cbee361c3eec Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Mon, 5 Dec 2016 19:37:24 +0800 Subject: HID: usbhid: fix improper return value Function hid_post_reset() should return negative error codes on failures. However, in its implementation, it incorrectly returns 1. This patch fixes the bug, returning proper error codes on failures. Signed-off-by: Pan Bian Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index ae83af649a60..333108ef18cf 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1459,7 +1459,7 @@ static int hid_post_reset(struct usb_interface *intf) rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL); if (!rdesc) { dbg_hid("couldn't allocate rdesc memory (post_reset)\n"); - return 1; + return -ENOMEM; } status = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, @@ -1467,13 +1467,13 @@ static int hid_post_reset(struct usb_interface *intf) if (status < 0) { dbg_hid("reading report descriptor failed (post_reset)\n"); kfree(rdesc); - return 1; + return status; } status = memcmp(rdesc, hid->dev_rdesc, hid->dev_rsize); kfree(rdesc); if (status != 0) { dbg_hid("report descriptor changed\n"); - return 1; + return -EPERM; } /* No need to do another reset or clear a halted endpoint */ -- cgit v1.2.3-55-g7522 From d46ddc593f4da8d6c1b068503c04e063fa3a0e70 Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Tue, 6 Dec 2016 15:48:35 -0500 Subject: HID: i2c-hid: Disable IRQ before freeing buffers The HID report buffers that are initially allocated on i2c_hid_probe() might not be big enough to hold the HID reports from a specific device, in which case they will be freed and new ones will be allocated in i2c_hid_start(), at point which the device's report size is known. But at this point ihid->irq is already running, and may call i2c_hid_get_input() which passes ihid->inbuf to i2c_master_recv(). Since this handler runs in a separate thread, ihid->inbuf may be freed at this very moment, and i2c_master_recv() will write on memory which may be already owned by a different part of the kernel, corrupting its data. This problem has been observed on an Asus UX360UA laptop which has an I2C touchpad, and results in a complete system freeze or an unusable slowness with a lof of "BUG: unable to handle kernel paging request at
" warnings. Enabling SLUB debugging shows a use-after-free warning on memory allocated in i2c_hid_alloc_buffers() and freed in i2c_hid_free_buffers(): ============================================================================= BUG kmalloc-64 (Not tainted): Poison overwritten ----------------------------------------------------------------------------- Disabling lock debugging due to kernel taint INFO: 0xffff880264083273-0xffff88026408329e. first byte 0x0 instead of 0x6b INFO: Allocated in i2c_hid_alloc_buffers+0x25/0xa0 [i2c_hid] age=35793 cpu=2 pid=430 ___slab_alloc+0x41e/0x460 __slab_alloc+0x20/0x40 __kmalloc+0x210/0x280 i2c_hid_alloc_buffers+0x25/0xa0 [i2c_hid] i2c_hid_probe+0x12f/0x5e0 [i2c_hid] i2c_device_probe+0x10a/0x1b0 driver_probe_device+0x220/0x4a0 __device_attach_driver+0x71/0xa0 bus_for_each_drv+0x67/0xb0 __device_attach+0xdc/0x170 device_initial_probe+0x13/0x20 bus_probe_device+0x92/0xa0 device_add+0x4aa/0x670 device_register+0x1a/0x20 i2c_new_device+0x18e/0x230 acpi_i2c_add_device+0x1a0/0x210 INFO: Freed in i2c_hid_free_buffers+0x16/0x60 [i2c_hid] age=7552 cpu=1 pid=1473 __slab_free+0x221/0x330 kfree+0x139/0x160 i2c_hid_free_buffers+0x16/0x60 [i2c_hid] i2c_hid_start+0x2a9/0x2df [i2c_hid] mt_probe+0x160/0x22e [hid_multitouch] hid_device_probe+0xd7/0x150 [hid] driver_probe_device+0x220/0x4a0 __driver_attach+0x84/0x90 bus_for_each_dev+0x6c/0xc0 driver_attach+0x1e/0x20 bus_add_driver+0x1c3/0x280 driver_register+0x60/0xe0 __hid_register_driver+0x53/0x90 [hid] 0xffffffffc004f01e do_one_initcall+0xb3/0x1f0 do_init_module+0x5f/0x1d0 INFO: Slab 0xffffea0009902080 objects=20 used=20 fp=0x (null) flags=0x17fff8000004080 INFO: Object 0xffff880264083260 @offset=4704 fp=0x (null) Bytes b4 ffff880264083250: 8d e6 fe ff 00 00 00 00 5a 5a 5a 5a 5a 5a 5a 5a ........ZZZZZZZZ Object ffff880264083260: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk Object ffff880264083270: 6b 6b 6b 00 00 00 00 00 00 00 00 00 00 00 00 00 kkk............. Object ffff880264083280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ Object ffff880264083290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ Redzone ffff8802640832a0: bb bb bb bb bb bb bb bb ........ Padding ffff8802640833e0: 5a 5a 5a 5a 5a 5a 5a 5a ZZZZZZZZ CPU: 1 PID: 1503 Comm: python3 Tainted: G B 4.4.21+ #10 Hardware name: ASUSTeK COMPUTER INC. UX360UA/UX360UA, BIOS UX360UA.200 05/05/2016 0000000000000086 00000000622d48a2 ffff88026061ba38 ffffffff813f6044 ffff880264082010 ffff880264083260 ffff88026061ba78 ffffffff811e8eab 0000000000000008 ffff880200000001 ffff88026408329f ffff88026a007700 Call Trace: [] dump_stack+0x63/0x8f [] print_trailer+0x14b/0x1f0 [] check_bytes_and_report+0xc1/0x100 [] check_object+0x1c4/0x240 [] ? ext4_htree_store_dirent+0x3e/0x120 [] alloc_debug_processing+0x104/0x180 [] ___slab_alloc+0x41e/0x460 [] ? ext4_htree_store_dirent+0x3e/0x120 [] ? __getblk_gfp+0x2b/0x60 [] ? ext4_getblk+0xa9/0x190 [] __slab_alloc+0x20/0x40 [] __kmalloc+0x210/0x280 [] ? ext4_htree_store_dirent+0x3e/0x120 [] ? ext4fs_dirhash+0xc2/0x2a0 [] ext4_htree_store_dirent+0x3e/0x120 [] htree_dirblock_to_tree+0x187/0x1b0 [] ext4_htree_fill_tree+0xb2/0x2e0 [] ? kmem_cache_alloc_trace+0x1fa/0x220 [] ? ext4_readdir+0x775/0x8b0 [] ext4_readdir+0x5e1/0x8b0 [] iterate_dir+0x92/0x120 [] SyS_getdents+0x98/0x110 [] ? iterate_dir+0x120/0x120 [] entry_SYSCALL_64_fastpath+0x16/0x71 FIX kmalloc-64: Restoring 0xffff880264083273-0xffff88026408329e=0x6b FIX kmalloc-64: Marking all objects used Signed-off-by: João Paulo Rechi Vita Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 8d53efe04dad..50d23e85b399 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -769,9 +769,11 @@ static int i2c_hid_start(struct hid_device *hid) i2c_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize); if (bufsize > ihid->bufsize) { + disable_irq(ihid->irq); i2c_hid_free_buffers(ihid); ret = i2c_hid_alloc_buffers(ihid, bufsize); + enable_irq(ihid->irq); if (ret) return ret; -- cgit v1.2.3-55-g7522 From ba1660f1791f31a76da247caebf273b3f3d5071c Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 9 Dec 2016 13:37:07 +0100 Subject: HID: i2c-hid: fix build Add a forgotten include that I've by mistake omitted when resolving merge conflict in ead0687fe30 ("HID: i2c-hid: support regulator power on/off"). Fixes: ead0687fe30 ("HID: i2c-hid: support regulator power on/off") Reported-by: kbuild test robot Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hid') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 50d23e85b399..5b90ddaa9350 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -38,6 +38,7 @@ #include #include #include +#include #include -- cgit v1.2.3-55-g7522 From 8cd16166b0002df427ed39e4970e0be4830199b2 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sat, 10 Dec 2016 21:58:55 +0100 Subject: HID: fix missing irq field commit ba18a9314a94 ("Revert "HID: i2c-hid: Add support for ACPI GPIO interrupts"") removed the need for storing the irq in struct i2c_hid. But then commit de3c99488609 ("HID: i2c-hid: Disable IRQ before freeing buffers") forgot to update the location of the irq. Fix this by using the actual I2C client irq. Reported-by: kbuild test robot Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 5b90ddaa9350..78fb32a7b103 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -770,11 +770,11 @@ static int i2c_hid_start(struct hid_device *hid) i2c_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize); if (bufsize > ihid->bufsize) { - disable_irq(ihid->irq); + disable_irq(client->irq); i2c_hid_free_buffers(ihid); ret = i2c_hid_alloc_buffers(ihid, bufsize); - enable_irq(ihid->irq); + enable_irq(client->irq); if (ret) return ret; -- cgit v1.2.3-55-g7522