lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <5114a63f-d6dc-e703-41de-6f78a2699611@valvesoftware.com>
Date:   Mon, 15 Oct 2018 12:55:58 -0700
From:   "Pierre-Loup A. Griffais" <pgriffais@...vesoftware.com>
To:     Rodrigo Rivas Costa <rodrigorivascosta@...il.com>,
        Benjamin Tissoires <benjamin.tissoires@...hat.com>,
        Jiri Kosina <jikos@...nel.org>,
        lkml <linux-kernel@...r.kernel.org>,
        linux-input <linux-input@...r.kernel.org>
Subject: Re: [PATCH] HID: steam: remove input device when a hid client is
 running.



On 10/14/18 10:36 AM, Rodrigo Rivas Costa wrote:
> 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

Hi Rodrigo,

Thanks a lot, this should indeed help a ton. Just the presence of an 
extra gamepad node can indeed cause applications to go completely 
different paths. Assuming it otherwise looks good, is there a chance 
this fix could be put back into the 4.18 branch?

Thanks,
  - Pierre-Loup

> 
> Signed-off-by: Rodrigo Rivas Costa <rodrigorivascosta@...il.com>
> ---
>   drivers/hid/hid-steam.c | 154 +++++++++++++++++++++++-----------------
>   1 file changed, 90 insertions(+), 64 deletions(-)
> 
> diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
> index 0422ec2b13d2..dc4128bfe2ca 100644
> --- 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 steam_device *steam, bool enable)
>   	}
>   }
>   
> -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_dev *dev)
>   		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 input_dev *dev)
>   	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 steam_device *steam)
>   	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_device *steam)
>   		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_device *steam)
>   		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 @@ static int steam_register(struct steam_device *steam)
>   	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 hid_device *hdev)
>   	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 hid_device *hdev)
>   
>   	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 *hdev,
>   		}
>   	}
>   
> -	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_device *hdev)
>   		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_device *hdev)
>   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_device *hdev,
>   			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(const char *val,
>   
>   	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;
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ