From 7bbe990c989ee16f2c1be3e4ae28f8004bec788c Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 13 Jun 2007 17:13:31 +0200 Subject: [PATCH] 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 Cc: Georges Toth Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usblcd.c | 46 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 6e093c2aac2c..504f7221b0d0 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -45,6 +45,7 @@ struct usb_lcd { 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 @@ static int lcd_open(struct inode *inode, struct file *file) { struct usb_lcd *dev; struct usb_interface *interface; - int subminor; + int subminor, r; subminor = iminor(inode); @@ -85,6 +86,13 @@ static int lcd_open(struct inode *inode, struct file *file) /* 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 @@ static int lcd_release(struct inode *inode, struct file *file) 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 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz 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 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz 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 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id } 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 @@ error: 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 @@ static struct usb_driver lcd_driver = { .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)