Re: [PATCH] HID: Separate struct hid_device's driver_lock into two locks.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 13 Feb 2013, Andrew de los Reyes wrote:

> Here's the latest version of the patch. I believe it addresses all
> outstanding comments.
> 
> Thanks!
> -andrew
> 
> This patch separates struct hid_device's driver_lock into two. The
> goal is to allow hid device drivers to receive input during their
> probe() or remove() function calls. This is necessary because some
> drivers need to communicate with the device to determine parameters
> needed during probe (e.g., size of a multi-touch surface), and if
> possible, may perfer to communicate with a device on host-initiated
> disconnect (e.g., to put it into a low-power state).
> 
> Historically, three functions used driver_lock:
> 
> - hid_device_probe: blocks to acquire lock
> - hid_device_remove: blocks to acquire lock
> - hid_input_report: if locked returns -EBUSY, else acquires lock
> 
> This patch adds another lock (driver_input_lock) which is used to
> block input from occurring. The lock behavior is now:
> 
> - hid_device_probe: blocks to acq. driver_lock, then driver_input_lock
> - hid_device_remove: blocks to acq. driver_lock, then driver_input_lock
> - hid_input_report: if driver_input_lock locked returns -EBUSY, else
>   acquires driver_input_lock
> 
> This patch also adds two helper functions to be called during probe()
> or remove(): hid_device_io_start() and hid_device_io_stop(). These
> functions lock and unlock, respectively, driver_input_lock; they also
> make a note of whether they did so that hid-core knows if a driver has
> changed the lock state.
> 
> This patch results in no behavior change for existing devices and
> drivers. However, during a probe() or remove() function call in a
> driver, that driver may now selectively call hid_device_io_start() to
> let input events come through, then optionally call
> hid_device_io_stop() to stop them.
> 
> Change-Id: I737f6fc15911134b51273acf8d3de92fa5cc0f85

Hi,

thanks for finally proceeding on this front.

Please provide your Signed-off-by line; without that, the patch can't be 
accepted.

> ---
>  drivers/hid/hid-core.c | 24 +++++++++++++++++++++---
>  include/linux/hid.h    | 38 +++++++++++++++++++++++++++++++++++++-
>  2 files changed, 58 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index 4da66b4..714d8b7 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1097,7 +1097,7 @@ int hid_input_report(struct hid_device *hid, int
> type, u8 *data, int size, int i
>  	if (!hid)
>  		return -ENODEV;
> 
> -	if (down_trylock(&hid->driver_lock))
> +	if (down_trylock(&hid->driver_input_lock))
>  		return -EBUSY;
> 
>  	if (!hid->driver) {
> @@ -1150,7 +1150,7 @@ nomem:
>  	hid_report_raw_event(hid, type, data, size, interrupt);
> 
>  unlock:
> -	up(&hid->driver_lock);
> +	up(&hid->driver_input_lock);
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(hid_input_report);
> @@ -1703,6 +1703,11 @@ static int hid_device_probe(struct device *dev)
> 
>  	if (down_interruptible(&hdev->driver_lock))
>  		return -EINTR;
> +	if (down_interruptible(&hdev->driver_input_lock)) {
> +		ret = -EINTR;
> +		goto unlock_driver_lock;
> +	}
> +	hdev->io_started = false;
> 
>  	if (!hdev->driver) {
>  		id = hid_match_device(hdev, hdrv);
> @@ -1726,6 +1731,9 @@ static int hid_device_probe(struct device *dev)
>  			hdev->driver = NULL;
>  	}
>  unlock:
> +	if (!hdev->io_started)
> +		up(&hdev->driver_input_lock);
> +unlock_driver_lock:
>  	up(&hdev->driver_lock);
>  	return ret;
>  }
> @@ -1734,9 +1742,15 @@ static int hid_device_remove(struct device *dev)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
>  	struct hid_driver *hdrv;
> +	int ret = 0;
> 
>  	if (down_interruptible(&hdev->driver_lock))
>  		return -EINTR;
> +	if (down_interruptible(&hdev->driver_input_lock)) {
> +		ret = -EINTR;
> +		goto unlock_driver_lock;
> +	}
> +	hdev->io_started = false;
> 
>  	hdrv = hdev->driver;
>  	if (hdrv) {
> @@ -1747,8 +1761,11 @@ static int hid_device_remove(struct device *dev)
>  		hdev->driver = NULL;
>  	}
> 
> +	if (!hdev->io_started)
> +		up(&hdev->driver_input_lock);
> +unlock_driver_lock:
>  	up(&hdev->driver_lock);
> -	return 0;
> +	return ret;
>  }
> 
>  static int hid_uevent(struct device *dev, struct kobj_uevent_env *env)
> @@ -2126,6 +2143,7 @@ struct hid_device *hid_allocate_device(void)
>  	init_waitqueue_head(&hdev->debug_wait);
>  	INIT_LIST_HEAD(&hdev->debug_list);
>  	sema_init(&hdev->driver_lock, 1);
> +	sema_init(&hdev->driver_input_lock, 1);
> 
>  	return hdev;
>  err:
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index 3a95da6..27282a1 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -481,7 +481,8 @@ struct hid_device {							/* device report descriptor */
>  	unsigned country;						/* HID country */
>  	struct hid_report_enum report_enum[HID_REPORT_TYPES];
> 
> -	struct semaphore driver_lock;					/* protects the current driver */
> +	struct semaphore driver_lock;					/* protects the current driver,
> except during input */
> +	struct semaphore driver_input_lock;				/* protects the current driver */
>  	struct device dev;						/* device */
>  	struct hid_driver *driver;
>  	struct hid_ll_driver *ll_driver;
> @@ -502,6 +503,7 @@ struct hid_device {							/* device report descriptor */
>  	unsigned int status;						/* see STAT flags above */
>  	unsigned claimed;						/* Claimed by hidinput, hiddev? */
>  	unsigned quirks;						/* Various quirks the device can pull on us */
> +	bool io_started;						/* Protected by driver_lock. If IO has started */
> 
>  	struct list_head inputs;					/* The list of inputs */
>  	void *hiddev;							/* The hiddev structure */
> @@ -622,6 +624,10 @@ struct hid_usage_id {
>   * @resume: invoked on resume if device was not reset (NULL means nop)
>   * @reset_resume: invoked on resume if device was reset (NULL means nop)
>   *
> + * probe should return -errno on error, or 0 on success. During probe,
> + * input will not be passed to raw_event unless hid_device_io_start is
> + * called.
> + *
>   * raw_event and event should return 0 on no action performed, 1 when no
>   * further processing should be done and negative on error
>   *
> @@ -742,6 +748,36 @@ const struct hid_device_id *hid_match_id(struct
> hid_device *hdev,
>  					 const struct hid_device_id *id);
> 
>  /**
> + * hid_device_io_start - enable HID input during probe, remove
> + *
> + * @hid - the device
> + *
> + * This should only be called during probe or remove and only be
> + * called by the thread calling probe or remove. It will allow
> + * incoming packets to be delivered to the driver.
> + */
> +static inline void hid_device_io_start(struct hid_device *hid) {
> +  hid->io_started = true;
> +  up(&hid->driver_input_lock);
> +}
> +
> +/**
> + * hid_device_io_stop - disable HID input during probe, remove
> + *
> + * @hid - the device
> + *
> + * Should only be called after hid_device_io_start. It will prevent
> + * incoming packets from going to the driver for the duration of
> + * probe, remove. If called during probe, packets will still go to the
> + * driver after probe is complete. This function should only be called
> + * by the thread calling probe or remove.
> + */
> +static inline void hid_device_io_stop(struct hid_device *hid) {
> +  hid->io_started = false;
> +  down(&hid->driver_input_lock);
> +}

Is there any particular reason to have hid_device_io_start() and 
hid_device_io_stop() indentation not use tabs?

Also, the functions are currently unused, so I'd rather suggest adding 
them together when individual device driver(s) are converted to using it.

Thanks again,

-- 
Jiri Kosina
SUSE Labs
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux