Re: autosuspend for hid

Alan Stern <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
On Wed, 16 May 2007, Oliver Neukum wrote:

> Here's part #2, the core autosuspend.

> @@ -230,12 +239,14 @@ static void hid_irq_in(struct urb *urb)
>  
>  	switch (urb->status) {
>  		case 0:			/* success */
> +			usbhid_mark_busy(usbhid);	
>  			usbhid->retry_delay = 0;
>  			hid_input_report(urb->context, HID_INPUT_REPORT,
>  					 urb->transfer_buffer,
>  					 urb->actual_length, 1);
>  			break;
>  		case -EPIPE:		/* stall */
> +			usbhid_mark_busy(usbhid);
>  			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
>  			set_bit(HID_CLEAR_HALT, &usbhid->iofl);
>  			schedule_work(&usbhid->reset_work);
> @@ -249,6 +260,7 @@ static void hid_irq_in(struct urb *urb)
>  		case -EPROTO:		/* protocol error or unplug */
>  		case -ETIME:		/* protocol error or unplug */
>  		case -ETIMEDOUT:	/* Should never happen, but... */
> +			usbhid_mark_busy(usbhid);
>  			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
>  			hid_io_error(hid);
>  			return;

At first I thought it would be easier to call usbhid_mark_busy() at 
every URB completion.  But obviously you don't want to do it when an 
URB is unlinked.

> @@ -592,8 +604,21 @@ static int hid_get_class_descriptor(stru
>  int usbhid_open(struct hid_device *hid)
>  {
>  	struct usbhid_device *usbhid = hid->driver_data;
> +	int res;
>  
> -	hid->open++;
> +	mutex_lock(&hid_open_mut);
> +	if (!hid->open++) {
> +		res = usb_autopm_get_interface(usbhid->intf);
> +		/* the device must be awake to reliable request remote wakeup */
> +		if (res < 0) {
> +			hid->open--;
> +			mutex_unlock(&hid_open_mut);
> +			return -EIO;
> +		}
> +		usbhid->intf->needs_remote_wakeup = 1;
> +		usb_autopm_put_interface(usbhid->intf);
> +	}
> +	mutex_unlock(&hid_open_mut);
>  	if (hid_start_in(hid))
>  		hid_io_error(hid);
>  	return 0;

Don't you need to do usb_autopm_get_interface() before hid_start_in()?

> @@ -1154,13 +1184,26 @@ static int hid_suspend(struct usb_interf
>  	struct usbhid_device *usbhid = hid->driver_data;
>  	struct usb_device *udev = interface_to_usbdev(intf);
>  
> -
> -	spin_lock_irq(&usbhid->inlock);
> -	set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
> -	spin_unlock_irq(&usbhid->inlock);
> -	if (usbhid_wait_io(hid) < 0)
> -		return -EIO;
> -
> +	if (udev->auto_pm) {
> +		spin_lock_irq(&usbhid->inlock);	/* Sync with error handler */
> +		if (!test_bit(HID_RESET_PENDING, &usbhid->iofl)
> +		    && !test_bit(HID_CLEAR_HALT, &usbhid->iofl)
> +		    && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)
> +		    && !test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
> +		{
> +			set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
> +			spin_unlock_irq(&usbhid->inlock);
> +		} else {
> +			spin_unlock_irq(&usbhid->inlock);
> +			return -EBUSY;
> +		}
> +	} else {
> +		spin_lock_irq(&usbhid->inlock);
> +		set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
> +		spin_unlock_irq(&usbhid->inlock);
> +		if (usbhid_wait_io(hid) < 0)
> +			return -EIO;
> +	}
>  	del_timer(&usbhid->io_retry);
>  	usb_kill_urb(usbhid->urbin);
>  	flush_scheduled_work();

This might be simpler if you inverted the order of the tests.  That is, 
first get the spinlock, then test for

	udev->auto_pm && (test_bit() || test_bit() || ...)

to see if you need to fail right away.  You probably don't even need to
check udev->auto_pm again before calling usbhid_wait_io.

> @@ -1175,6 +1218,7 @@ static int hid_resume(struct usb_interfa
>  	int status;
>  
>  	clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
> +	usbhid_mark_busy(usbhid);
>  	usbhid->retry_delay = 0;
>  	status = hid_start_in(hid);
>  	usbhid_restart_queues(usbhid);

Again, don't you need to call usb_autopm_get_interface() before 
hid_start_in()?

One last thing, about the race between a last-minute URB completion and 
autosuspend.  The USB autosuspend core routine doesn't check the 
last_busy value after suspending the interface drivers and before 
suspending the device.  Do we need to?

Alan Stern


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.