Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7bbe990c authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman
Browse files

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: default avatarOliver Neukum <oneukum@suse.de>
Cc: Georges Toth <g.toth@e-biz.lu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 55b3fd41
Loading
Loading
Loading
Loading
+43 −3
Original line number Diff line number Diff line
@@ -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;
}
@@ -226,11 +235,13 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz
			  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 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
	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)