aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/usb-skeleton.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/usb-skeleton.c')
-rw-r--r--drivers/usb/usb-skeleton.c41
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};
35MODULE_DEVICE_TABLE(usb, skel_table); 35MODULE_DEVICE_TABLE(usb, skel_table);
36 36
37/* to prevent a race between open and disconnect */
38static 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 */
46struct usb_skel { 53struct 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
247error: 262error:
@@ -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
255exit: 269exit:
@@ -344,6 +358,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
344 358
345error: 359error:
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
385static int __init usb_skel_init(void) 402static int __init usb_skel_init(void)