diff options
Diffstat (limited to 'drivers/usb/usb-skeleton.c')
-rw-r--r-- | drivers/usb/usb-skeleton.c | 72 |
1 files changed, 65 insertions, 7 deletions
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 38f8e4df9dd6..a31fcfd5eda7 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c | |||
@@ -51,16 +51,20 @@ struct usb_skel { | |||
51 | struct usb_device *udev; /* the usb device for this device */ | 51 | struct usb_device *udev; /* the usb device for this device */ |
52 | struct usb_interface *interface; /* the interface for this device */ | 52 | struct usb_interface *interface; /* the interface for this device */ |
53 | struct semaphore limit_sem; /* limiting the number of writes in progress */ | 53 | struct semaphore limit_sem; /* limiting the number of writes in progress */ |
54 | struct usb_anchor submitted; /* in case we need to retract our submissions */ | ||
54 | unsigned char *bulk_in_buffer; /* the buffer to receive data */ | 55 | unsigned char *bulk_in_buffer; /* the buffer to receive data */ |
55 | size_t bulk_in_size; /* the size of the receive buffer */ | 56 | size_t bulk_in_size; /* the size of the receive buffer */ |
56 | __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ | 57 | __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ |
57 | __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ | 58 | __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ |
59 | int errors; /* the last request tanked */ | ||
60 | spinlock_t err_lock; /* lock for errors */ | ||
58 | struct kref kref; | 61 | struct kref kref; |
59 | struct mutex io_mutex; /* synchronize I/O with disconnect */ | 62 | struct mutex io_mutex; /* synchronize I/O with disconnect */ |
60 | }; | 63 | }; |
61 | #define to_skel_dev(d) container_of(d, struct usb_skel, kref) | 64 | #define to_skel_dev(d) container_of(d, struct usb_skel, kref) |
62 | 65 | ||
63 | static struct usb_driver skel_driver; | 66 | static struct usb_driver skel_driver; |
67 | static void skel_draw_down(struct usb_skel *dev); | ||
64 | 68 | ||
65 | static void skel_delete(struct kref *kref) | 69 | static void skel_delete(struct kref *kref) |
66 | { | 70 | { |
@@ -130,6 +134,30 @@ static int skel_release(struct inode *inode, struct file *file) | |||
130 | return 0; | 134 | return 0; |
131 | } | 135 | } |
132 | 136 | ||
137 | static int skel_flush(struct file *file, fl_owner_t id) | ||
138 | { | ||
139 | struct usb_skel *dev; | ||
140 | int res; | ||
141 | |||
142 | dev = (struct usb_skel *)file->private_data; | ||
143 | if (dev == NULL) | ||
144 | return -ENODEV; | ||
145 | |||
146 | /* wait for io to stop */ | ||
147 | mutex_lock(&dev->io_mutex); | ||
148 | skel_draw_down(dev); | ||
149 | |||
150 | /* read out errors, leave subsequent opens a clean slate */ | ||
151 | spin_lock_irq(&dev->err_lock); | ||
152 | res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0; | ||
153 | dev->errors = 0; | ||
154 | spin_unlock_irq(&dev->err_lock); | ||
155 | |||
156 | mutex_unlock(&dev->io_mutex); | ||
157 | |||
158 | return res; | ||
159 | } | ||
160 | |||
133 | static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos) | 161 | static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos) |
134 | { | 162 | { |
135 | struct usb_skel *dev; | 163 | struct usb_skel *dev; |
@@ -171,12 +199,16 @@ static void skel_write_bulk_callback(struct urb *urb) | |||
171 | dev = (struct usb_skel *)urb->context; | 199 | dev = (struct usb_skel *)urb->context; |
172 | 200 | ||
173 | /* sync/async unlink faults aren't errors */ | 201 | /* sync/async unlink faults aren't errors */ |
174 | if (urb->status && | 202 | if (urb->status) { |
175 | !(urb->status == -ENOENT || | 203 | if(!(urb->status == -ENOENT || |
176 | urb->status == -ECONNRESET || | 204 | urb->status == -ECONNRESET || |
177 | urb->status == -ESHUTDOWN)) { | 205 | urb->status == -ESHUTDOWN)) |
178 | err("%s - nonzero write bulk status received: %d", | 206 | err("%s - nonzero write bulk status received: %d", |
179 | __FUNCTION__, urb->status); | 207 | __FUNCTION__, urb->status); |
208 | |||
209 | spin_lock(&dev->err_lock); | ||
210 | dev->errors = urb->status; | ||
211 | spin_unlock(&dev->err_lock); | ||
180 | } | 212 | } |
181 | 213 | ||
182 | /* free up our allocated buffer */ | 214 | /* free up our allocated buffer */ |
@@ -205,6 +237,17 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou | |||
205 | goto exit; | 237 | goto exit; |
206 | } | 238 | } |
207 | 239 | ||
240 | spin_lock_irq(&dev->err_lock); | ||
241 | if ((retval = dev->errors) < 0) { | ||
242 | /* any error is reported once */ | ||
243 | dev->errors = 0; | ||
244 | /* to preserve notifications about reset */ | ||
245 | retval = (retval == -EPIPE) ? retval : -EIO; | ||
246 | } | ||
247 | spin_unlock_irq(&dev->err_lock); | ||
248 | if (retval < 0) | ||
249 | goto error; | ||
250 | |||
208 | /* create a urb, and a buffer for it, and copy the data to the urb */ | 251 | /* create a urb, and a buffer for it, and copy the data to the urb */ |
209 | urb = usb_alloc_urb(0, GFP_KERNEL); | 252 | urb = usb_alloc_urb(0, GFP_KERNEL); |
210 | if (!urb) { | 253 | if (!urb) { |
@@ -236,13 +279,14 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou | |||
236 | usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), | 279 | usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), |
237 | buf, writesize, skel_write_bulk_callback, dev); | 280 | buf, writesize, skel_write_bulk_callback, dev); |
238 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | 281 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
282 | usb_anchor_urb(urb, &dev->submitted); | ||
239 | 283 | ||
240 | /* send the data out the bulk port */ | 284 | /* send the data out the bulk port */ |
241 | retval = usb_submit_urb(urb, GFP_KERNEL); | 285 | retval = usb_submit_urb(urb, GFP_KERNEL); |
242 | mutex_unlock(&dev->io_mutex); | 286 | mutex_unlock(&dev->io_mutex); |
243 | if (retval) { | 287 | if (retval) { |
244 | err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); | 288 | err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); |
245 | goto error; | 289 | goto error_unanchor; |
246 | } | 290 | } |
247 | 291 | ||
248 | /* release our reference to this urb, the USB core will eventually free it entirely */ | 292 | /* release our reference to this urb, the USB core will eventually free it entirely */ |
@@ -251,6 +295,8 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou | |||
251 | 295 | ||
252 | return writesize; | 296 | return writesize; |
253 | 297 | ||
298 | error_unanchor: | ||
299 | usb_unanchor_urb(urb); | ||
254 | error: | 300 | error: |
255 | if (urb) { | 301 | if (urb) { |
256 | usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); | 302 | usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); |
@@ -268,6 +314,7 @@ static const struct file_operations skel_fops = { | |||
268 | .write = skel_write, | 314 | .write = skel_write, |
269 | .open = skel_open, | 315 | .open = skel_open, |
270 | .release = skel_release, | 316 | .release = skel_release, |
317 | .flush = skel_flush, | ||
271 | }; | 318 | }; |
272 | 319 | ||
273 | /* | 320 | /* |
@@ -298,6 +345,8 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i | |||
298 | kref_init(&dev->kref); | 345 | kref_init(&dev->kref); |
299 | sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); | 346 | sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); |
300 | mutex_init(&dev->io_mutex); | 347 | mutex_init(&dev->io_mutex); |
348 | spin_lock_init(&dev->err_lock); | ||
349 | init_usb_anchor(&dev->submitted); | ||
301 | 350 | ||
302 | dev->udev = usb_get_dev(interface_to_usbdev(interface)); | 351 | dev->udev = usb_get_dev(interface_to_usbdev(interface)); |
303 | dev->interface = interface; | 352 | dev->interface = interface; |
@@ -377,6 +426,15 @@ static void skel_disconnect(struct usb_interface *interface) | |||
377 | info("USB Skeleton #%d now disconnected", minor); | 426 | info("USB Skeleton #%d now disconnected", minor); |
378 | } | 427 | } |
379 | 428 | ||
429 | static void skel_draw_down(struct usb_skel *dev) | ||
430 | { | ||
431 | int time; | ||
432 | |||
433 | time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); | ||
434 | if (!time) | ||
435 | usb_kill_anchored_urbs(&dev->submitted); | ||
436 | } | ||
437 | |||
380 | static struct usb_driver skel_driver = { | 438 | static struct usb_driver skel_driver = { |
381 | .name = "skeleton", | 439 | .name = "skeleton", |
382 | .probe = skel_probe, | 440 | .probe = skel_probe, |