Re: autosuspend for hid

Oliver Neukum <[email protected]>
Newsgroups gmane.linux.usb.devel
Organization Novell
Message-ID <[email protected]>
Am Dienstag, 15. Mai 2007 16:37 schrieb Alan Stern:
> Can you break this up into two patches?  If the first adds the queuing 
> code and the second adds autosuspend support, it will be much easier to 
> appraise and test them.

Here's part #2, the core autosuspend.

	Regards
		Oliver
-- 

--- linux-2.6.22-rc1/drivers/hid/usbhid/hid-core.c	2007-05-16 12:23:26.000000000 +0200
+++ linux-2.6.22-rc1-autosuspend/drivers/hid/usbhid/hid-core.c	2007-05-15 19:22:13.000000000 +0200
@@ -64,6 +64,8 @@ MODULE_PARM_DESC(quirks, "Add/modify USB
 /*
  * 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);
@@ -180,6 +182,13 @@ done:
 	spin_unlock_irqrestore(&usbhid->inlock, flags);
 }
 
+static void usbhid_mark_busy(struct usbhid_device *usbhid)
+{
+	struct usb_interface *intf = usbhid->intf;
+
+	usb_mark_last_busy(interface_to_usbdev(intf));
+}
+
 static int usbhid_restart_out_queue(struct usbhid_device *usbhid)
 {
 	struct hid_device *hid = usb_get_intfdata(usbhid->intf);
@@ -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;
@@ -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;
@@ -603,10 +628,13 @@ void usbhid_close(struct hid_device *hid
 {
 	struct usbhid_device *usbhid = hid->driver_data;
 
+	mutex_lock(&hid_open_mut);
 	if (!--hid->open) {
 		usb_kill_urb(usbhid->urbin);
 		flush_scheduled_work();
+		usbhid->intf->needs_remote_wakeup = 0;
 	}
+	mutex_unlock(&hid_open_mut);
 }
 
 /*
@@ -730,7 +758,9 @@ static void __usbhid_restart_queues(stru
 	struct usbhid_device *usbhid =
 		container_of(work, struct usbhid_device, restart_work);
 
+	usb_autopm_get_interface(usbhid->intf);
 	usbhid_restart_queues(usbhid);
+	usb_autopm_put_interface(usbhid->intf);
 }
 
 static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
@@ -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();
@@ -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);
@@ -1216,6 +1260,7 @@ static struct usb_driver hid_driver = {
 	.pre_reset =	hid_pre_reset,
 	.post_reset =	hid_post_reset,
 	.id_table =	hid_usb_ids,
+	.supports_autosuspend = 1,
 };
 
 static int __init hid_init(void)

-------------------------------------------------------------------------
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.