From 37d1601938349e79e9c31a8aba431d5543e09e72 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 28 Apr 2017 09:25:30 -0700 Subject: 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(+) (limited to 'drivers/hid') 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; -- cgit v1.2.3-58-ga151 From f496c09c0785b60fa6b762ad720ba31f6a9de0ac Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 28 Apr 2017 09:25:31 -0700 Subject: 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(+) (limited to 'drivers/hid') 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; -- cgit v1.2.3-58-ga151 From a7758702879e68d221cf6dd9844bf5b2a070c8cb Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 28 Apr 2017 09:25:32 -0700 Subject: 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(+) (limited to 'drivers/hid') 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; -- cgit v1.2.3-58-ga151 From 16e4598905a9d7793350ffad2f627b3dfdb7b595 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 28 Apr 2017 09:25:33 -0700 Subject: 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(-) (limited to 'drivers/hid') 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; -- cgit v1.2.3-58-ga151 From 5ac3d4ae58050f451a4fd868028f25258ea0a628 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Fri, 28 Apr 2017 09:25:34 -0700 Subject: 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 | 162 +++++++++++++++++++++++++++--------------------- drivers/hid/wacom_wac.h | 4 ++ 2 files changed, 95 insertions(+), 71 deletions(-) (limited to 'drivers/hid') 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,20 +1702,92 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, } } -static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, +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; - struct input_dev *input = wacom_wac->pad_input; 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) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct wacom_features *features = &wacom_wac->features; + struct input_dev *input = wacom_wac->pad_input; + unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); + + switch (equivalent_usage) { case WACOM_HID_WD_ACCELEROMETER_X: __set_bit(INPUT_PROP_ACCELEROMETER, input->propbit); wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 0); @@ -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)) -- cgit v1.2.3-58-ga151 From 91b9ae48aadd7e634161372b0bc3ffc88a050e8b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 May 2017 22:30:33 +0200 Subject: 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/i2c-hid.h | 42 ----------------------------------- include/linux/platform_data/i2c-hid.h | 42 +++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 43 deletions(-) delete mode 100644 include/linux/i2c/i2c-hid.h create mode 100644 include/linux/platform_data/i2c-hid.h (limited to 'drivers/hid') 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/i2c/i2c-hid.h deleted file mode 100644 index 1fb088239d12..000000000000 --- a/include/linux/i2c/i2c-hid.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * HID over I2C protocol implementation - * - * Copyright (c) 2012 Benjamin Tissoires - * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive for - * more details. - */ - -#ifndef __LINUX_I2C_HID_H -#define __LINUX_I2C_HID_H - -#include - -struct regulator; - -/** - * struct i2chid_platform_data - used by hid over i2c implementation. - * @hid_descriptor_address: i2c register where the HID descriptor is stored. - * @supply: regulator for powering on the device. - * @post_power_delay_ms: delay after powering on before device is usable. - * - * Note that it is the responsibility of the platform driver (or the acpi 5.0 - * driver, or the flattened device tree) to setup the irq related to the gpio in - * the struct i2c_board_info. - * The platform driver should also setup the gpio according to the device: - * - * A typical example is the following: - * irq = gpio_to_irq(intr_gpio); - * hkdk4412_i2c_devs5[0].irq = irq; // store the irq in i2c_board_info - * gpio_request(intr_gpio, "elan-irq"); - * s3c_gpio_setpull(intr_gpio, S3C_GPIO_PULL_UP); - */ -struct i2c_hid_platform_data { - u16 hid_descriptor_address; - struct regulator *supply; - int post_power_delay_ms; -}; - -#endif /* __LINUX_I2C_HID_H */ diff --git a/include/linux/platform_data/i2c-hid.h b/include/linux/platform_data/i2c-hid.h new file mode 100644 index 000000000000..1fb088239d12 --- /dev/null +++ b/include/linux/platform_data/i2c-hid.h @@ -0,0 +1,42 @@ +/* + * HID over I2C protocol implementation + * + * Copyright (c) 2012 Benjamin Tissoires + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#ifndef __LINUX_I2C_HID_H +#define __LINUX_I2C_HID_H + +#include + +struct regulator; + +/** + * struct i2chid_platform_data - used by hid over i2c implementation. + * @hid_descriptor_address: i2c register where the HID descriptor is stored. + * @supply: regulator for powering on the device. + * @post_power_delay_ms: delay after powering on before device is usable. + * + * Note that it is the responsibility of the platform driver (or the acpi 5.0 + * driver, or the flattened device tree) to setup the irq related to the gpio in + * the struct i2c_board_info. + * The platform driver should also setup the gpio according to the device: + * + * A typical example is the following: + * irq = gpio_to_irq(intr_gpio); + * hkdk4412_i2c_devs5[0].irq = irq; // store the irq in i2c_board_info + * gpio_request(intr_gpio, "elan-irq"); + * s3c_gpio_setpull(intr_gpio, S3C_GPIO_PULL_UP); + */ +struct i2c_hid_platform_data { + u16 hid_descriptor_address; + struct regulator *supply; + int post_power_delay_ms; +}; + +#endif /* __LINUX_I2C_HID_H */ -- cgit v1.2.3-58-ga151 From d36b7d4c271b2f93127e7e7cc007b5768a296594 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:31 -0700 Subject: 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(-) (limited to 'drivers/hid') 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); -- cgit v1.2.3-58-ga151 From 9a83563fb3f926cbf0d5992d5c70d760c445ba09 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:32 -0700 Subject: 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(-) (limited to 'drivers/hid') 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 */ -- cgit v1.2.3-58-ga151 From 28cbc863f4bfa92c26143493f0463e4eb96a1783 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:33 -0700 Subject: 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(-) (limited to 'drivers/hid') 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 -- cgit v1.2.3-58-ga151 From aaac082dac0a8ac6b00509c7ae2fa8280f966652 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:34 -0700 Subject: 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(-) (limited to 'drivers/hid') 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 -- cgit v1.2.3-58-ga151 From 85ae91133152c8c5e214303b8e26cfcbb91dfeb9 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:35 -0700 Subject: 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(-) (limited to 'drivers/hid') 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) -- cgit v1.2.3-58-ga151 From e399396a6b061ba9e68e64e2867cc3a0f26f0ace Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 Jun 2017 23:59:36 -0700 Subject: 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 | 115 +++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 62 deletions(-) (limited to 'drivers/hid') 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; - - 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); + int res; - /* - * 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); + if (hid->quirks & HID_QUIRK_ALWAYS_POLL) + return 0; + + 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; } /* -- cgit v1.2.3-58-ga151 From 08585e43d22802666a466af1ca5795085e74d60d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 13 Jun 2017 12:22:22 +0300 Subject: 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(-) (limited to 'drivers/hid') 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) -- cgit v1.2.3-58-ga151 From 504c932c7c47a6b4c572302a13873f7d83af1ff3 Mon Sep 17 00:00:00 2001 From: Masaki Ota Date: Fri, 16 Jun 2017 09:29:39 +0900 Subject: 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(-) (limited to 'drivers/hid') 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, -- cgit v1.2.3-58-ga151 From 0e82232c420e9bd40813000e82233e4b206d38d1 Mon Sep 17 00:00:00 2001 From: Wei-Ning Huang Date: Wed, 21 Jun 2017 10:43:25 +0800 Subject: 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(+) (limited to 'drivers/hid') 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) }, -- cgit v1.2.3-58-ga151 From 13b2e1ba486f07ccd26e01c563b52a9171b2c255 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Tue, 20 Jun 2017 18:13:37 +0200 Subject: 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 (limited to 'drivers/hid') 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"); -- cgit v1.2.3-58-ga151 From fd91189654a36f9566fedfd8ce6f4c5959c099bc Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 15 Jun 2017 15:32:03 +0200 Subject: 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(-) (limited to 'drivers/hid') 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 -- cgit v1.2.3-58-ga151 From 4f4001bc76fd1a138a501fbd3d68cce72cbf96ce Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 15 Jun 2017 15:32:04 +0200 Subject: 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(-) (limited to 'drivers/hid') 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; -- cgit v1.2.3-58-ga151 From 9609827458c37d7b2c37f2a9255631c603a5004c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 15 Jun 2017 15:32:05 +0200 Subject: 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(-) (limited to 'drivers/hid') 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); } -- cgit v1.2.3-58-ga151 From 75a5f3ac5c861e969e2379816709d55f1cb5e9f4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 26 Jun 2017 20:35:38 +0100 Subject: 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(-) (limited to 'drivers/hid') 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; -- cgit v1.2.3-58-ga151