diff options
Diffstat (limited to 'drivers/usb/usb-skeleton.c')
-rw-r--r-- | drivers/usb/usb-skeleton.c | 41 |
1 files changed, 29 insertions, 12 deletions
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 46929a1b6f24..8432bf171d2e 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c | |||
@@ -34,18 +34,25 @@ static struct usb_device_id skel_table [] = { | |||
34 | }; | 34 | }; |
35 | MODULE_DEVICE_TABLE(usb, skel_table); | 35 | MODULE_DEVICE_TABLE(usb, skel_table); |
36 | 36 | ||
37 | /* to prevent a race between open and disconnect */ | ||
38 | static DEFINE_MUTEX(skel_open_lock); | ||
39 | |||
37 | 40 | ||
38 | /* Get a minor range for your devices from the usb maintainer */ | 41 | /* Get a minor range for your devices from the usb maintainer */ |
39 | #define USB_SKEL_MINOR_BASE 192 | 42 | #define USB_SKEL_MINOR_BASE 192 |
40 | 43 | ||
41 | /* our private defines. if this grows any larger, use your own .h file */ | 44 | /* our private defines. if this grows any larger, use your own .h file */ |
42 | #define MAX_TRANSFER (PAGE_SIZE - 512) | 45 | #define MAX_TRANSFER (PAGE_SIZE - 512) |
46 | /* MAX_TRANSFER is chosen so that the VM is not stressed by | ||
47 | allocations > PAGE_SIZE and the number of packets in a page | ||
48 | is an integer 512 is the largest possible packet on EHCI */ | ||
43 | #define WRITES_IN_FLIGHT 8 | 49 | #define WRITES_IN_FLIGHT 8 |
50 | /* arbitrarily chosen */ | ||
44 | 51 | ||
45 | /* Structure to hold all of our device specific stuff */ | 52 | /* Structure to hold all of our device specific stuff */ |
46 | struct usb_skel { | 53 | struct usb_skel { |
47 | struct usb_device *dev; /* the usb device for this device */ | 54 | struct usb_device *udev; /* the usb device for this device */ |
48 | struct usb_interface *interface; /* the interface for this device */ | 55 | struct usb_interface *interface; /* the interface for this device */ |
49 | struct semaphore limit_sem; /* limiting the number of writes in progress */ | 56 | struct semaphore limit_sem; /* limiting the number of writes in progress */ |
50 | unsigned char *bulk_in_buffer; /* the buffer to receive data */ | 57 | unsigned char *bulk_in_buffer; /* the buffer to receive data */ |
51 | size_t bulk_in_size; /* the size of the receive buffer */ | 58 | size_t bulk_in_size; /* the size of the receive buffer */ |
@@ -76,8 +83,10 @@ static int skel_open(struct inode *inode, struct file *file) | |||
76 | 83 | ||
77 | subminor = iminor(inode); | 84 | subminor = iminor(inode); |
78 | 85 | ||
86 | mutex_lock(&skel_open_lock); | ||
79 | interface = usb_find_interface(&skel_driver, subminor); | 87 | interface = usb_find_interface(&skel_driver, subminor); |
80 | if (!interface) { | 88 | if (!interface) { |
89 | mutex_unlock(&skel_open_lock); | ||
81 | err ("%s - error, can't find device for minor %d", | 90 | err ("%s - error, can't find device for minor %d", |
82 | __FUNCTION__, subminor); | 91 | __FUNCTION__, subminor); |
83 | retval = -ENODEV; | 92 | retval = -ENODEV; |
@@ -86,12 +95,15 @@ static int skel_open(struct inode *inode, struct file *file) | |||
86 | 95 | ||
87 | dev = usb_get_intfdata(interface); | 96 | dev = usb_get_intfdata(interface); |
88 | if (!dev) { | 97 | if (!dev) { |
98 | mutex_unlock(&skel_open_lock); | ||
89 | retval = -ENODEV; | 99 | retval = -ENODEV; |
90 | goto exit; | 100 | goto exit; |
91 | } | 101 | } |
92 | 102 | ||
93 | /* increment our usage count for the device */ | 103 | /* increment our usage count for the device */ |
94 | kref_get(&dev->kref); | 104 | kref_get(&dev->kref); |
105 | /* now we can drop the lock */ | ||
106 | mutex_unlock(&skel_open_lock); | ||
95 | 107 | ||
96 | /* prevent the device from being autosuspended */ | 108 | /* prevent the device from being autosuspended */ |
97 | retval = usb_autopm_get_interface(interface); | 109 | retval = usb_autopm_get_interface(interface); |
@@ -201,12 +213,6 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou | |||
201 | goto exit; | 213 | goto exit; |
202 | } | 214 | } |
203 | 215 | ||
204 | mutex_lock(&dev->io_mutex); | ||
205 | if (!dev->interface) { /* disconnect() was called */ | ||
206 | retval = -ENODEV; | ||
207 | goto error; | ||
208 | } | ||
209 | |||
210 | /* create a urb, and a buffer for it, and copy the data to the urb */ | 216 | /* create a urb, and a buffer for it, and copy the data to the urb */ |
211 | urb = usb_alloc_urb(0, GFP_KERNEL); | 217 | urb = usb_alloc_urb(0, GFP_KERNEL); |
212 | if (!urb) { | 218 | if (!urb) { |
@@ -225,6 +231,14 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou | |||
225 | goto error; | 231 | goto error; |
226 | } | 232 | } |
227 | 233 | ||
234 | /* this lock makes sure we don't submit URBs to gone devices */ | ||
235 | mutex_lock(&dev->io_mutex); | ||
236 | if (!dev->interface) { /* disconnect() was called */ | ||
237 | mutex_unlock(&dev->io_mutex); | ||
238 | retval = -ENODEV; | ||
239 | goto error; | ||
240 | } | ||
241 | |||
228 | /* initialize the urb properly */ | 242 | /* initialize the urb properly */ |
229 | usb_fill_bulk_urb(urb, dev->udev, | 243 | usb_fill_bulk_urb(urb, dev->udev, |
230 | usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), | 244 | usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), |
@@ -233,6 +247,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou | |||
233 | 247 | ||
234 | /* send the data out the bulk port */ | 248 | /* send the data out the bulk port */ |
235 | retval = usb_submit_urb(urb, GFP_KERNEL); | 249 | retval = usb_submit_urb(urb, GFP_KERNEL); |
250 | mutex_unlock(&dev->io_mutex); | ||
236 | if (retval) { | 251 | if (retval) { |
237 | err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); | 252 | err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); |
238 | goto error; | 253 | goto error; |
@@ -241,7 +256,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou | |||
241 | /* release our reference to this urb, the USB core will eventually free it entirely */ | 256 | /* release our reference to this urb, the USB core will eventually free it entirely */ |
242 | usb_free_urb(urb); | 257 | usb_free_urb(urb); |
243 | 258 | ||
244 | mutex_unlock(&dev->io_mutex); | 259 | |
245 | return writesize; | 260 | return writesize; |
246 | 261 | ||
247 | error: | 262 | error: |
@@ -249,7 +264,6 @@ error: | |||
249 | usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); | 264 | usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); |
250 | usb_free_urb(urb); | 265 | usb_free_urb(urb); |
251 | } | 266 | } |
252 | mutex_unlock(&dev->io_mutex); | ||
253 | up(&dev->limit_sem); | 267 | up(&dev->limit_sem); |
254 | 268 | ||
255 | exit: | 269 | exit: |
@@ -344,6 +358,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i | |||
344 | 358 | ||
345 | error: | 359 | error: |
346 | if (dev) | 360 | if (dev) |
361 | /* this frees allocated memory */ | ||
347 | kref_put(&dev->kref, skel_delete); | 362 | kref_put(&dev->kref, skel_delete); |
348 | return retval; | 363 | return retval; |
349 | } | 364 | } |
@@ -354,20 +369,21 @@ static void skel_disconnect(struct usb_interface *interface) | |||
354 | int minor = interface->minor; | 369 | int minor = interface->minor; |
355 | 370 | ||
356 | /* prevent skel_open() from racing skel_disconnect() */ | 371 | /* prevent skel_open() from racing skel_disconnect() */ |
357 | lock_kernel(); | 372 | mutex_lock(&skel_open_lock); |
358 | 373 | ||
359 | dev = usb_get_intfdata(interface); | 374 | dev = usb_get_intfdata(interface); |
360 | usb_set_intfdata(interface, NULL); | 375 | usb_set_intfdata(interface, NULL); |
361 | 376 | ||
362 | /* give back our minor */ | 377 | /* give back our minor */ |
363 | usb_deregister_dev(interface, &skel_class); | 378 | usb_deregister_dev(interface, &skel_class); |
379 | mutex_unlock(&skel_open_lock); | ||
364 | 380 | ||
365 | /* prevent more I/O from starting */ | 381 | /* prevent more I/O from starting */ |
366 | mutex_lock(&dev->io_mutex); | 382 | mutex_lock(&dev->io_mutex); |
367 | dev->interface = NULL; | 383 | dev->interface = NULL; |
368 | mutex_unlock(&dev->io_mutex); | 384 | mutex_unlock(&dev->io_mutex); |
369 | 385 | ||
370 | unlock_kernel(); | 386 | |
371 | 387 | ||
372 | /* decrement our usage count */ | 388 | /* decrement our usage count */ |
373 | kref_put(&dev->kref, skel_delete); | 389 | kref_put(&dev->kref, skel_delete); |
@@ -380,6 +396,7 @@ static struct usb_driver skel_driver = { | |||
380 | .probe = skel_probe, | 396 | .probe = skel_probe, |
381 | .disconnect = skel_disconnect, | 397 | .disconnect = skel_disconnect, |
382 | .id_table = skel_table, | 398 | .id_table = skel_table, |
399 | .supports_autosuspend = 1, | ||
383 | }; | 400 | }; |
384 | 401 | ||
385 | static int __init usb_skel_init(void) | 402 | static int __init usb_skel_init(void) |