From 37d1601938349e79e9c31a8aba431d5543e09e72 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 28 Apr 2017 09:25:30 -0700 Subject: [PATCH 01/35] HID: wacom: generic: Scale battery capacity measurements to percentages The power_supply subsystem expects us to provide it with capacity values measured in percent. In particular, AES devices (HID_DG_BATTERYSTRENGTH) use the range 0-255, which needs to be rescaled. The MobileStudio Pro (WACOM_HID_WD_BATTERY_LEVEL) uses the range 0-100, but there's no guarantee that future devices will share the same range. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 4b225fb19a16..fd989e09ae2d 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1811,6 +1811,7 @@ static void wacom_wac_pad_battery_event(struct hid_device *hdev, struct hid_fiel switch (equivalent_usage) { case WACOM_HID_WD_BATTERY_LEVEL: + value = value * 100 / (field->logical_maximum - field->logical_minimum); wacom_wac->hid_data.battery_capacity = value; wacom_wac->hid_data.bat_connected = 1; break; @@ -2035,6 +2036,7 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field wacom_wac->hid_data.sense_state = value; return; case HID_DG_BATTERYSTRENGTH: + value = value * 100 / (field->logical_maximum - field->logical_minimum); wacom_wac->hid_data.battery_capacity = value; wacom_wac->hid_data.bat_connected = 1; break; From f496c09c0785b60fa6b762ad720ba31f6a9de0ac Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 28 Apr 2017 09:25:31 -0700 Subject: [PATCH 02/35] HID: wacom: generic: Ignore HID_DG_BATTERYSTRENTH == 0 AES sensors use the value 0 to indicate "not available" rather than "completely dead". Such values are often sent for dozens of reports while the pen is being brought into proximity and can cause userspace to get the wrong impression about the actual battery state. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index fd989e09ae2d..70a9e47b215a 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2036,6 +2036,8 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field wacom_wac->hid_data.sense_state = value; return; case HID_DG_BATTERYSTRENGTH: + if (value == 0) /* "not available" */ + break; value = value * 100 / (field->logical_maximum - field->logical_minimum); wacom_wac->hid_data.battery_capacity = value; wacom_wac->hid_data.bat_connected = 1; From a7758702879e68d221cf6dd9844bf5b2a070c8cb Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 28 Apr 2017 09:25:32 -0700 Subject: [PATCH 03/35] HID: wacom: generic: Report AES battery information When support for the HID_DG_BATTERYSTRENGTH usage was added for AES devices, it appears that the value was read, but never actually forwarded to the power_supply subystem for userspace's benefit. Let's correct that. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 70a9e47b215a..3e034506778f 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2177,6 +2177,8 @@ static void wacom_wac_pen_report(struct hid_device *hdev, input_sync(input); } + wacom_wac_pad_battery_report(hdev, report); + if (!prox) { wacom_wac->tool[0] = 0; wacom_wac->id[0] = 0; From 16e4598905a9d7793350ffad2f627b3dfdb7b595 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 28 Apr 2017 09:25:33 -0700 Subject: [PATCH 04/35] HID: wacom: Add ability to provide explicit battery status info At the moment, our driver relies on 'wacom_battery_get_property()' to determine the most likely battery state (e.g charging, discharging, or full) based on the information available. It is not always possible for the function to properly determine this, however. For instance, whenever an AES pen leaves proximity the battery state becomes indeterminite. This commit adds the ability to provide it with explict state information if desired. Whenever explicit state is not required (the majority of circumstances), WACOM_POWER_SUPPLY_STATUS_AUTO can be used in its place. Three uses of explicit battery status are added: two wireless disconnect paths and the AES case mentioned above. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom.h | 1 + drivers/hid/wacom_sys.c | 4 ++- drivers/hid/wacom_wac.c | 62 +++++++++++++++++++++++++---------------- drivers/hid/wacom_wac.h | 3 ++ 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h index c7b9ab1907d8..3c37c3cbf6f1 100644 --- a/drivers/hid/wacom.h +++ b/drivers/hid/wacom.h @@ -138,6 +138,7 @@ struct wacom_battery { struct power_supply_desc bat_desc; struct power_supply *battery; char bat_name[WACOM_NAME_MAX]; + int bat_status; int battery_capacity; int bat_charging; int bat_connected; diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 0022c0dac88a..838c1ebfffa9 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -1547,7 +1547,9 @@ static int wacom_battery_get_property(struct power_supply *psy, val->intval = battery->battery_capacity; break; case POWER_SUPPLY_PROP_STATUS: - if (battery->bat_charging) + if (battery->bat_status != WACOM_POWER_SUPPLY_STATUS_AUTO) + val->intval = battery->bat_status; + else if (battery->bat_charging) val->intval = POWER_SUPPLY_STATUS_CHARGING; else if (battery->battery_capacity == 100 && battery->ps_connected) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 3e034506778f..08a865f733fa 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -57,15 +57,18 @@ static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 }; static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 }; static void __wacom_notify_battery(struct wacom_battery *battery, - int bat_capacity, bool bat_charging, - bool bat_connected, bool ps_connected) + int bat_status, int bat_capacity, + bool bat_charging, bool bat_connected, + bool ps_connected) { - bool changed = battery->battery_capacity != bat_capacity || + bool changed = battery->bat_status != bat_status || + battery->battery_capacity != bat_capacity || battery->bat_charging != bat_charging || battery->bat_connected != bat_connected || battery->ps_connected != ps_connected; if (changed) { + battery->bat_status = bat_status; battery->battery_capacity = bat_capacity; battery->bat_charging = bat_charging; battery->bat_connected = bat_connected; @@ -77,13 +80,13 @@ static void __wacom_notify_battery(struct wacom_battery *battery, } static void wacom_notify_battery(struct wacom_wac *wacom_wac, - int bat_capacity, bool bat_charging, bool bat_connected, - bool ps_connected) + int bat_status, int bat_capacity, bool bat_charging, + bool bat_connected, bool ps_connected) { struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); - __wacom_notify_battery(&wacom->battery, bat_capacity, bat_charging, - bat_connected, ps_connected); + __wacom_notify_battery(&wacom->battery, bat_status, bat_capacity, + bat_charging, bat_connected, ps_connected); } static int wacom_penpartner_irq(struct wacom_wac *wacom) @@ -448,8 +451,9 @@ static int wacom_graphire_irq(struct wacom_wac *wacom) rw = (data[7] >> 2 & 0x07); battery_capacity = batcap_gr[rw]; ps_connected = rw == 7; - wacom_notify_battery(wacom, battery_capacity, ps_connected, - 1, ps_connected); + wacom_notify_battery(wacom, WACOM_POWER_SUPPLY_STATUS_AUTO, + battery_capacity, ps_connected, 1, + ps_connected); } exit: return retval; @@ -1071,7 +1075,8 @@ static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len) wacom->led.groups[i].select = touch_ring_mode; } - __wacom_notify_battery(&remote->remotes[index].battery, bat_percent, + __wacom_notify_battery(&remote->remotes[index].battery, + WACOM_POWER_SUPPLY_STATUS_AUTO, bat_percent, bat_charging, 1, bat_charging); out: @@ -1157,7 +1162,8 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len) bat_charging = (power_raw & 0x08) ? 1 : 0; ps_connected = (power_raw & 0x10) ? 1 : 0; battery_capacity = batcap_i4[power_raw & 0x07]; - wacom_notify_battery(wacom, battery_capacity, bat_charging, + wacom_notify_battery(wacom, WACOM_POWER_SUPPLY_STATUS_AUTO, + battery_capacity, bat_charging, battery_capacity || bat_charging, ps_connected); break; @@ -1334,7 +1340,8 @@ static void wacom_intuos_pro2_bt_battery(struct wacom_wac *wacom) bool chg = data[284] & 0x80; int battery_status = data[284] & 0x7F; - wacom_notify_battery(wacom, battery_status, chg, 1, chg); + wacom_notify_battery(wacom, WACOM_POWER_SUPPLY_STATUS_AUTO, + battery_status, chg, 1, chg); } static int wacom_intuos_pro2_bt_irq(struct wacom_wac *wacom, size_t len) @@ -1814,6 +1821,7 @@ static void wacom_wac_pad_battery_event(struct hid_device *hdev, struct hid_fiel value = value * 100 / (field->logical_maximum - field->logical_minimum); wacom_wac->hid_data.battery_capacity = value; wacom_wac->hid_data.bat_connected = 1; + wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO; break; case WACOM_HID_WD_BATTERY_CHARGING: @@ -1905,13 +1913,14 @@ static void wacom_wac_pad_battery_report(struct hid_device *hdev, struct wacom_features *features = &wacom_wac->features; if (features->quirks & WACOM_QUIRK_BATTERY) { + int status = wacom_wac->hid_data.bat_status; 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); + wacom_notify_battery(wacom_wac, status, capacity, + charging, connected, powered); } } @@ -2036,11 +2045,15 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field wacom_wac->hid_data.sense_state = value; return; case HID_DG_BATTERYSTRENGTH: - if (value == 0) /* "not available" */ - break; - value = value * 100 / (field->logical_maximum - field->logical_minimum); - wacom_wac->hid_data.battery_capacity = value; - wacom_wac->hid_data.bat_connected = 1; + if (value == 0) { + wacom_wac->hid_data.bat_status = POWER_SUPPLY_STATUS_UNKNOWN; + } + else { + value = value * 100 / (field->logical_maximum - field->logical_minimum); + wacom_wac->hid_data.battery_capacity = value; + wacom_wac->hid_data.bat_connected = 1; + wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO; + } break; case HID_DG_INVERT: wacom_wac->hid_data.invert_state = value; @@ -2818,13 +2831,14 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) wacom_schedule_work(wacom, WACOM_WORKER_WIRELESS); } - wacom_notify_battery(wacom, battery, charging, 1, 0); + wacom_notify_battery(wacom, WACOM_POWER_SUPPLY_STATUS_AUTO, + battery, charging, 1, 0); } else if (wacom->pid != 0) { /* disconnected while previously connected */ wacom->pid = 0; wacom_schedule_work(wacom, WACOM_WORKER_WIRELESS); - wacom_notify_battery(wacom, 0, 0, 0, 0); + wacom_notify_battery(wacom, POWER_SUPPLY_STATUS_UNKNOWN, 0, 0, 0, 0); } return 0; @@ -2852,8 +2866,8 @@ static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len) int battery = (data[8] & 0x3f) * 100 / 31; bool charging = !!(data[8] & 0x80); - wacom_notify_battery(wacom_wac, battery, charging, - battery || charging, 1); + wacom_notify_battery(wacom_wac, WACOM_POWER_SUPPLY_STATUS_AUTO, + battery, charging, battery || charging, 1); if (!wacom->battery.battery && !(features->quirks & WACOM_QUIRK_BATTERY)) { @@ -2865,7 +2879,7 @@ static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len) wacom->battery.battery) { features->quirks &= ~WACOM_QUIRK_BATTERY; wacom_schedule_work(wacom_wac, WACOM_WORKER_BATTERY); - wacom_notify_battery(wacom_wac, 0, 0, 0, 0); + wacom_notify_battery(wacom_wac, POWER_SUPPLY_STATUS_UNKNOWN, 0, 0, 0, 0); } return 0; } diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 570d29582b82..1824b530bcb5 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -96,6 +96,8 @@ #define WACOM_DEVICETYPE_WL_MONITOR 0x0008 #define WACOM_DEVICETYPE_DIRECT 0x0010 +#define WACOM_POWER_SUPPLY_STATUS_AUTO -1 + #define WACOM_HID_UP_WACOMDIGITIZER 0xff0d0000 #define WACOM_HID_SP_PAD 0x00040000 #define WACOM_HID_SP_BUTTON 0x00090000 @@ -297,6 +299,7 @@ struct hid_data { int last_slot_field; int num_expected; int num_received; + int bat_status; int battery_capacity; int bat_charging; int bat_connected; From 5ac3d4ae58050f451a4fd868028f25258ea0a628 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 28 Apr 2017 09:25:34 -0700 Subject: [PATCH 05/35] HID: wacom: generic: Refactor generic battery handling Generic battery handling code is spread between the pen and pad codepaths since battery usages may appear in reports for either. This makes it difficult to concisely see the logic involved. Since battery data is not treated like other data (i.e., we report it through the power_supply subsystem rather than through the input subsystem), it makes reasonable sense to split the functionality out into its own functions. This commit has the generic battery handling duplicate the same pattern that is used by the pen, pad, and touch interfaces. A "mapping" function is provided to set up the battery, an "event" function is provided to update the battery data, and a "report" function is provided to notify the power_supply subsystem after all the data has been read. We look at the usage itself rather than its collection to determine if one of the battery functions should handle it. Additionally, we unconditionally call the "report" function since there is no particularly good way to know if a report contained a battery usage; 'wacom_notify_battery()' will filter out any duplicate updates, however. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 166 ++++++++++++++++++++++------------------ drivers/hid/wacom_wac.h | 4 + 2 files changed, 97 insertions(+), 73 deletions(-) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 08a865f733fa..aa0becea865e 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1702,6 +1702,82 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, } } +static void wacom_wac_battery_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; + unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); + + switch (equivalent_usage) { + case HID_DG_BATTERYSTRENGTH: + case WACOM_HID_WD_BATTERY_LEVEL: + case WACOM_HID_WD_BATTERY_CHARGING: + features->quirks |= WACOM_QUIRK_BATTERY; + break; + } +} + +static void wacom_wac_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; + unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); + + switch (equivalent_usage) { + case HID_DG_BATTERYSTRENGTH: + if (value == 0) { + wacom_wac->hid_data.bat_status = POWER_SUPPLY_STATUS_UNKNOWN; + } + else { + value = value * 100 / (field->logical_maximum - field->logical_minimum); + wacom_wac->hid_data.battery_capacity = value; + wacom_wac->hid_data.bat_connected = 1; + wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO; + } + break; + case WACOM_HID_WD_BATTERY_LEVEL: + value = value * 100 / (field->logical_maximum - field->logical_minimum); + wacom_wac->hid_data.battery_capacity = value; + wacom_wac->hid_data.bat_connected = 1; + wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO; + 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; + wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO; + break; + } +} + +static void wacom_wac_battery_pre_report(struct hid_device *hdev, + struct hid_report *report) +{ + return; +} + +static void wacom_wac_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; + + if (features->quirks & WACOM_QUIRK_BATTERY) { + int status = wacom_wac->hid_data.bat_status; + 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, status, capacity, charging, + connected, powered); + } +} + static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { @@ -1712,10 +1788,6 @@ 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); @@ -1809,29 +1881,6 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, } } -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; - unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); - - switch (equivalent_usage) { - case WACOM_HID_WD_BATTERY_LEVEL: - value = value * 100 / (field->logical_maximum - field->logical_minimum); - wacom_wac->hid_data.battery_capacity = value; - wacom_wac->hid_data.bat_connected = 1; - wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO; - 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; - break; - } -} - static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { @@ -1905,25 +1954,6 @@ static void wacom_wac_pad_pre_report(struct hid_device *hdev, wacom_wac->hid_data.inrange_state = 0; } -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; - - if (features->quirks & WACOM_QUIRK_BATTERY) { - int status = wacom_wac->hid_data.bat_status; - 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, status, capacity, - charging, connected, powered); - } -} - static void wacom_wac_pad_report(struct hid_device *hdev, struct hid_report *report) { @@ -1969,9 +1999,6 @@ 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); @@ -2044,17 +2071,6 @@ static void 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; - case HID_DG_BATTERYSTRENGTH: - if (value == 0) { - wacom_wac->hid_data.bat_status = POWER_SUPPLY_STATUS_UNKNOWN; - } - else { - value = value * 100 / (field->logical_maximum - field->logical_minimum); - wacom_wac->hid_data.battery_capacity = value; - wacom_wac->hid_data.bat_connected = 1; - wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO; - } - break; case HID_DG_INVERT: wacom_wac->hid_data.invert_state = value; return; @@ -2190,8 +2206,6 @@ static void wacom_wac_pen_report(struct hid_device *hdev, input_sync(input); } - wacom_wac_pad_battery_report(hdev, report); - if (!prox) { wacom_wac->tool[0] = 0; wacom_wac->id[0] = 0; @@ -2413,7 +2427,10 @@ void wacom_wac_usage_mapping(struct hid_device *hdev, if (WACOM_DIRECT_DEVICE(field)) features->device_type |= WACOM_DEVICETYPE_DIRECT; - if (WACOM_PAD_FIELD(field)) + /* usage tests must precede field tests */ + if (WACOM_BATTERY_USAGE(usage)) + wacom_wac_battery_usage_mapping(hdev, field, usage); + else if (WACOM_PAD_FIELD(field)) wacom_wac_pad_usage_mapping(hdev, field, usage); else if (WACOM_PEN_FIELD(field)) wacom_wac_pen_usage_mapping(hdev, field, usage); @@ -2432,11 +2449,12 @@ void wacom_wac_event(struct hid_device *hdev, struct hid_field *field, if (value > field->logical_maximum || value < field->logical_minimum) return; - 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) + /* usage tests must precede field tests */ + if (WACOM_BATTERY_USAGE(usage)) + wacom_wac_battery_event(hdev, field, usage, value); + else 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) 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); @@ -2470,6 +2488,8 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report) if (wacom_wac->features.type != HID_GENERIC) return; + wacom_wac_battery_pre_report(hdev, report); + if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input) wacom_wac_pad_pre_report(hdev, report); else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input) @@ -2489,11 +2509,11 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report) if (report->type != HID_INPUT_REPORT) return; - 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_battery_report(hdev, report); + + if (WACOM_PAD_FIELD(field) && 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) wacom_wac_finger_report(hdev, report); diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 1824b530bcb5..8a03654048bf 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -153,6 +153,10 @@ #define WACOM_HID_WT_X (WACOM_HID_UP_WACOMTOUCH | 0x130) #define WACOM_HID_WT_Y (WACOM_HID_UP_WACOMTOUCH | 0x131) +#define WACOM_BATTERY_USAGE(f) (((f)->hid == HID_DG_BATTERYSTRENGTH) || \ + ((f)->hid == WACOM_HID_WD_BATTERY_CHARGING) || \ + ((f)->hid == WACOM_HID_WD_BATTERY_LEVEL)) + #define WACOM_PAD_FIELD(f) (((f)->physical == HID_DG_TABLETFUNCTIONKEY) || \ ((f)->physical == WACOM_HID_WD_DIGITIZERFNKEYS) || \ ((f)->physical == WACOM_HID_WD_DIGITIZERINFO)) From 61df56bef97e1708bfbc006b307b00834ad61fe8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 10 May 2017 17:12:52 +0200 Subject: [PATCH 06/35] HID: Add mapping for Microsoft Win8 Wireless Radio Controls extensions Microsoft has defined some extra HUT codes for the Generic Desktop Page for Wireless Radio controls, see: https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/airplane-mode-radio-management https://web.archive.org/web/20170509144631/https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/airplane-mode-radio-management I've 3 2-in-1 keyboard docks: Dell Venue Pro 11 keyboard dock, HP pavilion x2 keyboard dock and a PEAQ C1010 keyboard dock which have a wireless radio toggle hotkey, which uses the 0x000100c6 HUT code defined in these extensions. This commit adds a mapping for this key, this makes the rfkill toggle hotkey work on the Dell Venue Pro 11 and HP Pavilion X2 keyboards, the PEAQ C1010 keyboard does generate events for the 0x000100c6 HUT code when pressed, but the reported value is always 0. Signed-off-by: Hans de Goede Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 9 +++++++++ include/linux/hid.h | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index a1ebdd7d4d4d..412040b11268 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -656,6 +656,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_GD_START: map_key_clear(BTN_START); break; case HID_GD_SELECT: map_key_clear(BTN_SELECT); break; + case HID_GD_RFKILL_BTN: + /* MS wireless radio ctl extension, also check CA */ + if (field->application == 0x0001000c) { + map_key_clear(KEY_RFKILL); + /* We need to simulate the btn release */ + field->flags |= HID_MAIN_ITEM_RELATIVE; + break; + } + default: goto unknown; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 5be325d890d9..0b29466bbc21 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -182,6 +182,12 @@ struct hid_item { #define HID_GD_KEYBOARD 0x00010006 #define HID_GD_KEYPAD 0x00010007 #define HID_GD_MULTIAXIS 0x00010008 +/* + * Microsoft Win8 Wireless Radio Controls extensions CA, see (checked 09052017): + * https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/airplane-mode-radio-management + * https://web.archive.org/web/20170509144631/https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/airplane-mode-radio-management + */ +#define HID_GD_WIRELESS_RADIO_CTLS 0x0001000c #define HID_GD_X 0x00010030 #define HID_GD_Y 0x00010031 #define HID_GD_Z 0x00010032 @@ -210,6 +216,10 @@ struct hid_item { #define HID_GD_DOWN 0x00010091 #define HID_GD_RIGHT 0x00010092 #define HID_GD_LEFT 0x00010093 +/* Microsoft Win8 Wireless Radio Controls CA usage codes */ +#define HID_GD_RFKILL_BTN 0x000100c6 +#define HID_GD_RFKILL_LED 0x000100c7 +#define HID_GD_RFKILL_SWITCH 0x000100c8 #define HID_DC_BATTERYSTRENGTH 0x00060020 From f1918be1c1dd6d97390513677f184c4d5e22e2ac Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 10 May 2017 17:12:53 +0200 Subject: [PATCH 07/35] HID: ite: Add hid-ite driver The ITE8595 keyboard uses the HID_GD_RFKILL_BTN usage code from the Wireless Radio Controls Application Collection Microsoft has defined for Windows 8 and later. However it has a quirk, when the rfkill hotkey is pressed it does generate a report for the collection, but the reported value is always 0. Luckily it is the only button in this collection / report, and it sends a report on release only, so receiving a report means the button was pressed. This commit adds a hid-ite driver which watches for the Wireless Radio Controls Application Collection report and then reports a KEY_RFKILL event, ignoring the value, making the rfkill on this keyboard work. Signed-off-by: Hans de Goede Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 ++++++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-ite.c | 56 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+) create mode 100644 drivers/hid/hid-ite.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index fe40e5e499dd..c4f65ce65b9a 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -386,6 +386,13 @@ config HID_ICADE To compile this driver as a module, choose M here: the module will be called hid-icade. +config HID_ITE + tristate "ITE devices" + depends on HID + default !EXPERT + ---help--- + Support for ITE devices not fully compliant with HID standard. + config HID_TWINHAN tristate "Twinhan IR remote control" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fef027bc7fa3..05ac8d375aeb 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_HID_ICADE) += hid-icade.o +obj-$(CONFIG_HID_ITE) += hid-ite.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 37084b645785..bd48e1568462 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1913,6 +1913,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2) }, { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE8595) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_JESS_ZEN_AIO_KBD) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 643390ba749d..79674a3ee118 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -559,6 +559,7 @@ #define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386 #define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350 #define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396 +#define USB_DEVICE_ID_ITE8595 0x8595 #define USB_VENDOR_ID_JABRA 0x0b0e #define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 diff --git a/drivers/hid/hid-ite.c b/drivers/hid/hid-ite.c new file mode 100644 index 000000000000..1882a4ab0f29 --- /dev/null +++ b/drivers/hid/hid-ite.c @@ -0,0 +1,56 @@ +/* + * HID driver for some ITE "special" devices + * Copyright (c) 2017 Hans de Goede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +static int ite_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct input_dev *input; + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput) + return 0; + + input = field->hidinput->input; + + /* + * The ITE8595 always reports 0 as value for the rfkill button. Luckily + * it is the only button in its report, and it sends a report on + * release only, so receiving a report means the button was pressed. + */ + if (usage->hid == HID_GD_RFKILL_BTN) { + input_event(input, EV_KEY, KEY_RFKILL, 1); + input_sync(input); + input_event(input, EV_KEY, KEY_RFKILL, 0); + input_sync(input); + return 1; + } + + return 0; +} + +static const struct hid_device_id ite_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE8595) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ite_devices); + +static struct hid_driver ite_driver = { + .name = "itetech", + .id_table = ite_devices, + .event = ite_event, +}; +module_hid_driver(ite_driver); + +MODULE_LICENSE("GPL"); From 91b9ae48aadd7e634161372b0bc3ffc88a050e8b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 May 2017 22:30:33 +0200 Subject: [PATCH 08/35] HID: i2c-hid: move header file out of I2C realm include/linux/i2c is not for client devices. Move the header file to a more appropriate location. Signed-off-by: Wolfram Sang Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 2 +- include/linux/{i2c => platform_data}/i2c-hid.h | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename include/linux/{i2c => platform_data}/i2c-hid.h (100%) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 8daa8ce64ebb..841aa43526eb 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -40,7 +40,7 @@ #include #include -#include +#include #include "../hid-ids.h" diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/platform_data/i2c-hid.h similarity index 100% rename from include/linux/i2c/i2c-hid.h rename to include/linux/platform_data/i2c-hid.h From 6e7edabfc6a8ac5dce8c55363a7bb1576fc9348f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 11 May 2017 19:11:11 +0200 Subject: [PATCH 09/35] HID: Microsoft Win8 Wireless Radio Controls cleanup Use a better URL for the HUTRR40 Radio HID Usages documentation and use the HID_GD_WIRELESS_RADIO_CTLS define rather then hardcoding a check for 0x0001000c. Fixes: 61df56bef9 ("HID: Add mapping for Microsoft Win8 Wireless Radio Controls extensions") Suggested-by: Benjamin Tissoires Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 2 +- include/linux/hid.h | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 412040b11268..ccdff1ee1f0c 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -658,7 +658,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_GD_RFKILL_BTN: /* MS wireless radio ctl extension, also check CA */ - if (field->application == 0x0001000c) { + if (field->application == HID_GD_WIRELESS_RADIO_CTLS) { map_key_clear(KEY_RFKILL); /* We need to simulate the btn release */ field->flags |= HID_MAIN_ITEM_RELATIVE; diff --git a/include/linux/hid.h b/include/linux/hid.h index 0b29466bbc21..bebbf4893448 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -183,9 +183,8 @@ struct hid_item { #define HID_GD_KEYPAD 0x00010007 #define HID_GD_MULTIAXIS 0x00010008 /* - * Microsoft Win8 Wireless Radio Controls extensions CA, see (checked 09052017): - * https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/airplane-mode-radio-management - * https://web.archive.org/web/20170509144631/https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/airplane-mode-radio-management + * Microsoft Win8 Wireless Radio Controls extensions CA, see: + * http://www.usb.org/developers/hidpage/HUTRR40RadioHIDUsagesFinal.pdf */ #define HID_GD_WIRELESS_RADIO_CTLS 0x0001000c #define HID_GD_X 0x00010030 From 1260662fa3f293042fb0ae124c9a621f29f5bcab Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 May 2017 22:21:40 +0200 Subject: [PATCH 10/35] HID: intel_ish-hid: fix potential uninitialized data usage gcc points out an uninialized pointer dereference that could happen if we ever get to recv_ishtp_cl_msg_dma() or recv_ishtp_cl_msg() with an empty &dev->read_list: drivers/hid/intel-ish-hid/ishtp/client.c: In function 'recv_ishtp_cl_msg_dma': drivers/hid/intel-ish-hid/ishtp/client.c:1049:3: error: 'cl' may be used uninitialized in this function [-Werror=maybe-uninitialized] The warning only appeared in very few randconfig builds, as the spinlocks tend to prevent gcc from tracing the variables. I only saw it in configurations that had neither SMP nor LOCKDEP enabled. As we can see, we only enter the case if 'complete_rb' is non-NULL, and then 'cl' is known to point to complete_rb->cl. Adding another initialization to the same pointer is harmless here and makes it clear to the compiler that the behavior is well-defined. Signed-off-by: Arnd Bergmann Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ishtp/client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/intel-ish-hid/ishtp/client.c b/drivers/hid/intel-ish-hid/ishtp/client.c index aad61328f282..78d393e616a4 100644 --- a/drivers/hid/intel-ish-hid/ishtp/client.c +++ b/drivers/hid/intel-ish-hid/ishtp/client.c @@ -925,6 +925,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, } if (complete_rb) { + cl = complete_rb->cl; getnstimeofday(&cl->ts_rx); ++cl->recv_msg_cnt_ipc; ishtp_cl_read_complete(complete_rb); @@ -1045,6 +1046,7 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, } if (complete_rb) { + cl = complete_rb->cl; getnstimeofday(&cl->ts_rx); ++cl->recv_msg_cnt_dma; ishtp_cl_read_complete(complete_rb); From 538be0aa8610168d7a8f4b119452056f537cff46 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 May 2017 22:21:41 +0200 Subject: [PATCH 11/35] HID: intel_ish-hid: clarify locking in client code I was trying to understand this code while working on a warning fix and the locking made no sense: spin_lock_irqsave() is pointless when run inside of an interrupt handler or nested inside of another spin_lock_irq() or spin_lock_irqsave(). Here it turned out that the comment above the function is wrong, as both recv_ishtp_cl_msg_dma() and recv_ishtp_cl_msg() can in fact be called from a work queue rather than an ISR, so we do have to use the irqsave() version once. This fixes the comments accordingly, removes the misleading 'dev_flags' variable and modifies the inner spinlock to not use 'irqsave'. No functional change is intended, this is just for readability and it slightly simplifies the object code. Signed-off-by: Arnd Bergmann Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ishtp/client.c | 43 ++++++++++-------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/drivers/hid/intel-ish-hid/ishtp/client.c b/drivers/hid/intel-ish-hid/ishtp/client.c index 78d393e616a4..f54689ee67e1 100644 --- a/drivers/hid/intel-ish-hid/ishtp/client.c +++ b/drivers/hid/intel-ish-hid/ishtp/client.c @@ -803,7 +803,7 @@ void ishtp_cl_send_msg(struct ishtp_device *dev, struct ishtp_cl *cl) * @ishtp_hdr: Pointer to message header * * Receive and dispatch ISHTP client messages. This function executes in ISR - * context + * or work queue context */ void recv_ishtp_cl_msg(struct ishtp_device *dev, struct ishtp_msg_hdr *ishtp_hdr) @@ -813,7 +813,6 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, struct ishtp_cl_rb *new_rb; unsigned char *buffer = NULL; struct ishtp_cl_rb *complete_rb = NULL; - unsigned long dev_flags; unsigned long flags; int rb_count; @@ -828,7 +827,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, goto eoi; } - spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); + spin_lock_irqsave(&dev->read_list_spinlock, flags); rb_count = -1; list_for_each_entry(rb, &dev->read_list.list, list) { ++rb_count; @@ -840,8 +839,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, /* If no Rx buffer is allocated, disband the rb */ if (rb->buffer.size == 0 || rb->buffer.data == NULL) { - spin_unlock_irqrestore(&dev->read_list_spinlock, - dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); dev_err(&cl->device->dev, "Rx buffer is not allocated.\n"); list_del(&rb->list); @@ -857,8 +855,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, * back FC, so communication will be stuck anyway) */ if (rb->buffer.size < ishtp_hdr->length + rb->buf_idx) { - spin_unlock_irqrestore(&dev->read_list_spinlock, - dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); dev_err(&cl->device->dev, "message overflow. size %d len %d idx %ld\n", rb->buffer.size, ishtp_hdr->length, @@ -884,14 +881,13 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, * the whole msg arrived, send a new FC, and add a new * rb buffer for the next coming msg */ - spin_lock_irqsave(&cl->free_list_spinlock, flags); + spin_lock(&cl->free_list_spinlock); if (!list_empty(&cl->free_rb_list.list)) { new_rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb, list); list_del_init(&new_rb->list); - spin_unlock_irqrestore(&cl->free_list_spinlock, - flags); + spin_unlock(&cl->free_list_spinlock); new_rb->cl = cl; new_rb->buf_idx = 0; INIT_LIST_HEAD(&new_rb->list); @@ -900,8 +896,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, ishtp_hbm_cl_flow_control_req(dev, cl); } else { - spin_unlock_irqrestore(&cl->free_list_spinlock, - flags); + spin_unlock(&cl->free_list_spinlock); } } /* One more fragment in message (even if this was last) */ @@ -914,7 +909,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, break; } - spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); /* If it's nobody's message, just read and discard it */ if (!buffer) { uint8_t rd_msg_buf[ISHTP_RD_MSG_BUF_SIZE]; @@ -941,7 +936,7 @@ eoi: * @hbm: hbm buffer * * Receive and dispatch ISHTP client messages using DMA. This function executes - * in ISR context + * in ISR or work queue context */ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, struct dma_xfer_hbm *hbm) @@ -951,10 +946,10 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, struct ishtp_cl_rb *new_rb; unsigned char *buffer = NULL; struct ishtp_cl_rb *complete_rb = NULL; - unsigned long dev_flags; unsigned long flags; - spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); + spin_lock_irqsave(&dev->read_list_spinlock, flags); + list_for_each_entry(rb, &dev->read_list.list, list) { cl = rb->cl; if (!cl || !(cl->host_client_id == hbm->host_client_id && @@ -966,8 +961,7 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, * If no Rx buffer is allocated, disband the rb */ if (rb->buffer.size == 0 || rb->buffer.data == NULL) { - spin_unlock_irqrestore(&dev->read_list_spinlock, - dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); dev_err(&cl->device->dev, "response buffer is not allocated.\n"); list_del(&rb->list); @@ -983,8 +977,7 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, * back FC, so communication will be stuck anyway) */ if (rb->buffer.size < hbm->msg_length) { - spin_unlock_irqrestore(&dev->read_list_spinlock, - dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); dev_err(&cl->device->dev, "message overflow. size %d len %d idx %ld\n", rb->buffer.size, hbm->msg_length, rb->buf_idx); @@ -1008,14 +1001,13 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, * the whole msg arrived, send a new FC, and add a new * rb buffer for the next coming msg */ - spin_lock_irqsave(&cl->free_list_spinlock, flags); + spin_lock(&cl->free_list_spinlock); if (!list_empty(&cl->free_rb_list.list)) { new_rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb, list); list_del_init(&new_rb->list); - spin_unlock_irqrestore(&cl->free_list_spinlock, - flags); + spin_unlock(&cl->free_list_spinlock); new_rb->cl = cl; new_rb->buf_idx = 0; INIT_LIST_HEAD(&new_rb->list); @@ -1024,8 +1016,7 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, ishtp_hbm_cl_flow_control_req(dev, cl); } else { - spin_unlock_irqrestore(&cl->free_list_spinlock, - flags); + spin_unlock(&cl->free_list_spinlock); } /* One more fragment in message (this is always last) */ @@ -1038,7 +1029,7 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, break; } - spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); /* If it's nobody's message, just read and discard it */ if (!buffer) { dev_err(dev->devc, "Dropped Rx (DMA) msg - no request\n"); From 2503f7babbc7f570d06cfa3ca6b7ceec9262ced3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 May 2017 22:21:42 +0200 Subject: [PATCH 12/35] HID: intel_ish-hid: convert timespec to ktime_t The internal accounting uses 'timespec' based time stamps, which is slightly inefficient and also problematic once we get to the time_t overflow in 2038. When communicating to the firmware, we even get an open-coded 64-bit division that prevents the code from being build-tested on 32-bit architectures and is inefficient due to the double conversion from 64-bit nanoseconds to seconds+nanoseconds and then microseconds. This changes the code to use ktime_t instead. Signed-off-by: Arnd Bergmann Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/ipc.c | 15 ++++----------- drivers/hid/intel-ish-hid/ishtp/client.c | 4 ++-- drivers/hid/intel-ish-hid/ishtp/client.h | 6 +++--- drivers/hid/intel-ish-hid/ishtp/hbm.c | 11 ++++------- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index 842d8416a7a6..9a60ec13cb10 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -296,17 +296,12 @@ static int write_ipc_from_queue(struct ishtp_device *dev) /* If sending MNG_SYNC_FW_CLOCK, update clock again */ if (IPC_HEADER_GET_PROTOCOL(doorbell_val) == IPC_PROTOCOL_MNG && IPC_HEADER_GET_MNG_CMD(doorbell_val) == MNG_SYNC_FW_CLOCK) { - struct timespec ts_system; - struct timeval tv_utc; - uint64_t usec_system, usec_utc; + uint64_t usec_system, usec_utc; struct ipc_time_update_msg time_update; struct time_sync_format ts_format; - get_monotonic_boottime(&ts_system); - do_gettimeofday(&tv_utc); - usec_system = (timespec_to_ns(&ts_system)) / NSEC_PER_USEC; - usec_utc = (uint64_t)tv_utc.tv_sec * 1000000 + - ((uint32_t)tv_utc.tv_usec); + usec_system = ktime_to_us(ktime_get_boottime()); + usec_utc = ktime_to_us(ktime_get_real()); ts_format.ts1_source = HOST_SYSTEM_TIME_USEC; ts_format.ts2_source = HOST_UTC_TIME_USEC; ts_format.reserved = 0; @@ -575,15 +570,13 @@ static void fw_reset_work_fn(struct work_struct *unused) static void _ish_sync_fw_clock(struct ishtp_device *dev) { static unsigned long prev_sync; - struct timespec ts; uint64_t usec; if (prev_sync && jiffies - prev_sync < 20 * HZ) return; prev_sync = jiffies; - get_monotonic_boottime(&ts); - usec = (timespec_to_ns(&ts)) / NSEC_PER_USEC; + usec = ktime_to_us(ktime_get_boottime()); ipc_send_mng_msg(dev, MNG_SYNC_FW_CLOCK, &usec, sizeof(uint64_t)); } diff --git a/drivers/hid/intel-ish-hid/ishtp/client.c b/drivers/hid/intel-ish-hid/ishtp/client.c index f54689ee67e1..007443ef5fca 100644 --- a/drivers/hid/intel-ish-hid/ishtp/client.c +++ b/drivers/hid/intel-ish-hid/ishtp/client.c @@ -921,7 +921,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, if (complete_rb) { cl = complete_rb->cl; - getnstimeofday(&cl->ts_rx); + cl->ts_rx = ktime_get(); ++cl->recv_msg_cnt_ipc; ishtp_cl_read_complete(complete_rb); } @@ -1038,7 +1038,7 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, if (complete_rb) { cl = complete_rb->cl; - getnstimeofday(&cl->ts_rx); + cl->ts_rx = ktime_get(); ++cl->recv_msg_cnt_dma; ishtp_cl_read_complete(complete_rb); } diff --git a/drivers/hid/intel-ish-hid/ishtp/client.h b/drivers/hid/intel-ish-hid/ishtp/client.h index 444d069c2ed4..79eade547f5d 100644 --- a/drivers/hid/intel-ish-hid/ishtp/client.h +++ b/drivers/hid/intel-ish-hid/ishtp/client.h @@ -118,9 +118,9 @@ struct ishtp_cl { unsigned int out_flow_ctrl_cnt; /* Rx msg ... out FC timing */ - struct timespec ts_rx; - struct timespec ts_out_fc; - struct timespec ts_max_fc_delay; + ktime_t ts_rx; + ktime_t ts_out_fc; + ktime_t ts_max_fc_delay; void *client_data; }; diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.c b/drivers/hid/intel-ish-hid/ishtp/hbm.c index b7213608ce43..ae4a69f7f2f4 100644 --- a/drivers/hid/intel-ish-hid/ishtp/hbm.c +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.c @@ -321,13 +321,10 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev, if (!rv) { ++cl->out_flow_ctrl_creds; ++cl->out_flow_ctrl_cnt; - getnstimeofday(&cl->ts_out_fc); - if (cl->ts_rx.tv_sec && cl->ts_rx.tv_nsec) { - struct timespec ts_diff; - - ts_diff = timespec_sub(cl->ts_out_fc, cl->ts_rx); - if (timespec_compare(&ts_diff, &cl->ts_max_fc_delay) - > 0) + cl->ts_out_fc = ktime_get(); + if (cl->ts_rx) { + ktime_t ts_diff = ktime_sub(cl->ts_out_fc, cl->ts_rx); + if (ktime_after(ts_diff, cl->ts_max_fc_delay)) cl->ts_max_fc_delay = ts_diff; } } else { From 318fc2a867bc5bac688cb88f111eb75792675dc2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 May 2017 22:21:43 +0200 Subject: [PATCH 13/35] HID: intel_ish-hid: fix format string for size_t When building for 32-bit architectures, we get a harmless warning: intel-ish-hid/ishtp-hid-client.c: In function 'process_recv': intel-ish-hid/ishtp-hid-client.c:139:7: error: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'unsigned int' [-Werror=format=] This changes the format string to print size_t variables using %zu instead. Signed-off-by: Arnd Bergmann Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ishtp-hid-client.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c index 5c643d7a07b2..157b44aacdff 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid-client.c +++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c @@ -136,10 +136,9 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf, if (1 + sizeof(struct device_info) * i >= payload_len) { dev_err(&client_data->cl_device->dev, - "[hid-ish]: [ENUM_DEVICES]: content size %lu is bigger than payload_len %u\n", + "[hid-ish]: [ENUM_DEVICES]: content size %zu is bigger than payload_len %zu\n", 1 + sizeof(struct device_info) - * i, - (unsigned int)payload_len); + * i, payload_len); } if (1 + sizeof(struct device_info) * i >= From 21e04ddff42ec957ca86a95de0bb85ae6a9a36f5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 May 2017 22:21:44 +0200 Subject: [PATCH 14/35] HID: intel_ish-hid: enable compile testing To increase build coverage, drivers should generally be allowed to build on other architectures even if they are only used on one of them. Signed-off-by: Arnd Bergmann Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/intel-ish-hid/Kconfig b/drivers/hid/intel-ish-hid/Kconfig index ea065b3684a2..519e4c8b53c4 100644 --- a/drivers/hid/intel-ish-hid/Kconfig +++ b/drivers/hid/intel-ish-hid/Kconfig @@ -1,5 +1,5 @@ menu "Intel ISH HID support" - depends on X86_64 && PCI + depends on (X86_64 || COMPILE_TEST) && PCI config INTEL_ISH_HID tristate "Intel Integrated Sensor Hub" From 5be918035e44ae22854d7938c790d96d5154a5ae Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 5 Jun 2017 14:58:56 -0600 Subject: [PATCH 15/35] HID: move Asus keyboard support from hid-chicony to hid-asus The Asus AIO keyboard AK1D was added to hid-chicony based on its USB vendor ID, however images available online suggest that this keyboard is physically branded as ASUS with no mention of Chicony. A recent commit also added support for another Asus AIO keyboard into hid-chicony, this one with USB vendor ID Jess, and a pending review comment asked me to move it into hid-asus because it is also only physically branded as ASUS. I updated the USB ID defines to match the branding and product name, including noting that the recently added keyboard is labelled as ASUS MD-5112. Signed-off-by: Daniel Drake Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-asus.c | 29 +++++++++++++++++++++++++++++ drivers/hid/hid-chicony.c | 2 -- drivers/hid/hid-core.c | 4 ++-- drivers/hid/hid-ids.h | 4 ++-- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index a6268f2f7408..16a88da545b1 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -422,6 +422,33 @@ static int asus_input_mapping(struct hid_device *hdev, return 1; } + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR) { + set_bit(EV_REP, hi->input->evbit); + switch (usage->hid & HID_USAGE) { + case 0xff01: asus_map_key_clear(BTN_1); break; + case 0xff02: asus_map_key_clear(BTN_2); break; + case 0xff03: asus_map_key_clear(BTN_3); break; + case 0xff04: asus_map_key_clear(BTN_4); break; + case 0xff05: asus_map_key_clear(BTN_5); break; + case 0xff06: asus_map_key_clear(BTN_6); break; + case 0xff07: asus_map_key_clear(BTN_7); break; + case 0xff08: asus_map_key_clear(BTN_8); break; + case 0xff09: asus_map_key_clear(BTN_9); break; + case 0xff0a: asus_map_key_clear(BTN_A); break; + case 0xff0b: asus_map_key_clear(BTN_B); break; + case 0x00f1: asus_map_key_clear(KEY_WLAN); break; + case 0x00f2: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break; + case 0x00f3: asus_map_key_clear(KEY_BRIGHTNESSUP); break; + case 0x00f4: asus_map_key_clear(KEY_DISPLAY_OFF); break; + case 0x00f7: asus_map_key_clear(KEY_CAMERA); break; + case 0x00f8: asus_map_key_clear(KEY_PROG1); break; + default: + return 0; + } + + return 1; + } + if (drvdata->quirks & QUIRK_NO_CONSUMER_USAGES && (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) { switch (usage->hid & HID_USAGE) { @@ -572,6 +599,8 @@ static const struct hid_device_id asus_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T100_KEYBOARD), QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES }, + { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_ASUS_AK1D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) }, { } }; MODULE_DEVICE_TABLE(hid, asus_devices); diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c index f04ed9aabc3f..397a789a41be 100644 --- a/drivers/hid/hid-chicony.c +++ b/drivers/hid/hid-chicony.c @@ -84,9 +84,7 @@ static __u8 *ch_switch12_report_fixup(struct hid_device *hdev, __u8 *rdesc, static const struct hid_device_id ch_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) }, - { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_JESS_ZEN_AIO_KBD) }, { } }; MODULE_DEVICE_TABLE(hid, ch_devices); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 04cee65531d7..63bcd4f2ef89 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1869,7 +1869,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_ASUS_AK1D) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) }, { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90) }, { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB) }, @@ -1916,7 +1916,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2) }, { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_JESS_ZEN_AIO_KBD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 8ca1e8ce0af2..9fb49c6b8dcc 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -252,7 +252,7 @@ #define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618 #define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE 0x1053 #define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123 -#define USB_DEVICE_ID_CHICONY_AK1D 0x1125 +#define USB_DEVICE_ID_ASUS_AK1D 0x1125 #define USB_DEVICE_ID_CHICONY_ACER_SWITCH12 0x1421 #define USB_VENDOR_ID_CHUNGHWAT 0x2247 @@ -570,7 +570,7 @@ #define USB_VENDOR_ID_JESS 0x0c45 #define USB_DEVICE_ID_JESS_YUREX 0x1010 -#define USB_DEVICE_ID_JESS_ZEN_AIO_KBD 0x5112 +#define USB_DEVICE_ID_ASUS_MD_5112 0x5112 #define USB_VENDOR_ID_JESS2 0x0f30 #define USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD 0x0111 From 38b2d78c557ebb47e65de5ff1415144c65c4cf7c Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 5 Jun 2017 14:58:57 -0600 Subject: [PATCH 16/35] HID: asus: Add support for Zen AiO MD-5110 keyboard Add support for media keys on the MD-5110 wireless keyboard that comes with the Asus V221ID and ZN241IC All In One computers. The keys to support here are WLAN, BRIGHTNESSDOWN and BRIGHTNESSUP. The USB Vendor ID suggests that it is a TURBOX device, but the physical branding only mentions ASUS MD-5110. Signed-off-by: Daniel Drake Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-asus.c | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 16a88da545b1..a4a3c38bc145 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -600,6 +600,7 @@ static const struct hid_device_id asus_devices[] = { USB_DEVICE_ID_ASUSTEK_T100_KEYBOARD), QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_ASUS_AK1D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) }, { } }; diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 63bcd4f2ef89..5d17a57c269f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2089,6 +2089,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) }, { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 9fb49c6b8dcc..214157b4fb5f 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1021,6 +1021,7 @@ #define USB_VENDOR_ID_TURBOX 0x062a #define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 +#define USB_DEVICE_ID_ASUS_MD_5110 0x5110 #define USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART 0x7100 #define USB_VENDOR_ID_TWINHAN 0x6253 From d36b7d4c271b2f93127e7e7cc007b5768a296594 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:31 -0700 Subject: [PATCH 17/35] HID: hiddev: use hid_hw_open/close instead of usbhid_open/close Instead of calling into usbhid code directly, let's use the standard accessors for the transport HID drivers, and stop clobbering their errors with -EIO. This also allows us make usbhid_open and close static. Signed-off-by: Dmitry Torokhov Reviewed-by: Andy Shevchenko Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 4 ++-- drivers/hid/usbhid/hiddev.c | 16 +++++++++------- drivers/hid/usbhid/usbhid.h | 2 -- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 83772fa7d92a..fb0cf5d70504 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -677,7 +677,7 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, return result; } -int usbhid_open(struct hid_device *hid) +static int usbhid_open(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; int res = 0; @@ -722,7 +722,7 @@ done: return res; } -void usbhid_close(struct hid_device *hid) +static void usbhid_close(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 0e06368d1fbb..b4f714752245 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -237,7 +237,7 @@ static int hiddev_release(struct inode * inode, struct file * file) mutex_lock(&list->hiddev->existancelock); if (!--list->hiddev->open) { if (list->hiddev->exist) { - usbhid_close(list->hiddev->hid); + hid_hw_close(list->hiddev->hid); usbhid_put_power(list->hiddev->hid); } else { mutex_unlock(&list->hiddev->existancelock); @@ -282,11 +282,9 @@ static int hiddev_open(struct inode *inode, struct file *file) */ if (list->hiddev->exist) { if (!list->hiddev->open++) { - res = usbhid_open(hiddev->hid); - if (res < 0) { - res = -EIO; + res = hid_hw_open(hiddev->hid); + if (res < 0) goto bail; - } } } else { res = -ENODEV; @@ -306,10 +304,14 @@ static int hiddev_open(struct inode *inode, struct file *file) res = -EIO; goto bail_unlock; } - usbhid_open(hid); + res = hid_hw_open(hid); + if (res < 0) + goto bail_put_power; } mutex_unlock(&hiddev->existancelock); return 0; +bail_put_power: + usbhid_put_power(hid); bail_unlock: mutex_unlock(&hiddev->existancelock); bail: @@ -935,7 +937,7 @@ void hiddev_disconnect(struct hid_device *hid) if (hiddev->open) { mutex_unlock(&hiddev->existancelock); - usbhid_close(hiddev->hid); + hid_hw_close(hiddev->hid); wake_up_interruptible(&hiddev->wait); } else { mutex_unlock(&hiddev->existancelock); diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index fa47d666cfcf..83ef5c14aa92 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -34,8 +34,6 @@ #include /* API provided by hid-core.c for USB HID drivers */ -void usbhid_close(struct hid_device *hid); -int usbhid_open(struct hid_device *hid); void usbhid_init_reports(struct hid_device *hid); int usbhid_get_power(struct hid_device *hid); void usbhid_put_power(struct hid_device *hid); From 9a83563fb3f926cbf0d5992d5c70d760c445ba09 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:32 -0700 Subject: [PATCH 18/35] HID: hiddev: use hid_hw_power instead of usbhid_get/put_power Instead of calling into usbhid code directly, let's use the standard accessors for the transport HID drivers, and stop clobbering their error codes with -EIO. This also allows us to remove usbhid_get/put_power(), leaving only usbhid_power(). Signed-off-by: Dmitry Torokhov Reviewed-by: Andy Shevchenko Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 22 +++++----------------- drivers/hid/usbhid/hiddev.c | 14 ++++++-------- drivers/hid/usbhid/usbhid.h | 2 -- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index fb0cf5d70504..62b660622265 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1203,16 +1203,19 @@ static void usbhid_stop(struct hid_device *hid) static int usbhid_power(struct hid_device *hid, int lvl) { + struct usbhid_device *usbhid = hid->driver_data; int r = 0; switch (lvl) { case PM_HINT_FULLON: - r = usbhid_get_power(hid); + r = usb_autopm_get_interface(usbhid->intf); break; + case PM_HINT_NORMAL: - usbhid_put_power(hid); + usb_autopm_put_interface(usbhid->intf); break; } + return r; } @@ -1492,21 +1495,6 @@ static int hid_post_reset(struct usb_interface *intf) return 0; } -int usbhid_get_power(struct hid_device *hid) -{ - struct usbhid_device *usbhid = hid->driver_data; - - return usb_autopm_get_interface(usbhid->intf); -} - -void usbhid_put_power(struct hid_device *hid) -{ - struct usbhid_device *usbhid = hid->driver_data; - - usb_autopm_put_interface(usbhid->intf); -} - - #ifdef CONFIG_PM static int hid_resume_common(struct hid_device *hid, bool driver_suspended) { diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index b4f714752245..7d749b19c27c 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -238,7 +238,7 @@ static int hiddev_release(struct inode * inode, struct file * file) if (!--list->hiddev->open) { if (list->hiddev->exist) { hid_hw_close(list->hiddev->hid); - usbhid_put_power(list->hiddev->hid); + hid_hw_power(list->hiddev->hid, PM_HINT_NORMAL); } else { mutex_unlock(&list->hiddev->existancelock); kfree(list->hiddev); @@ -299,19 +299,17 @@ static int hiddev_open(struct inode *inode, struct file *file) if (!list->hiddev->open++) if (list->hiddev->exist) { struct hid_device *hid = hiddev->hid; - res = usbhid_get_power(hid); - if (res < 0) { - res = -EIO; + res = hid_hw_power(hid, PM_HINT_FULLON); + if (res < 0) goto bail_unlock; - } res = hid_hw_open(hid); if (res < 0) - goto bail_put_power; + goto bail_normal_power; } mutex_unlock(&hiddev->existancelock); return 0; -bail_put_power: - usbhid_put_power(hid); +bail_normal_power: + hid_hw_power(hid, PM_HINT_NORMAL); bail_unlock: mutex_unlock(&hiddev->existancelock); bail: diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 83ef5c14aa92..ffcd329b3c3b 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -35,8 +35,6 @@ /* API provided by hid-core.c for USB HID drivers */ void usbhid_init_reports(struct hid_device *hid); -int usbhid_get_power(struct hid_device *hid); -void usbhid_put_power(struct hid_device *hid); struct usb_interface *usbhid_find_interface(int minor); /* iofl flags */ From 28cbc863f4bfa92c26143493f0463e4eb96a1783 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:33 -0700 Subject: [PATCH 19/35] HID: usbhid: do not rely on hid->open when deciding to do IO Instead of checking hid->open (that we plan on having HID core manage) in hid_start_in(), let's allocate a couple of new flags: HID_IN_POLLING and HID_OPENED, and use them to decide whether we should be submitting URBs or not. Signed-off-by: Dmitry Torokhov Reviewed-by: Andy Shevchenko Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 25 ++++++++++++++++++------- drivers/hid/usbhid/usbhid.h | 11 +++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 62b660622265..d927fe4ba592 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -85,10 +85,10 @@ static int hid_start_in(struct hid_device *hid) struct usbhid_device *usbhid = hid->driver_data; spin_lock_irqsave(&usbhid->lock, flags); - if ((hid->open > 0 || hid->quirks & HID_QUIRK_ALWAYS_POLL) && - !test_bit(HID_DISCONNECTED, &usbhid->iofl) && - !test_bit(HID_SUSPENDED, &usbhid->iofl) && - !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { + if (test_bit(HID_IN_POLLING, &usbhid->iofl) && + !test_bit(HID_DISCONNECTED, &usbhid->iofl) && + !test_bit(HID_SUSPENDED, &usbhid->iofl) && + !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC); if (rc != 0) { clear_bit(HID_IN_RUNNING, &usbhid->iofl); @@ -272,13 +272,13 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid) static void hid_irq_in(struct urb *urb) { struct hid_device *hid = urb->context; - struct usbhid_device *usbhid = hid->driver_data; + struct usbhid_device *usbhid = hid->driver_data; int status; switch (urb->status) { case 0: /* success */ usbhid->retry_delay = 0; - if ((hid->quirks & HID_QUIRK_ALWAYS_POLL) && !hid->open) + if (!test_bit(HID_OPENED, &usbhid->iofl)) break; usbhid_mark_busy(usbhid); if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) { @@ -692,6 +692,8 @@ static int usbhid_open(struct hid_device *hid) goto done; } usbhid->intf->needs_remote_wakeup = 1; + set_bit(HID_OPENED, &usbhid->iofl); + set_bit(HID_IN_POLLING, &usbhid->iofl); set_bit(HID_RESUME_RUNNING, &usbhid->iofl); res = hid_start_in(hid); if (res) { @@ -701,6 +703,9 @@ static int usbhid_open(struct hid_device *hid) } else { /* no use opening if resources are insufficient */ hid->open--; + clear_bit(HID_OPENED, &usbhid->iofl); + if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) + clear_bit(HID_IN_POLLING, &usbhid->iofl); res = -EBUSY; usbhid->intf->needs_remote_wakeup = 0; } @@ -734,6 +739,9 @@ static void usbhid_close(struct hid_device *hid) */ spin_lock_irq(&usbhid->lock); if (!--hid->open) { + if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) + clear_bit(HID_IN_POLLING, &usbhid->iofl); + clear_bit(HID_OPENED, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); hid_cancel_delayed_stuff(usbhid); if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) { @@ -1135,6 +1143,7 @@ static int usbhid_start(struct hid_device *hid) ret = usb_autopm_get_interface(usbhid->intf); if (ret) goto fail; + set_bit(HID_IN_POLLING, &usbhid->iofl); usbhid->intf->needs_remote_wakeup = 1; ret = hid_start_in(hid); if (ret) { @@ -1176,8 +1185,10 @@ static void usbhid_stop(struct hid_device *hid) if (WARN_ON(!usbhid)) return; - if (hid->quirks & HID_QUIRK_ALWAYS_POLL) + if (hid->quirks & HID_QUIRK_ALWAYS_POLL) { + clear_bit(HID_IN_POLLING, &usbhid->iofl); usbhid->intf->needs_remote_wakeup = 0; + } clear_bit(HID_STARTED, &usbhid->iofl); spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */ diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index ffcd329b3c3b..da9c61d54be6 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -49,6 +49,17 @@ struct usb_interface *usbhid_find_interface(int minor); #define HID_KEYS_PRESSED 10 #define HID_NO_BANDWIDTH 11 #define HID_RESUME_RUNNING 12 +/* + * The device is opened, meaning there is a client that is interested + * in data coming from the device. + */ +#define HID_OPENED 13 +/* + * We are polling input endpoint by [re]submitting IN URB, because + * either HID device is opened or ALWAYS POLL quirk is set for the + * device. + */ +#define HID_IN_POLLING 14 /* * USB-specific HID struct, to be pointed to From aaac082dac0a8ac6b00509c7ae2fa8280f966652 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:34 -0700 Subject: [PATCH 20/35] HID: serialize hid_hw_open and hid_hw_close The HID transport drivers either re-implement exactly the same logic (usbhid, i2c-hid) or forget to implement it (usbhid) which causes issues when the same device is accessed via multiple interfaces (for example input device through evdev and also hidraw). Let's muve the locking logic into HID core to make sure the serialized behavior is always enforced. Also let's uninline and move hid_hw_start() and hid_hw_stop() into hid-core as hid_hw_start() is somewhat large and do not believe we get any benefit from these two being inline. Signed-off-by: Dmitry Torokhov Reviewed-by: Andy Shevchenko Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 89 ++++++++++++++++++++++++++++++++++++++++++ include/linux/hid.h | 72 +++++----------------------------- 2 files changed, 98 insertions(+), 63 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 04cee65531d7..f93dd6f48a79 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1750,6 +1750,94 @@ void hid_disconnect(struct hid_device *hdev) } EXPORT_SYMBOL_GPL(hid_disconnect); +/** + * hid_hw_start - start underlying HW + * @hdev: hid device + * @connect_mask: which outputs to connect, see HID_CONNECT_* + * + * Call this in probe function *after* hid_parse. This will setup HW + * buffers and start the device (if not defeirred to device open). + * hid_hw_stop must be called if this was successful. + */ +int hid_hw_start(struct hid_device *hdev, unsigned int connect_mask) +{ + int error; + + error = hdev->ll_driver->start(hdev); + if (error) + return error; + + if (connect_mask) { + error = hid_connect(hdev, connect_mask); + if (error) { + hdev->ll_driver->stop(hdev); + return error; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(hid_hw_start); + +/** + * hid_hw_stop - stop underlying HW + * @hdev: hid device + * + * This is usually called from remove function or from probe when something + * failed and hid_hw_start was called already. + */ +void hid_hw_stop(struct hid_device *hdev) +{ + hid_disconnect(hdev); + hdev->ll_driver->stop(hdev); +} +EXPORT_SYMBOL_GPL(hid_hw_stop); + +/** + * hid_hw_open - signal underlying HW to start delivering events + * @hdev: hid device + * + * Tell underlying HW to start delivering events from the device. + * This function should be called sometime after successful call + * to hid_hiw_start(). + */ +int hid_hw_open(struct hid_device *hdev) +{ + int ret; + + ret = mutex_lock_killable(&hdev->ll_open_lock); + if (ret) + return ret; + + if (!hdev->ll_open_count++) { + ret = hdev->ll_driver->open(hdev); + if (ret) + hdev->ll_open_count--; + } + + mutex_unlock(&hdev->ll_open_lock); + return ret; +} +EXPORT_SYMBOL_GPL(hid_hw_open); + +/** + * hid_hw_close - signal underlaying HW to stop delivering events + * + * @hdev: hid device + * + * This function indicates that we are not interested in the events + * from this device anymore. Delivery of events may or may not stop, + * depending on the number of users still outstanding. + */ +void hid_hw_close(struct hid_device *hdev) +{ + mutex_lock(&hdev->ll_open_lock); + if (!--hdev->ll_open_count) + hdev->ll_driver->close(hdev); + mutex_unlock(&hdev->ll_open_lock); +} +EXPORT_SYMBOL_GPL(hid_hw_close); + /* * A list of devices for which there is a specialized driver on HID bus. * @@ -2747,6 +2835,7 @@ struct hid_device *hid_allocate_device(void) spin_lock_init(&hdev->debug_list_lock); sema_init(&hdev->driver_lock, 1); sema_init(&hdev->driver_input_lock, 1); + mutex_init(&hdev->ll_open_lock); return hdev; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 5be325d890d9..5501eb64dbc4 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -520,7 +521,10 @@ struct hid_device { /* device report descriptor */ struct semaphore driver_input_lock; /* protects the current driver */ struct device dev; /* device */ struct hid_driver *driver; + struct hid_ll_driver *ll_driver; + struct mutex ll_open_lock; + unsigned int ll_open_count; #ifdef CONFIG_HID_BATTERY_STRENGTH /* @@ -937,69 +941,11 @@ static inline int __must_check hid_parse(struct hid_device *hdev) return hid_open_report(hdev); } -/** - * hid_hw_start - start underlaying HW - * - * @hdev: hid device - * @connect_mask: which outputs to connect, see HID_CONNECT_* - * - * Call this in probe function *after* hid_parse. This will setup HW buffers - * and start the device (if not deffered to device open). hid_hw_stop must be - * called if this was successful. - */ -static inline int __must_check hid_hw_start(struct hid_device *hdev, - unsigned int connect_mask) -{ - int ret = hdev->ll_driver->start(hdev); - if (ret || !connect_mask) - return ret; - ret = hid_connect(hdev, connect_mask); - if (ret) - hdev->ll_driver->stop(hdev); - return ret; -} - -/** - * hid_hw_stop - stop underlaying HW - * - * @hdev: hid device - * - * This is usually called from remove function or from probe when something - * failed and hid_hw_start was called already. - */ -static inline void hid_hw_stop(struct hid_device *hdev) -{ - hid_disconnect(hdev); - hdev->ll_driver->stop(hdev); -} - -/** - * hid_hw_open - signal underlaying HW to start delivering events - * - * @hdev: hid device - * - * Tell underlying HW to start delivering events from the device. - * This function should be called sometime after successful call - * to hid_hiw_start(). - */ -static inline int __must_check hid_hw_open(struct hid_device *hdev) -{ - return hdev->ll_driver->open(hdev); -} - -/** - * hid_hw_close - signal underlaying HW to stop delivering events - * - * @hdev: hid device - * - * This function indicates that we are not interested in the events - * from this device anymore. Delivery of events may or may not stop, - * depending on the number of users still outstanding. - */ -static inline void hid_hw_close(struct hid_device *hdev) -{ - hdev->ll_driver->close(hdev); -} +int __must_check hid_hw_start(struct hid_device *hdev, + unsigned int connect_mask); +void hid_hw_stop(struct hid_device *hdev); +int __must_check hid_hw_open(struct hid_device *hdev); +void hid_hw_close(struct hid_device *hdev); /** * hid_hw_power - requests underlying HW to go into given power mode From 85ae91133152c8c5e214303b8e26cfcbb91dfeb9 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:35 -0700 Subject: [PATCH 21/35] HID: i2c-hid: remove custom locking from i2c_hid_open/close Now that HID core enforces serialization of transport driver open/close calls we can remove custom locking from i2c-hid driver. Signed-off-by: Dmitry Torokhov Reviewed-by: Andy Shevchenko Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index fb55fb4c39fc..6355015ed249 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -743,18 +743,12 @@ static int i2c_hid_open(struct hid_device *hid) struct i2c_hid *ihid = i2c_get_clientdata(client); int ret = 0; - mutex_lock(&i2c_hid_open_mut); - if (!hid->open++) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - hid->open--; - goto done; - } - set_bit(I2C_HID_STARTED, &ihid->flags); - } -done: - mutex_unlock(&i2c_hid_open_mut); - return ret < 0 ? ret : 0; + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) + return ret; + + set_bit(I2C_HID_STARTED, &ihid->flags); + return 0; } static void i2c_hid_close(struct hid_device *hid) @@ -762,18 +756,10 @@ static void i2c_hid_close(struct hid_device *hid) struct i2c_client *client = hid->driver_data; struct i2c_hid *ihid = i2c_get_clientdata(client); - /* protecting hid->open to make sure we don't restart - * data acquistion due to a resumption we no longer - * care about - */ - mutex_lock(&i2c_hid_open_mut); - if (!--hid->open) { - clear_bit(I2C_HID_STARTED, &ihid->flags); + clear_bit(I2C_HID_STARTED, &ihid->flags); - /* Save some power */ - pm_runtime_put(&client->dev); - } - mutex_unlock(&i2c_hid_open_mut); + /* Save some power */ + pm_runtime_put(&client->dev); } static int i2c_hid_power(struct hid_device *hid, int lvl) From e399396a6b061ba9e68e64e2867cc3a0f26f0ace Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:36 -0700 Subject: [PATCH 22/35] HID: usbhid: remove custom locking from usbhid_open/close Now that HID core enforces serialization of transport driver open/close calls we can remove custom locking from usbhid driver. Signed-off-by: Dmitry Torokhov Reviewed-by: Andy Shevchenko Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 113 ++++++++++++++++------------------ 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index d927fe4ba592..76013eb5cb7f 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -70,8 +70,6 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying " /* * Input submission and I/O error handler. */ -static DEFINE_MUTEX(hid_open_mut); - static void hid_io_error(struct hid_device *hid); static int hid_submit_out(struct hid_device *hid); static int hid_submit_ctrl(struct hid_device *hid); @@ -680,50 +678,48 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, static int usbhid_open(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; - int res = 0; + int res; - mutex_lock(&hid_open_mut); - if (!hid->open++) { - res = usb_autopm_get_interface(usbhid->intf); - /* the device must be awake to reliably request remote wakeup */ - if (res < 0) { - hid->open--; - res = -EIO; - goto done; - } - usbhid->intf->needs_remote_wakeup = 1; - set_bit(HID_OPENED, &usbhid->iofl); - set_bit(HID_IN_POLLING, &usbhid->iofl); - set_bit(HID_RESUME_RUNNING, &usbhid->iofl); - res = hid_start_in(hid); - if (res) { - if (res != -ENOSPC) { - hid_io_error(hid); - res = 0; - } else { - /* no use opening if resources are insufficient */ - hid->open--; - clear_bit(HID_OPENED, &usbhid->iofl); - if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) - clear_bit(HID_IN_POLLING, &usbhid->iofl); - res = -EBUSY; - usbhid->intf->needs_remote_wakeup = 0; - } - } - usb_autopm_put_interface(usbhid->intf); + if (hid->quirks & HID_QUIRK_ALWAYS_POLL) + return 0; - /* - * In case events are generated while nobody was listening, - * some are released when the device is re-opened. - * Wait 50 msec for the queue to empty before allowing events - * to go through hid. - */ - if (res == 0 && !(hid->quirks & HID_QUIRK_ALWAYS_POLL)) - msleep(50); - clear_bit(HID_RESUME_RUNNING, &usbhid->iofl); + res = usb_autopm_get_interface(usbhid->intf); + /* the device must be awake to reliably request remote wakeup */ + if (res < 0) + return -EIO; + + usbhid->intf->needs_remote_wakeup = 1; + + set_bit(HID_RESUME_RUNNING, &usbhid->iofl); + set_bit(HID_OPENED, &usbhid->iofl); + set_bit(HID_IN_POLLING, &usbhid->iofl); + + res = hid_start_in(hid); + if (res) { + if (res != -ENOSPC) { + hid_io_error(hid); + res = 0; + } else { + /* no use opening if resources are insufficient */ + res = -EBUSY; + clear_bit(HID_OPENED, &usbhid->iofl); + clear_bit(HID_IN_POLLING, &usbhid->iofl); + usbhid->intf->needs_remote_wakeup = 0; + } } -done: - mutex_unlock(&hid_open_mut); + + usb_autopm_put_interface(usbhid->intf); + + /* + * In case events are generated while nobody was listening, + * some are released when the device is re-opened. + * Wait 50 msec for the queue to empty before allowing events + * to go through hid. + */ + if (res == 0) + msleep(50); + + clear_bit(HID_RESUME_RUNNING, &usbhid->iofl); return res; } @@ -731,27 +727,22 @@ static void usbhid_close(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; - mutex_lock(&hid_open_mut); + if (hid->quirks & HID_QUIRK_ALWAYS_POLL) + return; - /* protecting hid->open to make sure we don't restart - * data acquistion due to a resumption we no longer - * care about + /* + * Make sure we don't restart data acquisition due to + * a resumption we no longer care about by avoiding racing + * with hid_start_in(). */ spin_lock_irq(&usbhid->lock); - if (!--hid->open) { - if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) - clear_bit(HID_IN_POLLING, &usbhid->iofl); - clear_bit(HID_OPENED, &usbhid->iofl); - spin_unlock_irq(&usbhid->lock); - hid_cancel_delayed_stuff(usbhid); - if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) { - usb_kill_urb(usbhid->urbin); - usbhid->intf->needs_remote_wakeup = 0; - } - } else { - spin_unlock_irq(&usbhid->lock); - } - mutex_unlock(&hid_open_mut); + clear_bit(HID_IN_POLLING, &usbhid->iofl); + clear_bit(HID_OPENED, &usbhid->iofl); + spin_unlock_irq(&usbhid->lock); + + hid_cancel_delayed_stuff(usbhid); + usb_kill_urb(usbhid->urbin); + usbhid->intf->needs_remote_wakeup = 0; } /* From d9d2401f59355264a435c723aadee5b84b75881b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:37 -0700 Subject: [PATCH 23/35] greybus: hid: remove custom locking from gb_hid_open/close Now that HID core enforces serialization of transport driver open/close calls we can remove custom locking from greybus hid driver. Signed-off-by: Dmitry Torokhov Acked-by: Greg Kroah-Hartman Acked-by: Viresh Kumar Reviewed-by: Andy Shevchenko Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/staging/greybus/hid.c | 39 +++++++++++------------------------ 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 730d746fc4c2..465101bbab69 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -32,8 +32,6 @@ struct gb_hid { char *inbuf; }; -static DEFINE_MUTEX(gb_hid_open_mutex); - /* Routines to get controller's information over greybus */ /* Operations performed on greybus */ @@ -346,19 +344,14 @@ static void gb_hid_stop(struct hid_device *hid) static int gb_hid_open(struct hid_device *hid) { struct gb_hid *ghid = hid->driver_data; - int ret = 0; + int ret; - mutex_lock(&gb_hid_open_mutex); - if (!hid->open++) { - ret = gb_hid_set_power(ghid, GB_HID_TYPE_PWR_ON); - if (ret < 0) - hid->open--; - else - set_bit(GB_HID_STARTED, &ghid->flags); - } - mutex_unlock(&gb_hid_open_mutex); + ret = gb_hid_set_power(ghid, GB_HID_TYPE_PWR_ON); + if (ret < 0) + return ret; - return ret; + set_bit(GB_HID_STARTED, &ghid->flags); + return 0; } static void gb_hid_close(struct hid_device *hid) @@ -366,21 +359,13 @@ static void gb_hid_close(struct hid_device *hid) struct gb_hid *ghid = hid->driver_data; int ret; - /* - * Protecting hid->open to make sure we don't restart data acquistion - * due to a resumption we no longer care about.. - */ - mutex_lock(&gb_hid_open_mutex); - if (!--hid->open) { - clear_bit(GB_HID_STARTED, &ghid->flags); + clear_bit(GB_HID_STARTED, &ghid->flags); - /* Save some power */ - ret = gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF); - if (ret) - dev_err(&ghid->connection->bundle->dev, - "failed to power off (%d)\n", ret); - } - mutex_unlock(&gb_hid_open_mutex); + /* Save some power */ + ret = gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF); + if (ret) + dev_err(&ghid->connection->bundle->dev, + "failed to power off (%d)\n", ret); } static int gb_hid_power(struct hid_device *hid, int lvl) From 283a21da1239d8db7fdf6d9077feed73a6efffa2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:38 -0700 Subject: [PATCH 24/35] HID: remove no longer used hid->open field Now that all users have migrated to use hid->ll_open_count, we can remove hid->open field. Signed-off-by: Dmitry Torokhov Reviewed-by: Andy Shevchenko Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- include/linux/hid.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/hid.h b/include/linux/hid.h index 5501eb64dbc4..72e8ac667771 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -548,7 +548,6 @@ struct hid_device { /* device report descriptor */ void *hiddev; /* The hiddev structure */ void *hidraw; - int open; /* is the device open by anyone? */ char name[128]; /* Device name */ char phys[64]; /* Device physical location */ char uniq[64]; /* Device unique identifier (serial #) */ From bd77a0f08ec57f7b805dfbaa64b36329dfa005d6 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Fri, 2 Jun 2017 09:28:39 -0600 Subject: [PATCH 25/35] HID: apple: Use country code to detect ISO keyboards At least on newer laptops, Apple uses the same USB ID for both ISO and ANSI keyboards. However, they have been good about filling in the bCountryCode field in the HID descriptor on all of their keyboards. A value of 13 indicates an ISO layout and other values indicate various country-specific ANSI layouts. With this patch, users of Apple US keyboards will no longer have to run `echo 0 > /sys/module/hid_apple/parameters/iso_layout` to get a working tilde key. Please test this patch and send feedback if you have a Macbook or an Apple keyboard. Signed-off-by: Alex Henrie Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 59 +++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 2e046082210f..25b7bd56ae11 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -28,7 +28,7 @@ #define APPLE_IGNORE_MOUSE 0x0002 #define APPLE_HAS_FN 0x0004 #define APPLE_HIDDEV 0x0008 -#define APPLE_ISO_KEYBOARD 0x0010 +/* 0x0010 reserved, was: APPLE_ISO_KEYBOARD */ #define APPLE_MIGHTYMOUSE 0x0020 #define APPLE_INVERT_HWHEEL 0x0040 #define APPLE_IGNORE_HIDINPUT 0x0080 @@ -36,6 +36,8 @@ #define APPLE_FLAG_FKEY 0x01 +#define HID_COUNTRY_INTERNATIONAL_ISO 13 + static unsigned int fnmode = 1; module_param(fnmode, uint, 0644); MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, " @@ -247,7 +249,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, } if (iso_layout) { - if (asc->quirks & APPLE_ISO_KEYBOARD) { + if (hid->country == HID_COUNTRY_INTERNATIONAL_ISO) { trans = apple_find_translation(apple_iso_keyboard, usage->code); if (trans) { input_event(input, usage->type, trans->to, value); @@ -412,60 +414,54 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO), - .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | - APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO), - .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | - APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO), - .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | - APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO), - .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | - APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_JIS), .driver_data = APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO), - .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | - APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO), - .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | - APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, @@ -479,86 +475,85 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ISO), - .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), - .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | - APPLE_ISO_KEYBOARD }, + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY), From 08585e43d22802666a466af1ca5795085e74d60d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 13 Jun 2017 12:22:22 +0300 Subject: [PATCH 26/35] HID: core: don't use negative operands when shift The recent C standard in 6.5.7 paragraph 4 defines that operands for bitwise shift operators should be non-negative, otherwise it's an undefined behaviour. Signed-off-by: Andy Shevchenko Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 37084b645785..8017de4e5c11 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1046,7 +1046,7 @@ static s32 snto32(__u32 value, unsigned n) case 16: return ((__s16)value); case 32: return ((__s32)value); } - return value & (1 << (n - 1)) ? value | (-1 << n) : value; + return value & (1 << (n - 1)) ? value | (~0U << n) : value; } s32 hid_snto32(__u32 value, unsigned n) From 504c932c7c47a6b4c572302a13873f7d83af1ff3 Mon Sep 17 00:00:00 2001 From: Masaki Ota Date: Fri, 16 Jun 2017 09:29:39 +0900 Subject: [PATCH 27/35] HID: multitouch: Support PTP Stick and Touchpad device Support PTP Stick and Touchpad device. This Touchpad is Precision Touchpad (PTP), and Stick Pointer data is the same as Mouse; Stick Pointer works as Mouse. [jkosina@suse.cz: changelog deuglification] Signed-off-by: Masaki Ota Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 2 ++ drivers/hid/hid-multitouch.c | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 8ca1e8ce0af2..d36d4ac508f6 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -75,6 +75,8 @@ #define USB_VENDOR_ID_ALPS_JP 0x044E #define HID_DEVICE_ID_ALPS_U1_DUAL 0x120B +#define HID_DEVICE_ID_ALPS_U1_DUAL_PTP 0x121F +#define HID_DEVICE_ID_ALPS_U1_DUAL_3BTN_PTP 0x1220 #define USB_VENDOR_ID_AMI 0x046b #define USB_DEVICE_ID_AMI_VIRT_KEYBOARD_AND_MOUSE 0xff10 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 24d5b6deb571..ba9d9020f958 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -148,6 +148,7 @@ static void mt_post_parse(struct mt_device *td); /* reserved 0x0011 */ #define MT_CLS_WIN_8 0x0012 #define MT_CLS_EXPORT_ALL_INPUTS 0x0013 +#define MT_CLS_WIN_8_DUAL 0x0014 /* vendor specific classes */ #define MT_CLS_3M 0x0101 @@ -217,6 +218,12 @@ static struct mt_class mt_classes[] = { .quirks = MT_QUIRK_ALWAYS_VALID | MT_QUIRK_CONTACT_CNT_ACCURATE, .export_all_inputs = true }, + { .name = MT_CLS_WIN_8_DUAL, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_IGNORE_DUPLICATES | + MT_QUIRK_HOVERING | + MT_QUIRK_CONTACT_CNT_ACCURATE, + .export_all_inputs = true }, /* * vendor specific classes @@ -512,7 +519,8 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, mt_store_field(usage, td, hi); return 1; case HID_DG_CONFIDENCE: - if (cls->name == MT_CLS_WIN_8 && + if ((cls->name == MT_CLS_WIN_8 || + cls->name == MT_CLS_WIN_8_DUAL) && field->application == HID_DG_TOUCHPAD) cls->quirks |= MT_QUIRK_CONFIDENCE; mt_store_field(usage, td, hi); @@ -579,7 +587,8 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, * MS PTP spec says that external buttons left and right have * usages 2 and 3. */ - if (cls->name == MT_CLS_WIN_8 && + if ((cls->name == MT_CLS_WIN_8 || + cls->name == MT_CLS_WIN_8_DUAL) && field->application == HID_DG_TOUCHPAD && (usage->hid & HID_USAGE) > 1) code--; @@ -1290,6 +1299,16 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M3266) }, + /* Alps devices */ + { .driver_data = MT_CLS_WIN_8_DUAL, + HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, + USB_VENDOR_ID_ALPS_JP, + HID_DEVICE_ID_ALPS_U1_DUAL_PTP) }, + { .driver_data = MT_CLS_WIN_8_DUAL, + HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, + USB_VENDOR_ID_ALPS_JP, + HID_DEVICE_ID_ALPS_U1_DUAL_3BTN_PTP) }, + /* Anton devices */ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, MT_USB_DEVICE(USB_VENDOR_ID_ANTON, From 0e82232c420e9bd40813000e82233e4b206d38d1 Mon Sep 17 00:00:00 2001 From: Wei-Ning Huang Date: Wed, 21 Jun 2017 10:43:25 +0800 Subject: [PATCH 28/35] HID: multitouch: Add support for Google Rose Touchpad Add Google Rose Touchpad USB PID and required quirks. Signed-off-by: Wei-Ning Huang Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 3 +++ drivers/hid/hid-multitouch.c | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index d36d4ac508f6..d55796934d63 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -427,6 +427,9 @@ #define USB_VENDOR_ID_GOODTOUCH 0x1aad #define USB_DEVICE_ID_GOODTOUCH_000f 0x000f +#define USB_VENDOR_ID_GOOGLE 0x18d1 +#define USB_DEVICE_ID_GOOGLE_TOUCH_ROSE 0x5028 + #define USB_VENDOR_ID_GOTOP 0x08f2 #define USB_DEVICE_ID_SUPER_Q2 0x007f #define USB_DEVICE_ID_GOGOPEN 0x00ce diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index ba9d9020f958..f07256ee2d26 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -162,6 +162,7 @@ static void mt_post_parse(struct mt_device *td); #define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109 #define MT_CLS_LG 0x010a #define MT_CLS_VTL 0x0110 +#define MT_CLS_GOOGLE 0x0111 #define MT_DEFAULT_MAXCONTACT 10 #define MT_MAX_MAXCONTACT 250 @@ -285,6 +286,12 @@ static struct mt_class mt_classes[] = { MT_QUIRK_CONTACT_CNT_ACCURATE | MT_QUIRK_FORCE_GET_FEATURE, }, + { .name = MT_CLS_GOOGLE, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_SLOT_IS_CONTACTID | + MT_QUIRK_HOVERING + }, { } }; @@ -1588,6 +1595,11 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR2) }, + /* Google MT devices */ + { .driver_data = MT_CLS_GOOGLE, + HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE, + USB_DEVICE_ID_GOOGLE_TOUCH_ROSE) }, + /* Generic MT device */ { HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, From 13b2e1ba486f07ccd26e01c563b52a9171b2c255 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Tue, 20 Jun 2017 18:13:37 +0200 Subject: [PATCH 29/35] HID: Add driver for Retrode2 joypad adapter This driver does 2 things: - Apply the MULTI_INPUT quirk to create separate joypad device nodes for each one of the 4 connectors. - Rename the input devices so that their names are different, and allow users to recognise which device corresponds to which physical port, including the SNES (Mario Paint) Mouse. Signed-off-by: Bastien Nocera Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 8 +++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 3 ++ drivers/hid/hid-ids.h | 3 ++ drivers/hid/hid-retrode.c | 100 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 115 insertions(+) create mode 100644 drivers/hid/hid-retrode.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 687705c50794..d409435fc0c2 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -741,6 +741,14 @@ config HID_PRIMAX Support for Primax devices that are not fully compliant with the HID standard. +config HID_RETRODE + tristate "Retrode" + depends on USB_HID + ---help--- + Support for + + * Retrode 2 cartridge and controller adapter + config HID_ROCCAT tristate "Roccat device support" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fef027bc7fa3..9ac08e7ed887 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -81,6 +81,7 @@ hid-picolcd-$(CONFIG_DEBUG_FS) += hid-picolcd_debugfs.o obj-$(CONFIG_HID_PLANTRONICS) += hid-plantronics.o obj-$(CONFIG_HID_PRIMAX) += hid-primax.o +obj-$(CONFIG_HID_RETRODE) += hid-retrode.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6e040692f1d8..49b9b4e8bfb4 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2151,6 +2151,9 @@ static const struct hid_device_id hid_have_special_driver[] = { #if IS_ENABLED(CONFIG_HID_PRODIKEYS) { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) }, #endif +#if IS_ENABLED(CONFIG_HID_RETRODE) + { HID_USB_DEVICE(USB_VENDOR_ID_FUTURE_TECHNOLOGY, USB_DEVICE_ID_RETRODE2) }, +#endif #if IS_ENABLED(CONFIG_HID_RMI) { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_COVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4f9a3938189a..82c9f2c43570 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -386,6 +386,9 @@ #define USB_VENDOR_ID_FUTABA 0x0547 #define USB_DEVICE_ID_LED_DISPLAY 0x7000 +#define USB_VENDOR_ID_FUTURE_TECHNOLOGY 0x0403 +#define USB_DEVICE_ID_RETRODE2 0x97c1 + #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 diff --git a/drivers/hid/hid-retrode.c b/drivers/hid/hid-retrode.c new file mode 100644 index 000000000000..30cc7ebb4d75 --- /dev/null +++ b/drivers/hid/hid-retrode.c @@ -0,0 +1,100 @@ +/* + * HID driver for Retrode 2 controller adapter and plug-in extensions + * + * Copyright (c) 2017 Bastien Nocera + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include "hid-ids.h" + +#define CONTROLLER_NAME_BASE "Retrode" + +static int retrode_input_configured(struct hid_device *hdev, + struct hid_input *hi) +{ + struct hid_field *field = hi->report->field[0]; + const char *suffix; + int number = 0; + char *name; + + switch (field->report->id) { + case 0: + suffix = "SNES Mouse"; + break; + case 1: + case 2: + suffix = "SNES / N64"; + number = field->report->id; + break; + case 3: + case 4: + suffix = "Mega Drive"; + number = field->report->id - 2; + break; + default: + hid_err(hdev, "Got unhandled report id %d\n", field->report->id); + suffix = "Unknown"; + } + + if (number) + name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "%s %s #%d", CONTROLLER_NAME_BASE, + suffix, number); + else + name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "%s %s", CONTROLLER_NAME_BASE, suffix); + + if (!name) + return -ENOMEM; + + hi->input->name = name; + + return 0; +} + +static int retrode_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + + int ret; + + /* Has no effect on the mouse device */ + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + + ret = hid_parse(hdev); + if (ret) + return ret; + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) + return ret; + + return 0; +} + +static const struct hid_device_id retrode_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_FUTURE_TECHNOLOGY, USB_DEVICE_ID_RETRODE2) }, + { } +}; +MODULE_DEVICE_TABLE(hid, retrode_devices); + +static struct hid_driver retrode_driver = { + .name = "hid-retrode", + .id_table = retrode_devices, + .input_configured = retrode_input_configured, + .probe = retrode_probe, +}; + +module_hid_driver(retrode_driver); + +MODULE_LICENSE("GPL"); From fd91189654a36f9566fedfd8ce6f4c5959c099bc Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 15 Jun 2017 15:32:03 +0200 Subject: [PATCH 30/35] HID: multitouch: use BIT macro (1 << X) is wrong. We should use BIT(X) Signed-off-by: Benjamin Tissoires Tested-by: Arek Burdach Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index f07256ee2d26..ba0675cf21a0 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -54,22 +54,22 @@ MODULE_LICENSE("GPL"); #include "hid-ids.h" /* quirks to control the device */ -#define MT_QUIRK_NOT_SEEN_MEANS_UP (1 << 0) -#define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1) -#define MT_QUIRK_CYPRESS (1 << 2) -#define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3) -#define MT_QUIRK_ALWAYS_VALID (1 << 4) -#define MT_QUIRK_VALID_IS_INRANGE (1 << 5) -#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6) -#define MT_QUIRK_CONFIDENCE (1 << 7) -#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8) -#define MT_QUIRK_NO_AREA (1 << 9) -#define MT_QUIRK_IGNORE_DUPLICATES (1 << 10) -#define MT_QUIRK_HOVERING (1 << 11) -#define MT_QUIRK_CONTACT_CNT_ACCURATE (1 << 12) -#define MT_QUIRK_FORCE_GET_FEATURE (1 << 13) -#define MT_QUIRK_FIX_CONST_CONTACT_ID (1 << 14) -#define MT_QUIRK_TOUCH_SIZE_SCALING (1 << 15) +#define MT_QUIRK_NOT_SEEN_MEANS_UP BIT(0) +#define MT_QUIRK_SLOT_IS_CONTACTID BIT(1) +#define MT_QUIRK_CYPRESS BIT(2) +#define MT_QUIRK_SLOT_IS_CONTACTNUMBER BIT(3) +#define MT_QUIRK_ALWAYS_VALID BIT(4) +#define MT_QUIRK_VALID_IS_INRANGE BIT(5) +#define MT_QUIRK_VALID_IS_CONFIDENCE BIT(6) +#define MT_QUIRK_CONFIDENCE BIT(7) +#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE BIT(8) +#define MT_QUIRK_NO_AREA BIT(9) +#define MT_QUIRK_IGNORE_DUPLICATES BIT(10) +#define MT_QUIRK_HOVERING BIT(11) +#define MT_QUIRK_CONTACT_CNT_ACCURATE BIT(12) +#define MT_QUIRK_FORCE_GET_FEATURE BIT(13) +#define MT_QUIRK_FIX_CONST_CONTACT_ID BIT(14) +#define MT_QUIRK_TOUCH_SIZE_SCALING BIT(15) #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 From 4f4001bc76fd1a138a501fbd3d68cce72cbf96ce Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 15 Jun 2017 15:32:04 +0200 Subject: [PATCH 31/35] HID: multitouch: fix rare Win 8 cases when the touch up event gets missing Instead of blindly trusting the hardware to send us release, we should consider some events can get lost and release them when we judge time has come. The Windows 8 spec allows to be confident in the fact that the device will continuously report events when a finger touches the surface. This has been tested on the HID recording database I have, and all of those devices behave properly. Also, Arek tested it on his Lenovo Yoga 910, which exports such bug in some situations, when the movements are rather slow. We use an atomic bit here to guard against concurrent accesses to the mt slots because both mt_process_mt_event() and mt_expired_timeout() are called in interrupt context. Signed-off-by: Arek Burdach Signed-off-by: Benjamin Tissoires Tested-by: Arek Burdach Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 102 +++++++++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 23 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index ba0675cf21a0..25641b2f90ab 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -44,6 +44,7 @@ #include #include #include +#include MODULE_AUTHOR("Stephane Chatty "); @@ -70,12 +71,15 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_FORCE_GET_FEATURE BIT(13) #define MT_QUIRK_FIX_CONST_CONTACT_ID BIT(14) #define MT_QUIRK_TOUCH_SIZE_SCALING BIT(15) +#define MT_QUIRK_STICKY_FINGERS BIT(16) #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 #define MT_BUTTONTYPE_CLICKPAD 0 +#define MT_IO_FLAGS_RUNNING 0 + struct mt_slot { __s32 x, y, cx, cy, p, w, h; __s32 contactid; /* the device ContactID assigned to this slot */ @@ -104,8 +108,10 @@ struct mt_fields { struct mt_device { struct mt_slot curdata; /* placeholder of incoming data */ struct mt_class mtclass; /* our mt device class */ + struct timer_list release_timer; /* to release sticky fingers */ struct mt_fields *fields; /* temporary placeholder for storing the multitouch fields */ + unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_*) */ int cc_index; /* contact count field index in the report */ int cc_value_index; /* contact count value index in the field */ unsigned last_slot_field; /* the last field of a slot */ @@ -214,7 +220,8 @@ static struct mt_class mt_classes[] = { .quirks = MT_QUIRK_ALWAYS_VALID | MT_QUIRK_IGNORE_DUPLICATES | MT_QUIRK_HOVERING | - MT_QUIRK_CONTACT_CNT_ACCURATE }, + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_STICKY_FINGERS }, { .name = MT_CLS_EXPORT_ALL_INPUTS, .quirks = MT_QUIRK_ALWAYS_VALID | MT_QUIRK_CONTACT_CNT_ACCURATE, @@ -804,6 +811,10 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) unsigned count; int r, n; + /* sticky fingers release in progress, abort */ + if (test_and_set_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) + return; + /* * Includes multi-packet support where subsequent * packets are sent with zero contactcount. @@ -829,6 +840,29 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) if (td->num_received >= td->num_expected) mt_sync_frame(td, report->field[0]->hidinput->input); + + /* + * Windows 8 specs says 2 things: + * - once a contact has been reported, it has to be reported in each + * subsequent report + * - the report rate when fingers are present has to be at least + * the refresh rate of the screen, 60 or 120 Hz + * + * I interprete this that the specification forces a report rate of + * at least 60 Hz for a touchscreen to be certified. + * Which means that if we do not get a report whithin 16 ms, either + * something wrong happens, either the touchscreen forgets to send + * a release. Taking a reasonable margin allows to remove issues + * with USB communication or the load of the machine. + * + * Given that Win 8 devices are forced to send a release, this will + * only affect laggish machines and the ones that have a firmware + * defect. + */ + if (td->mtclass.quirks & MT_QUIRK_STICKY_FINGERS) + mod_timer(&td->release_timer, jiffies + msecs_to_jiffies(100)); + + clear_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } static int mt_touch_input_configured(struct hid_device *hdev, @@ -1140,6 +1174,46 @@ static void mt_fix_const_fields(struct hid_device *hdev, unsigned int usage) } } +static void mt_release_contacts(struct hid_device *hid) +{ + struct hid_input *hidinput; + struct mt_device *td = hid_get_drvdata(hid); + + list_for_each_entry(hidinput, &hid->inputs, list) { + struct input_dev *input_dev = hidinput->input; + struct input_mt *mt = input_dev->mt; + int i; + + if (mt) { + for (i = 0; i < mt->num_slots; i++) { + input_mt_slot(input_dev, i); + input_mt_report_slot_state(input_dev, + MT_TOOL_FINGER, + false); + } + input_mt_sync_frame(input_dev); + input_sync(input_dev); + } + } + + td->num_received = 0; +} + +static void mt_expired_timeout(unsigned long arg) +{ + struct hid_device *hdev = (void *)arg; + struct mt_device *td = hid_get_drvdata(hdev); + + /* + * An input report came in just before we release the sticky fingers, + * it will take care of the sticky fingers. + */ + if (test_and_set_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) + return; + mt_release_contacts(hdev); + clear_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); +} + static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret, i; @@ -1209,6 +1283,8 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) */ hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; + setup_timer(&td->release_timer, mt_expired_timeout, (long)hdev); + ret = hid_parse(hdev); if (ret != 0) return ret; @@ -1236,28 +1312,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) } #ifdef CONFIG_PM -static void mt_release_contacts(struct hid_device *hid) -{ - struct hid_input *hidinput; - - list_for_each_entry(hidinput, &hid->inputs, list) { - struct input_dev *input_dev = hidinput->input; - struct input_mt *mt = input_dev->mt; - int i; - - if (mt) { - for (i = 0; i < mt->num_slots; i++) { - input_mt_slot(input_dev, i); - input_mt_report_slot_state(input_dev, - MT_TOOL_FINGER, - false); - } - input_mt_sync_frame(input_dev); - input_sync(input_dev); - } - } -} - static int mt_reset_resume(struct hid_device *hdev) { mt_release_contacts(hdev); @@ -1282,6 +1336,8 @@ static void mt_remove(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); + del_timer_sync(&td->release_timer); + sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); hid_hw_stop(hdev); hdev->quirks = td->initial_quirks; From 9609827458c37d7b2c37f2a9255631c603a5004c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 15 Jun 2017 15:32:05 +0200 Subject: [PATCH 32/35] HID: multitouch: optimize the sticky fingers timer Instead of unconditionally expiring the timer and calling a long mt_release_contacts(), we can check if some slots are used when the timer expires. We can also remove the timer if we happen to receive all the releases. The logic behind the MT_IO_FLAGS_PENDING_SLOTS could be implemented by counting how many slots are active, but using bits feels slightly more efficient. Signed-off-by: Benjamin Tissoires Tested-by: Arek Burdach Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 25641b2f90ab..f3e35e7a189d 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -79,6 +79,8 @@ MODULE_LICENSE("GPL"); #define MT_BUTTONTYPE_CLICKPAD 0 #define MT_IO_FLAGS_RUNNING 0 +#define MT_IO_FLAGS_ACTIVE_SLOTS 1 +#define MT_IO_FLAGS_PENDING_SLOTS 2 struct mt_slot { __s32 x, y, cx, cy, p, w, h; @@ -705,6 +707,8 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input) input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); + + set_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags); } } @@ -720,6 +724,11 @@ static void mt_sync_frame(struct mt_device *td, struct input_dev *input) input_mt_sync_frame(input); input_sync(input); td->num_received = 0; + if (test_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags)) + set_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags); + else + clear_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags); + clear_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags); } static int mt_touch_event(struct hid_device *hid, struct hid_field *field, @@ -859,8 +868,13 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) * only affect laggish machines and the ones that have a firmware * defect. */ - if (td->mtclass.quirks & MT_QUIRK_STICKY_FINGERS) - mod_timer(&td->release_timer, jiffies + msecs_to_jiffies(100)); + if (td->mtclass.quirks & MT_QUIRK_STICKY_FINGERS) { + if (test_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags)) + mod_timer(&td->release_timer, + jiffies + msecs_to_jiffies(100)); + else + del_timer(&td->release_timer); + } clear_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } @@ -1210,7 +1224,8 @@ static void mt_expired_timeout(unsigned long arg) */ if (test_and_set_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) return; - mt_release_contacts(hdev); + if (test_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags)) + mt_release_contacts(hdev); clear_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } From 75a5f3ac5c861e969e2379816709d55f1cb5e9f4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 26 Jun 2017 20:35:38 +0100 Subject: [PATCH 33/35] HID: wacom: fix mistake in printk trivial fix to spelling mistake in hid_warn warning message Signed-off-by: Colin Ian King Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index aa0becea865e..149bdff28d32 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2109,28 +2109,28 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field return; case WACOM_HID_WD_OFFSETLEFT: if (features->offset_left && value != features->offset_left) - hid_warn(hdev, "%s: overriding exising left offset " + hid_warn(hdev, "%s: overriding existing left offset " "%d -> %d\n", __func__, value, features->offset_left); features->offset_left = value; return; case WACOM_HID_WD_OFFSETRIGHT: if (features->offset_right && value != features->offset_right) - hid_warn(hdev, "%s: overriding exising right offset " + hid_warn(hdev, "%s: overriding existing right offset " "%d -> %d\n", __func__, value, features->offset_right); features->offset_right = value; return; case WACOM_HID_WD_OFFSETTOP: if (features->offset_top && value != features->offset_top) - hid_warn(hdev, "%s: overriding exising top offset " + hid_warn(hdev, "%s: overriding existing top offset " "%d -> %d\n", __func__, value, features->offset_top); features->offset_top = value; return; case WACOM_HID_WD_OFFSETBOTTOM: if (features->offset_bottom && value != features->offset_bottom) - hid_warn(hdev, "%s: overriding exising bottom offset " + hid_warn(hdev, "%s: overriding existing bottom offset " "%d -> %d\n", __func__, value, features->offset_bottom); features->offset_bottom = value; From 1e3b74a2f844c3fcd8b3206178b8c6524aa34d05 Mon Sep 17 00:00:00 2001 From: Song Hongyan Date: Thu, 29 Jun 2017 13:43:33 -0700 Subject: [PATCH 34/35] HID: intel-ish-hid: Enable Cannon Lake ish driver Added PCI ID for Cannon Lake ISH. Signed-off-by: Song Hongyan Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/hw-ish.h | 1 + drivers/hid/intel-ish-hid/ipc/pci-ish.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h index fd34307a7a70..ddc826347917 100644 --- a/drivers/hid/intel-ish-hid/ipc/hw-ish.h +++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h @@ -26,6 +26,7 @@ #define BXT_Bx_DEVICE_ID 0x1AA2 #define APL_Ax_DEVICE_ID 0x5AA2 #define SPT_Ax_DEVICE_ID 0x9D35 +#define CNL_Ax_DEVICE_ID 0x9DFC #define REVISION_ID_CHT_A0 0x6 #define REVISION_ID_CHT_Ax_SI 0x0 diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 8df81dc84529..878e96a89d9d 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -35,6 +35,7 @@ static const struct pci_device_id ish_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)}, {0, } }; MODULE_DEVICE_TABLE(pci, ish_pci_tbl); From 1694130910cd654a9215fbc0244975e3cf208fc1 Mon Sep 17 00:00:00 2001 From: Song Hongyan Date: Thu, 29 Jun 2017 13:43:34 -0700 Subject: [PATCH 35/35] HID: intel-ish-hid: Enable Gemini Lake ish driver Added PCI ID for Gemini Lake ISH. Signed-off-by: Song Hongyan Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/hw-ish.h | 1 + drivers/hid/intel-ish-hid/ipc/pci-ish.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h index ddc826347917..2aac097c3f70 100644 --- a/drivers/hid/intel-ish-hid/ipc/hw-ish.h +++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h @@ -27,6 +27,7 @@ #define APL_Ax_DEVICE_ID 0x5AA2 #define SPT_Ax_DEVICE_ID 0x9D35 #define CNL_Ax_DEVICE_ID 0x9DFC +#define GLK_Ax_DEVICE_ID 0x31A2 #define REVISION_ID_CHT_A0 0x6 #define REVISION_ID_CHT_Ax_SI 0x0 diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 878e96a89d9d..20d824f74f99 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -36,6 +36,7 @@ static const struct pci_device_id ish_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)}, {0, } }; MODULE_DEVICE_TABLE(pci, ish_pci_tbl);