diff options
Diffstat (limited to 'drivers/usb/usb-skeleton.c')
| -rw-r--r-- | drivers/usb/usb-skeleton.c | 101 |
1 files changed, 66 insertions, 35 deletions
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index b362039792b3..1b51d3187a95 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.2 |
| 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 | * |
| @@ -7,9 +7,8 @@ | |||
| 7 | * modify it under the terms of the GNU General Public License as | 7 | * modify it under the terms of the GNU General Public License as |
| 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 */ |
| @@ -32,38 +32,39 @@ static struct usb_device_id skel_table [] = { | |||
| 32 | { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, | 32 | { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, |
| 33 | { } /* Terminating entry */ | 33 | { } /* Terminating entry */ |
| 34 | }; | 34 | }; |
| 35 | MODULE_DEVICE_TABLE (usb, skel_table); | 35 | MODULE_DEVICE_TABLE(usb, skel_table); |
| 36 | 36 | ||
| 37 | 37 | ||
| 38 | /* Get a minor range for your devices from the usb maintainer */ | 38 | /* Get a minor range for your devices from the usb maintainer */ |
| 39 | #define USB_SKEL_MINOR_BASE 192 | 39 | #define USB_SKEL_MINOR_BASE 192 |
| 40 | 40 | ||
| 41 | /* our private defines. if this grows any larger, use your own .h file */ | 41 | /* our private defines. if this grows any larger, use your own .h file */ |
| 42 | #define MAX_TRANSFER ( PAGE_SIZE - 512 ) | 42 | #define MAX_TRANSFER (PAGE_SIZE - 512) |
| 43 | #define WRITES_IN_FLIGHT 8 | 43 | #define WRITES_IN_FLIGHT 8 |
| 44 | 44 | ||
| 45 | /* Structure to hold all of our device specific stuff */ | 45 | /* Structure to hold all of our device specific stuff */ |
| 46 | struct usb_skel { | 46 | struct usb_skel { |
| 47 | struct usb_device * udev; /* the usb device for this device */ | 47 | struct usb_device *dev; /* the usb device for this device */ |
| 48 | struct usb_interface * interface; /* the interface for this device */ | 48 | struct usb_interface *interface; /* the interface for this device */ |
| 49 | struct semaphore limit_sem; /* limiting the number of writes in progress */ | 49 | struct semaphore limit_sem; /* limiting the number of writes in progress */ |
| 50 | unsigned char * bulk_in_buffer; /* the buffer to receive data */ | 50 | unsigned char *bulk_in_buffer; /* the buffer to receive data */ |
| 51 | size_t bulk_in_size; /* the size of the receive buffer */ | 51 | size_t bulk_in_size; /* the size of the receive buffer */ |
| 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 | ||
| 58 | static struct usb_driver skel_driver; | 59 | static struct usb_driver skel_driver; |
| 59 | 60 | ||
| 60 | static void skel_delete(struct kref *kref) | 61 | static void skel_delete(struct kref *kref) |
| 61 | { | 62 | { |
| 62 | struct usb_skel *dev = to_skel_dev(kref); | 63 | struct usb_skel *dev = to_skel_dev(kref); |
| 63 | 64 | ||
| 64 | usb_put_dev(dev->udev); | 65 | usb_put_dev(dev->udev); |
| 65 | kfree (dev->bulk_in_buffer); | 66 | kfree(dev->bulk_in_buffer); |
| 66 | kfree (dev); | 67 | kfree(dev); |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | static int skel_open(struct inode *inode, struct file *file) | 70 | static int skel_open(struct inode *inode, struct file *file) |
| @@ -89,6 +90,11 @@ static int skel_open(struct inode *inode, struct file *file) | |||
| 89 | goto exit; | 90 | goto exit; |
| 90 | } | 91 | } |
| 91 | 92 | ||
| 93 | /* prevent the device from being autosuspended */ | ||
| 94 | retval = usb_autopm_get_interface(interface); | ||
| 95 | if (retval) | ||
| 96 | goto exit; | ||
| 97 | |||
| 92 | /* increment our usage count for the device */ | 98 | /* increment our usage count for the device */ |
| 93 | kref_get(&dev->kref); | 99 | kref_get(&dev->kref); |
| 94 | 100 | ||
| @@ -107,6 +113,12 @@ static int skel_release(struct inode *inode, struct file *file) | |||
| 107 | if (dev == NULL) | 113 | if (dev == NULL) |
| 108 | return -ENODEV; | 114 | return -ENODEV; |
| 109 | 115 | ||
| 116 | /* allow the device to be autosuspended */ | ||
| 117 | mutex_lock(&dev->io_mutex); | ||
| 118 | if (dev->interface) | ||
| 119 | usb_autopm_put_interface(dev->interface); | ||
| 120 | mutex_unlock(&dev->io_mutex); | ||
| 121 | |||
| 110 | /* decrement the count on our device */ | 122 | /* decrement the count on our device */ |
| 111 | kref_put(&dev->kref, skel_delete); | 123 | kref_put(&dev->kref, skel_delete); |
| 112 | return 0; | 124 | return 0; |
| @@ -115,11 +127,17 @@ static int skel_release(struct inode *inode, struct file *file) | |||
| 115 | static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos) | 127 | static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos) |
| 116 | { | 128 | { |
| 117 | struct usb_skel *dev; | 129 | struct usb_skel *dev; |
| 118 | int retval = 0; | 130 | int retval; |
| 119 | int bytes_read; | 131 | int bytes_read; |
| 120 | 132 | ||
| 121 | dev = (struct usb_skel *)file->private_data; | 133 | dev = (struct usb_skel *)file->private_data; |
| 122 | 134 | ||
| 135 | mutex_lock(&dev->io_mutex); | ||
| 136 | if (!dev->interface) { /* disconnect() was called */ | ||
| 137 | retval = -ENODEV; | ||
| 138 | goto exit; | ||
| 139 | } | ||
| 140 | |||
| 123 | /* do a blocking bulk read to get data from the device */ | 141 | /* do a blocking bulk read to get data from the device */ |
| 124 | retval = usb_bulk_msg(dev->udev, | 142 | retval = usb_bulk_msg(dev->udev, |
| 125 | usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), | 143 | usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), |
| @@ -135,6 +153,8 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t * | |||
| 135 | retval = bytes_read; | 153 | retval = bytes_read; |
| 136 | } | 154 | } |
| 137 | 155 | ||
| 156 | exit: | ||
| 157 | mutex_unlock(&dev->io_mutex); | ||
| 138 | return retval; | 158 | return retval; |
| 139 | } | 159 | } |
| 140 | 160 | ||
| @@ -145,16 +165,16 @@ static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs) | |||
| 145 | dev = (struct usb_skel *)urb->context; | 165 | dev = (struct usb_skel *)urb->context; |
| 146 | 166 | ||
| 147 | /* sync/async unlink faults aren't errors */ | 167 | /* sync/async unlink faults aren't errors */ |
| 148 | if (urb->status && | 168 | if (urb->status && |
| 149 | !(urb->status == -ENOENT || | 169 | !(urb->status == -ENOENT || |
| 150 | urb->status == -ECONNRESET || | 170 | urb->status == -ECONNRESET || |
| 151 | urb->status == -ESHUTDOWN)) { | 171 | urb->status == -ESHUTDOWN)) { |
| 152 | dbg("%s - nonzero write bulk status received: %d", | 172 | err("%s - nonzero write bulk status received: %d", |
| 153 | __FUNCTION__, urb->status); | 173 | __FUNCTION__, urb->status); |
| 154 | } | 174 | } |
| 155 | 175 | ||
| 156 | /* free up our allocated buffer */ | 176 | /* free up our allocated buffer */ |
| 157 | usb_buffer_free(urb->dev, urb->transfer_buffer_length, | 177 | usb_buffer_free(urb->dev, urb->transfer_buffer_length, |
| 158 | urb->transfer_buffer, urb->transfer_dma); | 178 | urb->transfer_buffer, urb->transfer_dma); |
| 159 | up(&dev->limit_sem); | 179 | up(&dev->limit_sem); |
| 160 | } | 180 | } |
| @@ -179,6 +199,12 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou | |||
| 179 | goto exit; | 199 | goto exit; |
| 180 | } | 200 | } |
| 181 | 201 | ||
| 202 | mutex_lock(&dev->io_mutex); | ||
| 203 | if (!dev->interface) { /* disconnect() was called */ | ||
| 204 | retval = -ENODEV; | ||
| 205 | goto error; | ||
| 206 | } | ||
| 207 | |||
| 182 | /* create a urb, and a buffer for it, and copy the data to the urb */ | 208 | /* create a urb, and a buffer for it, and copy the data to the urb */ |
| 183 | urb = usb_alloc_urb(0, GFP_KERNEL); | 209 | urb = usb_alloc_urb(0, GFP_KERNEL); |
| 184 | if (!urb) { | 210 | if (!urb) { |
| @@ -213,17 +239,22 @@ 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 */ | 239 | /* release our reference to this urb, the USB core will eventually free it entirely */ |
| 214 | usb_free_urb(urb); | 240 | usb_free_urb(urb); |
| 215 | 241 | ||
| 216 | exit: | 242 | mutex_unlock(&dev->io_mutex); |
| 217 | return writesize; | 243 | return writesize; |
| 218 | 244 | ||
| 219 | error: | 245 | error: |
| 220 | usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); | 246 | if (urb) { |
| 221 | usb_free_urb(urb); | 247 | usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); |
| 248 | usb_free_urb(urb); | ||
| 249 | } | ||
| 250 | mutex_unlock(&dev->io_mutex); | ||
| 222 | up(&dev->limit_sem); | 251 | up(&dev->limit_sem); |
| 252 | |||
| 253 | exit: | ||
| 223 | return retval; | 254 | return retval; |
| 224 | } | 255 | } |
| 225 | 256 | ||
| 226 | static struct file_operations skel_fops = { | 257 | static const struct file_operations skel_fops = { |
| 227 | .owner = THIS_MODULE, | 258 | .owner = THIS_MODULE, |
| 228 | .read = skel_read, | 259 | .read = skel_read, |
| 229 | .write = skel_write, | 260 | .write = skel_write, |
| @@ -231,7 +262,7 @@ static struct file_operations skel_fops = { | |||
| 231 | .release = skel_release, | 262 | .release = skel_release, |
| 232 | }; | 263 | }; |
| 233 | 264 | ||
| 234 | /* | 265 | /* |
| 235 | * usb class driver info in order to get a minor number from the usb core, | 266 | * usb class driver info in order to get a minor number from the usb core, |
| 236 | * and to have the device registered with the driver core | 267 | * and to have the device registered with the driver core |
| 237 | */ | 268 | */ |
| @@ -243,7 +274,7 @@ static struct usb_class_driver skel_class = { | |||
| 243 | 274 | ||
| 244 | static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id) | 275 | static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id) |
| 245 | { | 276 | { |
| 246 | struct usb_skel *dev = NULL; | 277 | struct usb_skel *dev; |
| 247 | struct usb_host_interface *iface_desc; | 278 | struct usb_host_interface *iface_desc; |
| 248 | struct usb_endpoint_descriptor *endpoint; | 279 | struct usb_endpoint_descriptor *endpoint; |
| 249 | size_t buffer_size; | 280 | size_t buffer_size; |
| @@ -252,12 +283,13 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i | |||
| 252 | 283 | ||
| 253 | /* allocate memory for our device state and initialize it */ | 284 | /* allocate memory for our device state and initialize it */ |
| 254 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | 285 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
| 255 | if (dev == NULL) { | 286 | if (!dev) { |
| 256 | err("Out of memory"); | 287 | err("Out of memory"); |
| 257 | goto error; | 288 | goto error; |
| 258 | } | 289 | } |
| 259 | kref_init(&dev->kref); | 290 | kref_init(&dev->kref); |
| 260 | sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); | 291 | sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); |
| 292 | mutex_init(&dev->io_mutex); | ||
| 261 | 293 | ||
| 262 | dev->udev = usb_get_dev(interface_to_usbdev(interface)); | 294 | dev->udev = usb_get_dev(interface_to_usbdev(interface)); |
| 263 | dev->interface = interface; | 295 | dev->interface = interface; |
| @@ -269,10 +301,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i | |||
| 269 | endpoint = &iface_desc->endpoint[i].desc; | 301 | endpoint = &iface_desc->endpoint[i].desc; |
| 270 | 302 | ||
| 271 | if (!dev->bulk_in_endpointAddr && | 303 | if (!dev->bulk_in_endpointAddr && |
| 272 | ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) | 304 | usb_endpoint_is_bulk_in(endpoint)) { |
| 273 | == USB_DIR_IN) && | ||
| 274 | ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) | ||
| 275 | == USB_ENDPOINT_XFER_BULK)) { | ||
| 276 | /* we found a bulk in endpoint */ | 305 | /* we found a bulk in endpoint */ |
| 277 | buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); | 306 | buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); |
| 278 | dev->bulk_in_size = buffer_size; | 307 | dev->bulk_in_size = buffer_size; |
| @@ -285,10 +314,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i | |||
| 285 | } | 314 | } |
| 286 | 315 | ||
| 287 | if (!dev->bulk_out_endpointAddr && | 316 | if (!dev->bulk_out_endpointAddr && |
| 288 | ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) | 317 | usb_endpoint_is_bulk_out(endpoint)) { |
| 289 | == USB_DIR_OUT) && | ||
| 290 | ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) | ||
| 291 | == USB_ENDPOINT_XFER_BULK)) { | ||
| 292 | /* we found a bulk out endpoint */ | 318 | /* we found a bulk out endpoint */ |
| 293 | dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; | 319 | dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; |
| 294 | } | 320 | } |
| @@ -334,6 +360,11 @@ static void skel_disconnect(struct usb_interface *interface) | |||
| 334 | /* give back our minor */ | 360 | /* give back our minor */ |
| 335 | usb_deregister_dev(interface, &skel_class); | 361 | usb_deregister_dev(interface, &skel_class); |
| 336 | 362 | ||
| 363 | /* prevent more I/O from starting */ | ||
| 364 | mutex_lock(&dev->io_mutex); | ||
| 365 | dev->interface = NULL; | ||
| 366 | mutex_unlock(&dev->io_mutex); | ||
| 367 | |||
| 337 | unlock_kernel(); | 368 | unlock_kernel(); |
| 338 | 369 | ||
| 339 | /* decrement our usage count */ | 370 | /* decrement our usage count */ |
| @@ -367,7 +398,7 @@ static void __exit usb_skel_exit(void) | |||
| 367 | usb_deregister(&skel_driver); | 398 | usb_deregister(&skel_driver); |
| 368 | } | 399 | } |
| 369 | 400 | ||
| 370 | module_init (usb_skel_init); | 401 | module_init(usb_skel_init); |
| 371 | module_exit (usb_skel_exit); | 402 | module_exit(usb_skel_exit); |
| 372 | 403 | ||
| 373 | MODULE_LICENSE("GPL"); | 404 | MODULE_LICENSE("GPL"); |
