Commit 121e287c authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

usb-skeleton: don't submit URBs after disconnection

This patch (as712b) is a slight revision of one submitted earlier.  It
fixes the usb-skeleton example driver so that it won't try to submit
URBs after skel_disconnect() has returned.  This could cause errors, if
the driver was unbound and then a different driver was bound to the
device.  It also fixes a couple of small bugs in the skel_write()
routine.

The revised patch uses a slightly different test, suggested by Dave
Brownell, for determining whether to free a transfer buffer.  It's a
little clearer than the earlier version.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 349710c3
/* /*
* USB Skeleton driver - 2.0 * USB Skeleton driver - 2.1
* *
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
* *
...@@ -8,8 +8,7 @@ ...@@ -8,8 +8,7 @@
* published by the Free Software Foundation, version 2. * published by the Free Software Foundation, version 2.
* *
* This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
* but has been rewritten to be easy to read and use, as no locks are now * but has been rewritten to be easier to read and use.
* needed anymore.
* *
*/ */
...@@ -21,6 +20,7 @@ ...@@ -21,6 +20,7 @@
#include <linux/kref.h> #include <linux/kref.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/mutex.h>
/* Define these values to match your devices */ /* Define these values to match your devices */
...@@ -52,6 +52,7 @@ struct usb_skel { ...@@ -52,6 +52,7 @@ struct usb_skel {
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
struct kref kref; struct kref kref;
struct mutex io_mutex; /* synchronize I/O with disconnect */
}; };
#define to_skel_dev(d) container_of(d, struct usb_skel, kref) #define to_skel_dev(d) container_of(d, struct usb_skel, kref)
...@@ -119,7 +120,13 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t * ...@@ -119,7 +120,13 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *
int bytes_read; int bytes_read;
dev = (struct usb_skel *)file->private_data; dev = (struct usb_skel *)file->private_data;
mutex_lock(&dev->io_mutex);
if (!dev->interface) { /* disconnect() was called */
retval = -ENODEV;
goto exit;
}
/* do a blocking bulk read to get data from the device */ /* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg(dev->udev, retval = usb_bulk_msg(dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
...@@ -135,6 +142,8 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t * ...@@ -135,6 +142,8 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *
retval = bytes_read; retval = bytes_read;
} }
exit:
mutex_unlock(&dev->io_mutex);
return retval; return retval;
} }
...@@ -179,6 +188,12 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou ...@@ -179,6 +188,12 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
goto exit; goto exit;
} }
mutex_lock(&dev->io_mutex);
if (!dev->interface) { /* disconnect() was called */
retval = -ENODEV;
goto error;
}
/* create a urb, and a buffer for it, and copy the data to the urb */ /* create a urb, and a buffer for it, and copy the data to the urb */
urb = usb_alloc_urb(0, GFP_KERNEL); urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) { if (!urb) {
...@@ -213,13 +228,18 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou ...@@ -213,13 +228,18 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
/* release our reference to this urb, the USB core will eventually free it entirely */ /* release our reference to this urb, the USB core will eventually free it entirely */
usb_free_urb(urb); usb_free_urb(urb);
exit: mutex_unlock(&dev->io_mutex);
return writesize; return writesize;
error: error:
usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); if (urb) {
usb_free_urb(urb); usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
usb_free_urb(urb);
}
mutex_unlock(&dev->io_mutex);
up(&dev->limit_sem); up(&dev->limit_sem);
exit:
return retval; return retval;
} }
...@@ -258,6 +278,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i ...@@ -258,6 +278,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
} }
kref_init(&dev->kref); kref_init(&dev->kref);
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
mutex_init(&dev->io_mutex);
dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface; dev->interface = interface;
...@@ -334,6 +355,11 @@ static void skel_disconnect(struct usb_interface *interface) ...@@ -334,6 +355,11 @@ static void skel_disconnect(struct usb_interface *interface)
/* give back our minor */ /* give back our minor */
usb_deregister_dev(interface, &skel_class); usb_deregister_dev(interface, &skel_class);
/* prevent more I/O from starting */
mutex_lock(&dev->io_mutex);
dev->interface = NULL;
mutex_unlock(&dev->io_mutex);
unlock_kernel(); unlock_kernel();
/* decrement our usage count */ /* decrement our usage count */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment