USB: autosuspend for usblcd

this patch implements autosuspend for the usblcd driver. It uses
the new usb_anchor infrastructure. Many thanks to Georges for testing.

Signed-off-by: Oliver Neukum <oneukum@suse.de>
Cc: Georges Toth <g.toth@e-biz.lu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index 6e093c2..504f722 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -45,6 +45,7 @@
 	struct kref		kref;
 	struct semaphore	limit_sem;		/* to stop writes at full throttle from
 							 * using up all RAM */
+	struct usb_anchor	submitted;		/* URBs to wait for before suspend */
 };
 #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
 
@@ -67,7 +68,7 @@
 {
 	struct usb_lcd *dev;
 	struct usb_interface *interface;
-	int subminor;
+	int subminor, r;
 
 	subminor = iminor(inode);
 
@@ -85,6 +86,13 @@
 	/* increment our usage count for the device */
 	kref_get(&dev->kref);
 
+	/* grab a power reference */
+	r = usb_autopm_get_interface(interface);
+	if (r < 0) {
+		kref_put(&dev->kref, lcd_delete);
+		return r;
+	}
+
 	/* save our object in the file's private structure */
 	file->private_data = dev;
 
@@ -100,6 +108,7 @@
 		return -ENODEV;
 
 	/* decrement the count on our device */
+	usb_autopm_put_interface(dev->interface);
 	kref_put(&dev->kref, lcd_delete);
 	return 0;
 }
@@ -225,12 +234,14 @@
 			  usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
 			  buf, count, lcd_write_bulk_callback, dev);
 	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	usb_anchor_urb(urb, &dev->submitted);
 	
 	/* send the data out the bulk port */
 	retval = usb_submit_urb(urb, GFP_KERNEL);
 	if (retval) {
 		err("USBLCD: %s - failed submitting write urb, error %d", __FUNCTION__, retval);
-		goto error;
+		goto error_unanchor;
 	}
 	
 	/* release our reference to this urb, the USB core will eventually free it entirely */
@@ -238,7 +249,8 @@
 
 exit:
 	return count;
-
+error_unanchor:
+	usb_unanchor_urb(urb);
 error:
 	usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
 	usb_free_urb(urb);
@@ -283,6 +295,7 @@
 	}
 	kref_init(&dev->kref);
 	sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES);
+	init_usb_anchor(&dev->submitted);
 
 	dev->udev = usb_get_dev(interface_to_usbdev(interface));
 	dev->interface = interface;
@@ -350,6 +363,30 @@
 	return retval;
 }
 
+static void lcd_draw_down(struct usb_lcd *dev)
+{
+	int time;
+
+	time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
+	if (!time)
+		usb_kill_anchored_urbs(&dev->submitted);
+}
+
+static int lcd_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_lcd *dev = usb_get_intfdata(intf);
+
+	if (!dev)
+		return 0;
+	lcd_draw_down(dev);
+	return 0;
+}
+
+static int lcd_resume (struct usb_interface *intf)
+{
+	return 0;
+}
+
 static void lcd_disconnect(struct usb_interface *interface)
 {
 	struct usb_lcd *dev;
@@ -371,7 +408,10 @@
 	.name =		"usblcd",
 	.probe =	lcd_probe,
 	.disconnect =	lcd_disconnect,
+	.suspend =	lcd_suspend,
+	.resume =	lcd_resume,
 	.id_table =	id_table,
+	.supports_autosuspend = 1,
 };
 
 static int __init usb_lcd_init(void)