diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2006-07-01 22:06:36 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-09-27 14:58:49 -0400 |
commit | 121e287cb554f3d3402c85a1950d852691b08f5c (patch) | |
tree | fa05e0d622e5f3d339004c4708223a24088ee574 /drivers/usb/usb-skeleton.c | |
parent | 349710c3a79c0405911b8b604953f0c665e17756 (diff) |
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: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/usb-skeleton.c')
-rw-r--r-- | drivers/usb/usb-skeleton.c | 40 |
1 files changed, 33 insertions, 7 deletions
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index b362039792b3..33f0e81c58d3 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * USB Skeleton driver - 2.0 | 2 | * USB Skeleton driver - 2.1 |
3 | * | 3 | * |
4 | * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) | 4 | * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) |
5 | * | 5 | * |
@@ -8,8 +8,7 @@ | |||
8 | * published by the Free Software Foundation, version 2. | 8 | * published by the Free Software Foundation, version 2. |
9 | * | 9 | * |
10 | * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c | 10 | * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c |
11 | * but has been rewritten to be easy to read and use, as no locks are now | 11 | * but has been rewritten to be easier to read and use. |
12 | * needed anymore. | ||
13 | * | 12 | * |
14 | */ | 13 | */ |
15 | 14 | ||
@@ -21,6 +20,7 @@ | |||
21 | #include <linux/kref.h> | 20 | #include <linux/kref.h> |
22 | #include <asm/uaccess.h> | 21 | #include <asm/uaccess.h> |
23 | #include <linux/usb.h> | 22 | #include <linux/usb.h> |
23 | #include <linux/mutex.h> | ||
24 | 24 | ||
25 | 25 | ||
26 | /* Define these values to match your devices */ | 26 | /* Define these values to match your devices */ |
@@ -52,6 +52,7 @@ struct usb_skel { | |||
52 | __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ | 52 | __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ |
53 | __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ | 53 | __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ |
54 | struct kref kref; | 54 | struct kref kref; |
55 | struct mutex io_mutex; /* synchronize I/O with disconnect */ | ||
55 | }; | 56 | }; |
56 | #define to_skel_dev(d) container_of(d, struct usb_skel, kref) | 57 | #define to_skel_dev(d) container_of(d, struct usb_skel, kref) |
57 | 58 | ||
@@ -119,7 +120,13 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t * | |||
119 | int bytes_read; | 120 | int bytes_read; |
120 | 121 | ||
121 | dev = (struct usb_skel *)file->private_data; | 122 | dev = (struct usb_skel *)file->private_data; |
122 | 123 | ||
124 | mutex_lock(&dev->io_mutex); | ||
125 | if (!dev->interface) { /* disconnect() was called */ | ||
126 | retval = -ENODEV; | ||
127 | goto exit; | ||
128 | } | ||
129 | |||
123 | /* do a blocking bulk read to get data from the device */ | 130 | /* do a blocking bulk read to get data from the device */ |
124 | retval = usb_bulk_msg(dev->udev, | 131 | retval = usb_bulk_msg(dev->udev, |
125 | usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), | 132 | 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 | retval = bytes_read; | 142 | retval = bytes_read; |
136 | } | 143 | } |
137 | 144 | ||
145 | exit: | ||
146 | mutex_unlock(&dev->io_mutex); | ||
138 | return retval; | 147 | return retval; |
139 | } | 148 | } |
140 | 149 | ||
@@ -179,6 +188,12 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou | |||
179 | goto exit; | 188 | goto exit; |
180 | } | 189 | } |
181 | 190 | ||
191 | mutex_lock(&dev->io_mutex); | ||
192 | if (!dev->interface) { /* disconnect() was called */ | ||
193 | retval = -ENODEV; | ||
194 | goto error; | ||
195 | } | ||
196 | |||
182 | /* create a urb, and a buffer for it, and copy the data to the urb */ | 197 | /* create a urb, and a buffer for it, and copy the data to the urb */ |
183 | urb = usb_alloc_urb(0, GFP_KERNEL); | 198 | urb = usb_alloc_urb(0, GFP_KERNEL); |
184 | if (!urb) { | 199 | if (!urb) { |
@@ -213,13 +228,18 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou | |||
213 | /* release our reference to this urb, the USB core will eventually free it entirely */ | 228 | /* release our reference to this urb, the USB core will eventually free it entirely */ |
214 | usb_free_urb(urb); | 229 | usb_free_urb(urb); |
215 | 230 | ||
216 | exit: | 231 | mutex_unlock(&dev->io_mutex); |
217 | return writesize; | 232 | return writesize; |
218 | 233 | ||
219 | error: | 234 | error: |
220 | usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); | 235 | if (urb) { |
221 | usb_free_urb(urb); | 236 | usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); |
237 | usb_free_urb(urb); | ||
238 | } | ||
239 | mutex_unlock(&dev->io_mutex); | ||
222 | up(&dev->limit_sem); | 240 | up(&dev->limit_sem); |
241 | |||
242 | exit: | ||
223 | return retval; | 243 | return retval; |
224 | } | 244 | } |
225 | 245 | ||
@@ -258,6 +278,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i | |||
258 | } | 278 | } |
259 | kref_init(&dev->kref); | 279 | kref_init(&dev->kref); |
260 | sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); | 280 | sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); |
281 | mutex_init(&dev->io_mutex); | ||
261 | 282 | ||
262 | dev->udev = usb_get_dev(interface_to_usbdev(interface)); | 283 | dev->udev = usb_get_dev(interface_to_usbdev(interface)); |
263 | dev->interface = interface; | 284 | dev->interface = interface; |
@@ -334,6 +355,11 @@ static void skel_disconnect(struct usb_interface *interface) | |||
334 | /* give back our minor */ | 355 | /* give back our minor */ |
335 | usb_deregister_dev(interface, &skel_class); | 356 | usb_deregister_dev(interface, &skel_class); |
336 | 357 | ||
358 | /* prevent more I/O from starting */ | ||
359 | mutex_lock(&dev->io_mutex); | ||
360 | dev->interface = NULL; | ||
361 | mutex_unlock(&dev->io_mutex); | ||
362 | |||
337 | unlock_kernel(); | 363 | unlock_kernel(); |
338 | 364 | ||
339 | /* decrement our usage count */ | 365 | /* decrement our usage count */ |