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