diff options
| author | Oliver Neukum <oneukum@suse.de> | 2007-05-25 13:42:56 +0200 | 
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-12 16:34:28 -0700 | 
| commit | 403dfb58c3134a339e415fba9f6d45b51c6ee357 (patch) | |
| tree | 65055125aca69e2e8c0d55a4f5221bd8693cfe7b /drivers/usb | |
| parent | 51a2f077c44e559841b09de6da605b4d3ae40dad (diff) | |
| download | olio-linux-3.10-403dfb58c3134a339e415fba9f6d45b51c6ee357.tar.xz olio-linux-3.10-403dfb58c3134a339e415fba9f6d45b51c6ee357.zip  | |
USB: usb-skeleton: usb anchor to implement flush
This patch set introduces usb_anchor and uses it to implement all modern
APIs in the skeleton driver.
- proper error reporting in the skeleton driver
- implementation of flush()
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
| -rw-r--r-- | drivers/usb/usb-skeleton.c | 72 | 
1 files changed, 65 insertions, 7 deletions
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 38f8e4df9dd..a31fcfd5eda 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -51,16 +51,20 @@ struct usb_skel {  	struct usb_device	*udev;			/* the usb device for this device */  	struct usb_interface	*interface;		/* the interface for this device */  	struct semaphore	limit_sem;		/* limiting the number of writes in progress */ +	struct usb_anchor	submitted;		/* in case we need to retract our submissions */  	unsigned char           *bulk_in_buffer;	/* the buffer to receive data */  	size_t			bulk_in_size;		/* the size of the receive buffer */  	__u8			bulk_in_endpointAddr;	/* the address of the bulk in endpoint */  	__u8			bulk_out_endpointAddr;	/* the address of the bulk out endpoint */ +	int			errors;			/* the last request tanked */ +	spinlock_t		err_lock;		/* lock for errors */  	struct kref		kref;  	struct mutex		io_mutex;		/* synchronize I/O with disconnect */  };  #define to_skel_dev(d) container_of(d, struct usb_skel, kref)  static struct usb_driver skel_driver; +static void skel_draw_down(struct usb_skel *dev);  static void skel_delete(struct kref *kref)  { @@ -130,6 +134,30 @@ static int skel_release(struct inode *inode, struct file *file)  	return 0;  } +static int skel_flush(struct file *file, fl_owner_t id) +{ +	struct usb_skel *dev; +	int res; + +	dev = (struct usb_skel *)file->private_data; +	if (dev == NULL) +		return -ENODEV; + +	/* wait for io to stop */ +	mutex_lock(&dev->io_mutex); +	skel_draw_down(dev); + +	/* read out errors, leave subsequent opens a clean slate */ +	spin_lock_irq(&dev->err_lock); +	res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0; +	dev->errors = 0; +	spin_unlock_irq(&dev->err_lock); + +	mutex_unlock(&dev->io_mutex); + +	return res; +} +  static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)  {  	struct usb_skel *dev; @@ -171,12 +199,16 @@ static void skel_write_bulk_callback(struct urb *urb)  	dev = (struct usb_skel *)urb->context;  	/* sync/async unlink faults aren't errors */ -	if (urb->status && -	    !(urb->status == -ENOENT || -	      urb->status == -ECONNRESET || -	      urb->status == -ESHUTDOWN)) { -		err("%s - nonzero write bulk status received: %d", -		    __FUNCTION__, urb->status); +	if (urb->status) { +		if(!(urb->status == -ENOENT || +		    urb->status == -ECONNRESET || +		    urb->status == -ESHUTDOWN)) +			err("%s - nonzero write bulk status received: %d", +			    __FUNCTION__, urb->status); + +		spin_lock(&dev->err_lock); +		dev->errors = urb->status; +		spin_unlock(&dev->err_lock);  	}  	/* free up our allocated buffer */ @@ -205,6 +237,17 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou  		goto exit;  	} +	spin_lock_irq(&dev->err_lock); +	if ((retval = dev->errors) < 0) { +		/* any error is reported once */ +		dev->errors = 0; +		/* to preserve notifications about reset */ +		retval = (retval == -EPIPE) ? retval : -EIO; +	} +	spin_unlock_irq(&dev->err_lock); +	if (retval < 0) +		goto error; +  	/* create a urb, and a buffer for it, and copy the data to the urb */  	urb = usb_alloc_urb(0, GFP_KERNEL);  	if (!urb) { @@ -236,13 +279,14 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou  			  usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),  			  buf, writesize, skel_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);  	mutex_unlock(&dev->io_mutex);  	if (retval) {  		err("%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 */ @@ -251,6 +295,8 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou  	return writesize; +error_unanchor: +	usb_unanchor_urb(urb);  error:  	if (urb) {  		usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); @@ -268,6 +314,7 @@ static const struct file_operations skel_fops = {  	.write =	skel_write,  	.open =		skel_open,  	.release =	skel_release, +	.flush =	skel_flush,  };  /* @@ -298,6 +345,8 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i  	kref_init(&dev->kref);  	sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);  	mutex_init(&dev->io_mutex); +	spin_lock_init(&dev->err_lock); +	init_usb_anchor(&dev->submitted);  	dev->udev = usb_get_dev(interface_to_usbdev(interface));  	dev->interface = interface; @@ -377,6 +426,15 @@ static void skel_disconnect(struct usb_interface *interface)  	info("USB Skeleton #%d now disconnected", minor);  } +static void skel_draw_down(struct usb_skel *dev) +{ +	int time; + +	time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); +	if (!time) +		usb_kill_anchored_urbs(&dev->submitted); +} +  static struct usb_driver skel_driver = {  	.name =		"skeleton",  	.probe =	skel_probe,  |