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) |
