From: Greg Kroah-Hartman Date: Wed, 28 Nov 2018 08:42:00 +0000 (+0100) Subject: 4.19-stable patches X-Git-Tag: v4.19.6~65 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=00270640bf3d6b565abf452cb9997e3b6cf16108;p=thirdparty%2Fkernel%2Fstable-queue.git 4.19-stable patches added patches: hid-steam-remove-input-device-when-a-hid-client-is-running.patch --- diff --git a/queue-4.19/hid-steam-remove-input-device-when-a-hid-client-is-running.patch b/queue-4.19/hid-steam-remove-input-device-when-a-hid-client-is-running.patch new file mode 100644 index 00000000000..0cd3b708710 --- /dev/null +++ b/queue-4.19/hid-steam-remove-input-device-when-a-hid-client-is-running.patch @@ -0,0 +1,319 @@ +From 385a4886778f6d6e61eff1d4d295af332d7130e1 Mon Sep 17 00:00:00 2001 +From: Rodrigo Rivas Costa +Date: Sun, 14 Oct 2018 19:36:43 +0200 +Subject: HID: steam: remove input device when a hid client is running. + +From: Rodrigo Rivas Costa + +commit 385a4886778f6d6e61eff1d4d295af332d7130e1 upstream. + +Previously, when a HID client such as the Steam Client was running, this +driver disabled its input device to avoid doubling the input events. + +While it worked mostly fine, some games got confused by the idle gamepad, +and switched to two player mode, or asked the user to choose which gamepad +to use. Other games just crashed, probably a bug in Unity [1]. + +With this commit, when a HID client starts, the input device is removed; +when the HID client ends the input device is recreated. + +[1]: https://github.com/ValveSoftware/steam-for-linux/issues/5645 + +Signed-off-by: Rodrigo Rivas Costa +Signed-off-by: Jiri Kosina +Cc: Pierre-Loup Griffais +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/hid/hid-steam.c | 154 ++++++++++++++++++++++++++++-------------------- + 1 file changed, 90 insertions(+), 64 deletions(-) + +--- a/drivers/hid/hid-steam.c ++++ b/drivers/hid/hid-steam.c +@@ -23,8 +23,9 @@ + * In order to avoid breaking them this driver creates a layered hidraw device, + * so it can detect when the client is running and then: + * - it will not send any command to the controller. +- * - this input device will be disabled, to avoid double input of the same ++ * - this input device will be removed, to avoid double input of the same + * user action. ++ * When the client is closed, this input device will be created again. + * + * For additional functions, such as changing the right-pad margin or switching + * the led, you can use the user-space tool at: +@@ -113,7 +114,7 @@ struct steam_device { + spinlock_t lock; + struct hid_device *hdev, *client_hdev; + struct mutex mutex; +- bool client_opened, input_opened; ++ bool client_opened; + struct input_dev __rcu *input; + unsigned long quirks; + struct work_struct work_connect; +@@ -279,18 +280,6 @@ static void steam_set_lizard_mode(struct + } + } + +-static void steam_update_lizard_mode(struct steam_device *steam) +-{ +- mutex_lock(&steam->mutex); +- if (!steam->client_opened) { +- if (steam->input_opened) +- steam_set_lizard_mode(steam, false); +- else +- steam_set_lizard_mode(steam, lizard_mode); +- } +- mutex_unlock(&steam->mutex); +-} +- + static int steam_input_open(struct input_dev *dev) + { + struct steam_device *steam = input_get_drvdata(dev); +@@ -301,7 +290,6 @@ static int steam_input_open(struct input + return ret; + + mutex_lock(&steam->mutex); +- steam->input_opened = true; + if (!steam->client_opened && lizard_mode) + steam_set_lizard_mode(steam, false); + mutex_unlock(&steam->mutex); +@@ -313,7 +301,6 @@ static void steam_input_close(struct inp + struct steam_device *steam = input_get_drvdata(dev); + + mutex_lock(&steam->mutex); +- steam->input_opened = false; + if (!steam->client_opened && lizard_mode) + steam_set_lizard_mode(steam, true); + mutex_unlock(&steam->mutex); +@@ -400,7 +387,7 @@ static int steam_battery_register(struct + return 0; + } + +-static int steam_register(struct steam_device *steam) ++static int steam_input_register(struct steam_device *steam) + { + struct hid_device *hdev = steam->hdev; + struct input_dev *input; +@@ -414,17 +401,6 @@ static int steam_register(struct steam_d + return 0; + } + +- /* +- * Unlikely, but getting the serial could fail, and it is not so +- * important, so make up a serial number and go on. +- */ +- if (steam_get_serial(steam) < 0) +- strlcpy(steam->serial_no, "XXXXXXXXXX", +- sizeof(steam->serial_no)); +- +- hid_info(hdev, "Steam Controller '%s' connected", +- steam->serial_no); +- + input = input_allocate_device(); + if (!input) + return -ENOMEM; +@@ -492,11 +468,6 @@ static int steam_register(struct steam_d + goto input_register_fail; + + rcu_assign_pointer(steam->input, input); +- +- /* ignore battery errors, we can live without it */ +- if (steam->quirks & STEAM_QUIRK_WIRELESS) +- steam_battery_register(steam); +- + return 0; + + input_register_fail: +@@ -504,27 +475,88 @@ input_register_fail: + return ret; + } + +-static void steam_unregister(struct steam_device *steam) ++static void steam_input_unregister(struct steam_device *steam) + { + struct input_dev *input; ++ rcu_read_lock(); ++ input = rcu_dereference(steam->input); ++ rcu_read_unlock(); ++ if (!input) ++ return; ++ RCU_INIT_POINTER(steam->input, NULL); ++ synchronize_rcu(); ++ input_unregister_device(input); ++} ++ ++static void steam_battery_unregister(struct steam_device *steam) ++{ + struct power_supply *battery; + + rcu_read_lock(); +- input = rcu_dereference(steam->input); + battery = rcu_dereference(steam->battery); + rcu_read_unlock(); + +- if (battery) { +- RCU_INIT_POINTER(steam->battery, NULL); +- synchronize_rcu(); +- power_supply_unregister(battery); ++ if (!battery) ++ return; ++ RCU_INIT_POINTER(steam->battery, NULL); ++ synchronize_rcu(); ++ power_supply_unregister(battery); ++} ++ ++static int steam_register(struct steam_device *steam) ++{ ++ int ret; ++ ++ /* ++ * This function can be called several times in a row with the ++ * wireless adaptor, without steam_unregister() between them, because ++ * another client send a get_connection_status command, for example. ++ * The battery and serial number are set just once per device. ++ */ ++ if (!steam->serial_no[0]) { ++ /* ++ * Unlikely, but getting the serial could fail, and it is not so ++ * important, so make up a serial number and go on. ++ */ ++ if (steam_get_serial(steam) < 0) ++ strlcpy(steam->serial_no, "XXXXXXXXXX", ++ sizeof(steam->serial_no)); ++ ++ hid_info(steam->hdev, "Steam Controller '%s' connected", ++ steam->serial_no); ++ ++ /* ignore battery errors, we can live without it */ ++ if (steam->quirks & STEAM_QUIRK_WIRELESS) ++ steam_battery_register(steam); ++ ++ mutex_lock(&steam_devices_lock); ++ list_add(&steam->list, &steam_devices); ++ mutex_unlock(&steam_devices_lock); + } +- if (input) { +- RCU_INIT_POINTER(steam->input, NULL); +- synchronize_rcu(); ++ ++ mutex_lock(&steam->mutex); ++ if (!steam->client_opened) { ++ steam_set_lizard_mode(steam, lizard_mode); ++ ret = steam_input_register(steam); ++ } else { ++ ret = 0; ++ } ++ mutex_unlock(&steam->mutex); ++ ++ return ret; ++} ++ ++static void steam_unregister(struct steam_device *steam) ++{ ++ steam_battery_unregister(steam); ++ steam_input_unregister(steam); ++ if (steam->serial_no[0]) { + hid_info(steam->hdev, "Steam Controller '%s' disconnected", + steam->serial_no); +- input_unregister_device(input); ++ mutex_lock(&steam_devices_lock); ++ list_del(&steam->list); ++ mutex_unlock(&steam_devices_lock); ++ steam->serial_no[0] = 0; + } + } + +@@ -600,6 +632,9 @@ static int steam_client_ll_open(struct h + mutex_lock(&steam->mutex); + steam->client_opened = true; + mutex_unlock(&steam->mutex); ++ ++ steam_input_unregister(steam); ++ + return ret; + } + +@@ -609,13 +644,13 @@ static void steam_client_ll_close(struct + + mutex_lock(&steam->mutex); + steam->client_opened = false; +- if (steam->input_opened) +- steam_set_lizard_mode(steam, false); +- else +- steam_set_lizard_mode(steam, lizard_mode); + mutex_unlock(&steam->mutex); + + hid_hw_close(steam->hdev); ++ if (steam->connected) { ++ steam_set_lizard_mode(steam, lizard_mode); ++ steam_input_register(steam); ++ } + } + + static int steam_client_ll_raw_request(struct hid_device *hdev, +@@ -744,11 +779,6 @@ static int steam_probe(struct hid_device + } + } + +- mutex_lock(&steam_devices_lock); +- steam_update_lizard_mode(steam); +- list_add(&steam->list, &steam_devices); +- mutex_unlock(&steam_devices_lock); +- + return 0; + + hid_hw_open_fail: +@@ -774,10 +804,6 @@ static void steam_remove(struct hid_devi + return; + } + +- mutex_lock(&steam_devices_lock); +- list_del(&steam->list); +- mutex_unlock(&steam_devices_lock); +- + hid_destroy_device(steam->client_hdev); + steam->client_opened = false; + cancel_work_sync(&steam->work_connect); +@@ -792,12 +818,14 @@ static void steam_remove(struct hid_devi + static void steam_do_connect_event(struct steam_device *steam, bool connected) + { + unsigned long flags; ++ bool changed; + + spin_lock_irqsave(&steam->lock, flags); ++ changed = steam->connected != connected; + steam->connected = connected; + spin_unlock_irqrestore(&steam->lock, flags); + +- if (schedule_work(&steam->work_connect) == 0) ++ if (changed && schedule_work(&steam->work_connect) == 0) + dbg_hid("%s: connected=%d event already queued\n", + __func__, connected); + } +@@ -1019,13 +1047,8 @@ static int steam_raw_event(struct hid_de + return 0; + rcu_read_lock(); + input = rcu_dereference(steam->input); +- if (likely(input)) { ++ if (likely(input)) + steam_do_input_event(steam, input, data); +- } else { +- dbg_hid("%s: input data without connect event\n", +- __func__); +- steam_do_connect_event(steam, true); +- } + rcu_read_unlock(); + break; + case STEAM_EV_CONNECT: +@@ -1074,7 +1097,10 @@ static int steam_param_set_lizard_mode(c + + mutex_lock(&steam_devices_lock); + list_for_each_entry(steam, &steam_devices, list) { +- steam_update_lizard_mode(steam); ++ mutex_lock(&steam->mutex); ++ if (!steam->client_opened) ++ steam_set_lizard_mode(steam, lizard_mode); ++ mutex_unlock(&steam->mutex); + } + mutex_unlock(&steam_devices_lock); + return 0;