diff options
author | Pete Zaitcev <zaitcev@redhat.com> | 2007-10-31 18:59:30 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-11-28 16:58:34 -0500 |
commit | f08812d5eb8f8cd1a5bd5f5c26a96eb93d97ab69 (patch) | |
tree | 1e71713fca245258755a0f99f58d0b9343424ee4 /drivers/usb/misc | |
parent | 3c886c50486cfbef2a2382b99bf4083a465c970a (diff) |
USB: FIx locks and urb->status in adutux (updated)
Two main issues fixed here are:
- An improper use of in-struct lock to protect an open count
- Use of urb status for -EINPROGRESS
Also, along the way:
- Change usb_unlink_urb to usb_kill_urb. Apparently there's no need
to use usb_unlink_urb whatsoever in this driver, and the old use of
usb_kill_urb was outright racy (it unlinked and immediately freed).
- Fix indentation in adu_write. Looks like it was damaged by a script.
- Vitaly wants -EBUSY on multiply opens.
- bInterval was taken from a wrong endpoint.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Vitaliy Ivanov <vitalivanov@gmail.com>
Tested-by: Vitaliy Ivanov <vitalivanov@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/misc')
-rw-r--r-- | drivers/usb/misc/adutux.c | 262 |
1 files changed, 139 insertions, 123 deletions
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index c567aa7a41ea..5a2c44e4c1f7 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c | |||
@@ -79,12 +79,22 @@ MODULE_DEVICE_TABLE(usb, device_table); | |||
79 | 79 | ||
80 | #define COMMAND_TIMEOUT (2*HZ) /* 60 second timeout for a command */ | 80 | #define COMMAND_TIMEOUT (2*HZ) /* 60 second timeout for a command */ |
81 | 81 | ||
82 | /* | ||
83 | * The locking scheme is a vanilla 3-lock: | ||
84 | * adu_device.buflock: A spinlock, covers what IRQs touch. | ||
85 | * adutux_mutex: A Static lock to cover open_count. It would also cover | ||
86 | * any globals, but we don't have them in 2.6. | ||
87 | * adu_device.mtx: A mutex to hold across sleepers like copy_from_user. | ||
88 | * It covers all of adu_device, except the open_count | ||
89 | * and what .buflock covers. | ||
90 | */ | ||
91 | |||
82 | /* Structure to hold all of our device specific stuff */ | 92 | /* Structure to hold all of our device specific stuff */ |
83 | struct adu_device { | 93 | struct adu_device { |
84 | struct mutex mtx; /* locks this structure */ | 94 | struct mutex mtx; |
85 | struct usb_device* udev; /* save off the usb device pointer */ | 95 | struct usb_device* udev; /* save off the usb device pointer */ |
86 | struct usb_interface* interface; | 96 | struct usb_interface* interface; |
87 | unsigned char minor; /* the starting minor number for this device */ | 97 | unsigned int minor; /* the starting minor number for this device */ |
88 | char serial_number[8]; | 98 | char serial_number[8]; |
89 | 99 | ||
90 | int open_count; /* number of times this port has been opened */ | 100 | int open_count; /* number of times this port has been opened */ |
@@ -107,8 +117,11 @@ struct adu_device { | |||
107 | char* interrupt_out_buffer; | 117 | char* interrupt_out_buffer; |
108 | struct usb_endpoint_descriptor* interrupt_out_endpoint; | 118 | struct usb_endpoint_descriptor* interrupt_out_endpoint; |
109 | struct urb* interrupt_out_urb; | 119 | struct urb* interrupt_out_urb; |
120 | int out_urb_finished; | ||
110 | }; | 121 | }; |
111 | 122 | ||
123 | static DEFINE_MUTEX(adutux_mutex); | ||
124 | |||
112 | static struct usb_driver adu_driver; | 125 | static struct usb_driver adu_driver; |
113 | 126 | ||
114 | static void adu_debug_data(int level, const char *function, int size, | 127 | static void adu_debug_data(int level, const char *function, int size, |
@@ -132,27 +145,31 @@ static void adu_debug_data(int level, const char *function, int size, | |||
132 | */ | 145 | */ |
133 | static void adu_abort_transfers(struct adu_device *dev) | 146 | static void adu_abort_transfers(struct adu_device *dev) |
134 | { | 147 | { |
135 | dbg(2," %s : enter", __FUNCTION__); | 148 | unsigned long flags; |
136 | 149 | ||
137 | if (dev == NULL) { | 150 | dbg(2," %s : enter", __FUNCTION__); |
138 | dbg(1," %s : dev is null", __FUNCTION__); | ||
139 | goto exit; | ||
140 | } | ||
141 | 151 | ||
142 | if (dev->udev == NULL) { | 152 | if (dev->udev == NULL) { |
143 | dbg(1," %s : udev is null", __FUNCTION__); | 153 | dbg(1," %s : udev is null", __FUNCTION__); |
144 | goto exit; | 154 | goto exit; |
145 | } | 155 | } |
146 | 156 | ||
147 | dbg(2," %s : udev state %d", __FUNCTION__, dev->udev->state); | ||
148 | if (dev->udev->state == USB_STATE_NOTATTACHED) { | ||
149 | dbg(1," %s : udev is not attached", __FUNCTION__); | ||
150 | goto exit; | ||
151 | } | ||
152 | |||
153 | /* shutdown transfer */ | 157 | /* shutdown transfer */ |
154 | usb_unlink_urb(dev->interrupt_in_urb); | 158 | |
155 | usb_unlink_urb(dev->interrupt_out_urb); | 159 | /* XXX Anchor these instead */ |
160 | spin_lock_irqsave(&dev->buflock, flags); | ||
161 | if (!dev->read_urb_finished) { | ||
162 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
163 | usb_kill_urb(dev->interrupt_in_urb); | ||
164 | } else | ||
165 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
166 | |||
167 | spin_lock_irqsave(&dev->buflock, flags); | ||
168 | if (!dev->out_urb_finished) { | ||
169 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
170 | usb_kill_urb(dev->interrupt_out_urb); | ||
171 | } else | ||
172 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
156 | 173 | ||
157 | exit: | 174 | exit: |
158 | dbg(2," %s : leave", __FUNCTION__); | 175 | dbg(2," %s : leave", __FUNCTION__); |
@@ -162,8 +179,6 @@ static void adu_delete(struct adu_device *dev) | |||
162 | { | 179 | { |
163 | dbg(2, "%s enter", __FUNCTION__); | 180 | dbg(2, "%s enter", __FUNCTION__); |
164 | 181 | ||
165 | adu_abort_transfers(dev); | ||
166 | |||
167 | /* free data structures */ | 182 | /* free data structures */ |
168 | usb_free_urb(dev->interrupt_in_urb); | 183 | usb_free_urb(dev->interrupt_in_urb); |
169 | usb_free_urb(dev->interrupt_out_urb); | 184 | usb_free_urb(dev->interrupt_out_urb); |
@@ -239,7 +254,10 @@ static void adu_interrupt_out_callback(struct urb *urb) | |||
239 | goto exit; | 254 | goto exit; |
240 | } | 255 | } |
241 | 256 | ||
242 | wake_up_interruptible(&dev->write_wait); | 257 | spin_lock(&dev->buflock); |
258 | dev->out_urb_finished = 1; | ||
259 | wake_up(&dev->write_wait); | ||
260 | spin_unlock(&dev->buflock); | ||
243 | exit: | 261 | exit: |
244 | 262 | ||
245 | adu_debug_data(5, __FUNCTION__, urb->actual_length, | 263 | adu_debug_data(5, __FUNCTION__, urb->actual_length, |
@@ -252,12 +270,17 @@ static int adu_open(struct inode *inode, struct file *file) | |||
252 | struct adu_device *dev = NULL; | 270 | struct adu_device *dev = NULL; |
253 | struct usb_interface *interface; | 271 | struct usb_interface *interface; |
254 | int subminor; | 272 | int subminor; |
255 | int retval = 0; | 273 | int retval; |
256 | 274 | ||
257 | dbg(2,"%s : enter", __FUNCTION__); | 275 | dbg(2,"%s : enter", __FUNCTION__); |
258 | 276 | ||
259 | subminor = iminor(inode); | 277 | subminor = iminor(inode); |
260 | 278 | ||
279 | if ((retval = mutex_lock_interruptible(&adutux_mutex))) { | ||
280 | dbg(2, "%s : mutex lock failed", __FUNCTION__); | ||
281 | goto exit_no_lock; | ||
282 | } | ||
283 | |||
261 | interface = usb_find_interface(&adu_driver, subminor); | 284 | interface = usb_find_interface(&adu_driver, subminor); |
262 | if (!interface) { | 285 | if (!interface) { |
263 | err("%s - error, can't find device for minor %d", | 286 | err("%s - error, can't find device for minor %d", |
@@ -267,54 +290,54 @@ static int adu_open(struct inode *inode, struct file *file) | |||
267 | } | 290 | } |
268 | 291 | ||
269 | dev = usb_get_intfdata(interface); | 292 | dev = usb_get_intfdata(interface); |
270 | if (!dev) { | 293 | if (!dev || !dev->udev) { |
271 | retval = -ENODEV; | 294 | retval = -ENODEV; |
272 | goto exit_no_device; | 295 | goto exit_no_device; |
273 | } | 296 | } |
274 | 297 | ||
275 | /* lock this device */ | 298 | /* check that nobody else is using the device */ |
276 | if ((retval = mutex_lock_interruptible(&dev->mtx))) { | 299 | if (dev->open_count) { |
277 | dbg(2, "%s : mutex lock failed", __FUNCTION__); | 300 | retval = -EBUSY; |
278 | goto exit_no_device; | 301 | goto exit_no_device; |
279 | } | 302 | } |
280 | 303 | ||
281 | /* increment our usage count for the device */ | ||
282 | ++dev->open_count; | 304 | ++dev->open_count; |
283 | dbg(2,"%s : open count %d", __FUNCTION__, dev->open_count); | 305 | dbg(2,"%s : open count %d", __FUNCTION__, dev->open_count); |
284 | 306 | ||
285 | /* save device in the file's private structure */ | 307 | /* save device in the file's private structure */ |
286 | file->private_data = dev; | 308 | file->private_data = dev; |
287 | 309 | ||
288 | if (dev->open_count == 1) { | 310 | /* initialize in direction */ |
289 | /* initialize in direction */ | 311 | dev->read_buffer_length = 0; |
290 | dev->read_buffer_length = 0; | ||
291 | 312 | ||
292 | /* fixup first read by having urb waiting for it */ | 313 | /* fixup first read by having urb waiting for it */ |
293 | usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, | 314 | usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, |
294 | usb_rcvintpipe(dev->udev, | 315 | usb_rcvintpipe(dev->udev, |
295 | dev->interrupt_in_endpoint->bEndpointAddress), | 316 | dev->interrupt_in_endpoint->bEndpointAddress), |
296 | dev->interrupt_in_buffer, | 317 | dev->interrupt_in_buffer, |
297 | le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), | 318 | le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), |
298 | adu_interrupt_in_callback, dev, | 319 | adu_interrupt_in_callback, dev, |
299 | dev->interrupt_in_endpoint->bInterval); | 320 | dev->interrupt_in_endpoint->bInterval); |
300 | /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */ | 321 | dev->read_urb_finished = 0; |
301 | dev->read_urb_finished = 0; | 322 | if (usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL)) |
302 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); | 323 | dev->read_urb_finished = 1; |
303 | if (retval) | 324 | /* we ignore failure */ |
304 | --dev->open_count; | 325 | /* end of fixup for first read */ |
305 | } | 326 | |
306 | mutex_unlock(&dev->mtx); | 327 | /* initialize out direction */ |
328 | dev->out_urb_finished = 1; | ||
329 | |||
330 | retval = 0; | ||
307 | 331 | ||
308 | exit_no_device: | 332 | exit_no_device: |
333 | mutex_unlock(&adutux_mutex); | ||
334 | exit_no_lock: | ||
309 | dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval); | 335 | dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval); |
310 | |||
311 | return retval; | 336 | return retval; |
312 | } | 337 | } |
313 | 338 | ||
314 | static int adu_release_internal(struct adu_device *dev) | 339 | static void adu_release_internal(struct adu_device *dev) |
315 | { | 340 | { |
316 | int retval = 0; | ||
317 | |||
318 | dbg(2," %s : enter", __FUNCTION__); | 341 | dbg(2," %s : enter", __FUNCTION__); |
319 | 342 | ||
320 | /* decrement our usage count for the device */ | 343 | /* decrement our usage count for the device */ |
@@ -326,12 +349,11 @@ static int adu_release_internal(struct adu_device *dev) | |||
326 | } | 349 | } |
327 | 350 | ||
328 | dbg(2," %s : leave", __FUNCTION__); | 351 | dbg(2," %s : leave", __FUNCTION__); |
329 | return retval; | ||
330 | } | 352 | } |
331 | 353 | ||
332 | static int adu_release(struct inode *inode, struct file *file) | 354 | static int adu_release(struct inode *inode, struct file *file) |
333 | { | 355 | { |
334 | struct adu_device *dev = NULL; | 356 | struct adu_device *dev; |
335 | int retval = 0; | 357 | int retval = 0; |
336 | 358 | ||
337 | dbg(2," %s : enter", __FUNCTION__); | 359 | dbg(2," %s : enter", __FUNCTION__); |
@@ -343,15 +365,13 @@ static int adu_release(struct inode *inode, struct file *file) | |||
343 | } | 365 | } |
344 | 366 | ||
345 | dev = file->private_data; | 367 | dev = file->private_data; |
346 | |||
347 | if (dev == NULL) { | 368 | if (dev == NULL) { |
348 | dbg(1," %s : object is NULL", __FUNCTION__); | 369 | dbg(1," %s : object is NULL", __FUNCTION__); |
349 | retval = -ENODEV; | 370 | retval = -ENODEV; |
350 | goto exit; | 371 | goto exit; |
351 | } | 372 | } |
352 | 373 | ||
353 | /* lock our device */ | 374 | mutex_lock(&adutux_mutex); /* not interruptible */ |
354 | mutex_lock(&dev->mtx); /* not interruptible */ | ||
355 | 375 | ||
356 | if (dev->open_count <= 0) { | 376 | if (dev->open_count <= 0) { |
357 | dbg(1," %s : device not opened", __FUNCTION__); | 377 | dbg(1," %s : device not opened", __FUNCTION__); |
@@ -359,19 +379,15 @@ static int adu_release(struct inode *inode, struct file *file) | |||
359 | goto exit; | 379 | goto exit; |
360 | } | 380 | } |
361 | 381 | ||
382 | adu_release_internal(dev); | ||
362 | if (dev->udev == NULL) { | 383 | if (dev->udev == NULL) { |
363 | /* the device was unplugged before the file was released */ | 384 | /* the device was unplugged before the file was released */ |
364 | mutex_unlock(&dev->mtx); | 385 | if (!dev->open_count) /* ... and we're the last user */ |
365 | adu_delete(dev); | 386 | adu_delete(dev); |
366 | dev = NULL; | ||
367 | } else { | ||
368 | /* do the work */ | ||
369 | retval = adu_release_internal(dev); | ||
370 | } | 387 | } |
371 | 388 | ||
372 | exit: | 389 | exit: |
373 | if (dev) | 390 | mutex_unlock(&adutux_mutex); |
374 | mutex_unlock(&dev->mtx); | ||
375 | dbg(2," %s : leave, return value %d", __FUNCTION__, retval); | 391 | dbg(2," %s : leave, return value %d", __FUNCTION__, retval); |
376 | return retval; | 392 | return retval; |
377 | } | 393 | } |
@@ -393,12 +409,12 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, | |||
393 | 409 | ||
394 | dev = file->private_data; | 410 | dev = file->private_data; |
395 | dbg(2," %s : dev=%p", __FUNCTION__, dev); | 411 | dbg(2," %s : dev=%p", __FUNCTION__, dev); |
396 | /* lock this object */ | 412 | |
397 | if (mutex_lock_interruptible(&dev->mtx)) | 413 | if (mutex_lock_interruptible(&dev->mtx)) |
398 | return -ERESTARTSYS; | 414 | return -ERESTARTSYS; |
399 | 415 | ||
400 | /* verify that the device wasn't unplugged */ | 416 | /* verify that the device wasn't unplugged */ |
401 | if (dev->udev == NULL || dev->minor == 0) { | 417 | if (dev->udev == NULL) { |
402 | retval = -ENODEV; | 418 | retval = -ENODEV; |
403 | err("No device or device unplugged %d", retval); | 419 | err("No device or device unplugged %d", retval); |
404 | goto exit; | 420 | goto exit; |
@@ -452,7 +468,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, | |||
452 | should_submit = 1; | 468 | should_submit = 1; |
453 | } else { | 469 | } else { |
454 | /* even the primary was empty - we may need to do IO */ | 470 | /* even the primary was empty - we may need to do IO */ |
455 | if (dev->interrupt_in_urb->status == -EINPROGRESS) { | 471 | if (!dev->read_urb_finished) { |
456 | /* somebody is doing IO */ | 472 | /* somebody is doing IO */ |
457 | spin_unlock_irqrestore(&dev->buflock, flags); | 473 | spin_unlock_irqrestore(&dev->buflock, flags); |
458 | dbg(2," %s : submitted already", __FUNCTION__); | 474 | dbg(2," %s : submitted already", __FUNCTION__); |
@@ -460,6 +476,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, | |||
460 | /* we must initiate input */ | 476 | /* we must initiate input */ |
461 | dbg(2," %s : initiate input", __FUNCTION__); | 477 | dbg(2," %s : initiate input", __FUNCTION__); |
462 | dev->read_urb_finished = 0; | 478 | dev->read_urb_finished = 0; |
479 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
463 | 480 | ||
464 | usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, | 481 | usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, |
465 | usb_rcvintpipe(dev->udev, | 482 | usb_rcvintpipe(dev->udev, |
@@ -469,15 +486,12 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, | |||
469 | adu_interrupt_in_callback, | 486 | adu_interrupt_in_callback, |
470 | dev, | 487 | dev, |
471 | dev->interrupt_in_endpoint->bInterval); | 488 | dev->interrupt_in_endpoint->bInterval); |
472 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); | 489 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); |
473 | if (!retval) { | 490 | if (retval) { |
474 | spin_unlock_irqrestore(&dev->buflock, flags); | 491 | dev->read_urb_finished = 1; |
475 | dbg(2," %s : submitted OK", __FUNCTION__); | ||
476 | } else { | ||
477 | if (retval == -ENOMEM) { | 492 | if (retval == -ENOMEM) { |
478 | retval = bytes_read ? bytes_read : -ENOMEM; | 493 | retval = bytes_read ? bytes_read : -ENOMEM; |
479 | } | 494 | } |
480 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
481 | dbg(2," %s : submit failed", __FUNCTION__); | 495 | dbg(2," %s : submit failed", __FUNCTION__); |
482 | goto exit; | 496 | goto exit; |
483 | } | 497 | } |
@@ -486,10 +500,14 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, | |||
486 | /* we wait for I/O to complete */ | 500 | /* we wait for I/O to complete */ |
487 | set_current_state(TASK_INTERRUPTIBLE); | 501 | set_current_state(TASK_INTERRUPTIBLE); |
488 | add_wait_queue(&dev->read_wait, &wait); | 502 | add_wait_queue(&dev->read_wait, &wait); |
489 | if (!dev->read_urb_finished) | 503 | spin_lock_irqsave(&dev->buflock, flags); |
504 | if (!dev->read_urb_finished) { | ||
505 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
490 | timeout = schedule_timeout(COMMAND_TIMEOUT); | 506 | timeout = schedule_timeout(COMMAND_TIMEOUT); |
491 | else | 507 | } else { |
508 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
492 | set_current_state(TASK_RUNNING); | 509 | set_current_state(TASK_RUNNING); |
510 | } | ||
493 | remove_wait_queue(&dev->read_wait, &wait); | 511 | remove_wait_queue(&dev->read_wait, &wait); |
494 | 512 | ||
495 | if (timeout <= 0) { | 513 | if (timeout <= 0) { |
@@ -509,19 +527,23 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, | |||
509 | 527 | ||
510 | retval = bytes_read; | 528 | retval = bytes_read; |
511 | /* if the primary buffer is empty then use it */ | 529 | /* if the primary buffer is empty then use it */ |
512 | if (should_submit && !dev->interrupt_in_urb->status==-EINPROGRESS) { | 530 | spin_lock_irqsave(&dev->buflock, flags); |
531 | if (should_submit && dev->read_urb_finished) { | ||
532 | dev->read_urb_finished = 0; | ||
533 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
513 | usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, | 534 | usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, |
514 | usb_rcvintpipe(dev->udev, | 535 | usb_rcvintpipe(dev->udev, |
515 | dev->interrupt_in_endpoint->bEndpointAddress), | 536 | dev->interrupt_in_endpoint->bEndpointAddress), |
516 | dev->interrupt_in_buffer, | 537 | dev->interrupt_in_buffer, |
517 | le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), | 538 | le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), |
518 | adu_interrupt_in_callback, | 539 | adu_interrupt_in_callback, |
519 | dev, | 540 | dev, |
520 | dev->interrupt_in_endpoint->bInterval); | 541 | dev->interrupt_in_endpoint->bInterval); |
521 | /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */ | 542 | if (usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL) != 0) |
522 | dev->read_urb_finished = 0; | 543 | dev->read_urb_finished = 1; |
523 | usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); | ||
524 | /* we ignore failure */ | 544 | /* we ignore failure */ |
545 | } else { | ||
546 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
525 | } | 547 | } |
526 | 548 | ||
527 | exit: | 549 | exit: |
@@ -535,24 +557,24 @@ exit: | |||
535 | static ssize_t adu_write(struct file *file, const __user char *buffer, | 557 | static ssize_t adu_write(struct file *file, const __user char *buffer, |
536 | size_t count, loff_t *ppos) | 558 | size_t count, loff_t *ppos) |
537 | { | 559 | { |
560 | DECLARE_WAITQUEUE(waita, current); | ||
538 | struct adu_device *dev; | 561 | struct adu_device *dev; |
539 | size_t bytes_written = 0; | 562 | size_t bytes_written = 0; |
540 | size_t bytes_to_write; | 563 | size_t bytes_to_write; |
541 | size_t buffer_size; | 564 | size_t buffer_size; |
565 | unsigned long flags; | ||
542 | int retval; | 566 | int retval; |
543 | int timeout = 0; | ||
544 | 567 | ||
545 | dbg(2," %s : enter, count = %Zd", __FUNCTION__, count); | 568 | dbg(2," %s : enter, count = %Zd", __FUNCTION__, count); |
546 | 569 | ||
547 | dev = file->private_data; | 570 | dev = file->private_data; |
548 | 571 | ||
549 | /* lock this object */ | ||
550 | retval = mutex_lock_interruptible(&dev->mtx); | 572 | retval = mutex_lock_interruptible(&dev->mtx); |
551 | if (retval) | 573 | if (retval) |
552 | goto exit_nolock; | 574 | goto exit_nolock; |
553 | 575 | ||
554 | /* verify that the device wasn't unplugged */ | 576 | /* verify that the device wasn't unplugged */ |
555 | if (dev->udev == NULL || dev->minor == 0) { | 577 | if (dev->udev == NULL) { |
556 | retval = -ENODEV; | 578 | retval = -ENODEV; |
557 | err("No device or device unplugged %d", retval); | 579 | err("No device or device unplugged %d", retval); |
558 | goto exit; | 580 | goto exit; |
@@ -564,42 +586,37 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, | |||
564 | goto exit; | 586 | goto exit; |
565 | } | 587 | } |
566 | 588 | ||
567 | |||
568 | while (count > 0) { | 589 | while (count > 0) { |
569 | if (dev->interrupt_out_urb->status == -EINPROGRESS) { | 590 | add_wait_queue(&dev->write_wait, &waita); |
570 | timeout = COMMAND_TIMEOUT; | 591 | set_current_state(TASK_INTERRUPTIBLE); |
592 | spin_lock_irqsave(&dev->buflock, flags); | ||
593 | if (!dev->out_urb_finished) { | ||
594 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
571 | 595 | ||
572 | while (timeout > 0) { | 596 | mutex_unlock(&dev->mtx); |
573 | if (signal_pending(current)) { | 597 | if (signal_pending(current)) { |
574 | dbg(1," %s : interrupted", __FUNCTION__); | 598 | dbg(1," %s : interrupted", __FUNCTION__); |
599 | set_current_state(TASK_RUNNING); | ||
575 | retval = -EINTR; | 600 | retval = -EINTR; |
576 | goto exit; | 601 | goto exit_onqueue; |
577 | } | 602 | } |
578 | mutex_unlock(&dev->mtx); | 603 | if (schedule_timeout(COMMAND_TIMEOUT) == 0) { |
579 | timeout = interruptible_sleep_on_timeout(&dev->write_wait, timeout); | 604 | dbg(1, "%s - command timed out.", __FUNCTION__); |
605 | retval = -ETIMEDOUT; | ||
606 | goto exit_onqueue; | ||
607 | } | ||
608 | remove_wait_queue(&dev->write_wait, &waita); | ||
580 | retval = mutex_lock_interruptible(&dev->mtx); | 609 | retval = mutex_lock_interruptible(&dev->mtx); |
581 | if (retval) { | 610 | if (retval) { |
582 | retval = bytes_written ? bytes_written : retval; | 611 | retval = bytes_written ? bytes_written : retval; |
583 | goto exit_nolock; | 612 | goto exit_nolock; |
584 | } | 613 | } |
585 | if (timeout > 0) { | ||
586 | break; | ||
587 | } | ||
588 | dbg(1," %s : interrupted timeout: %d", __FUNCTION__, timeout); | ||
589 | } | ||
590 | |||
591 | |||
592 | dbg(1," %s : final timeout: %d", __FUNCTION__, timeout); | ||
593 | |||
594 | if (timeout == 0) { | ||
595 | dbg(1, "%s - command timed out.", __FUNCTION__); | ||
596 | retval = -ETIMEDOUT; | ||
597 | goto exit; | ||
598 | } | ||
599 | |||
600 | dbg(4," %s : in progress, count = %Zd", __FUNCTION__, count); | ||
601 | 614 | ||
615 | dbg(4," %s : in progress, count = %Zd", __FUNCTION__, count); | ||
602 | } else { | 616 | } else { |
617 | spin_unlock_irqrestore(&dev->buflock, flags); | ||
618 | set_current_state(TASK_RUNNING); | ||
619 | remove_wait_queue(&dev->write_wait, &waita); | ||
603 | dbg(4," %s : sending, count = %Zd", __FUNCTION__, count); | 620 | dbg(4," %s : sending, count = %Zd", __FUNCTION__, count); |
604 | 621 | ||
605 | /* write the data into interrupt_out_buffer from userspace */ | 622 | /* write the data into interrupt_out_buffer from userspace */ |
@@ -622,11 +639,12 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, | |||
622 | bytes_to_write, | 639 | bytes_to_write, |
623 | adu_interrupt_out_callback, | 640 | adu_interrupt_out_callback, |
624 | dev, | 641 | dev, |
625 | dev->interrupt_in_endpoint->bInterval); | 642 | dev->interrupt_out_endpoint->bInterval); |
626 | /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */ | ||
627 | dev->interrupt_out_urb->actual_length = bytes_to_write; | 643 | dev->interrupt_out_urb->actual_length = bytes_to_write; |
644 | dev->out_urb_finished = 0; | ||
628 | retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL); | 645 | retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL); |
629 | if (retval < 0) { | 646 | if (retval < 0) { |
647 | dev->out_urb_finished = 1; | ||
630 | err("Couldn't submit interrupt_out_urb %d", retval); | 648 | err("Couldn't submit interrupt_out_urb %d", retval); |
631 | goto exit; | 649 | goto exit; |
632 | } | 650 | } |
@@ -637,16 +655,17 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, | |||
637 | bytes_written += bytes_to_write; | 655 | bytes_written += bytes_to_write; |
638 | } | 656 | } |
639 | } | 657 | } |
640 | 658 | mutex_unlock(&dev->mtx); | |
641 | retval = bytes_written; | 659 | return bytes_written; |
642 | 660 | ||
643 | exit: | 661 | exit: |
644 | /* unlock the device */ | ||
645 | mutex_unlock(&dev->mtx); | 662 | mutex_unlock(&dev->mtx); |
646 | exit_nolock: | 663 | exit_nolock: |
647 | |||
648 | dbg(2," %s : leave, return value %d", __FUNCTION__, retval); | 664 | dbg(2," %s : leave, return value %d", __FUNCTION__, retval); |
665 | return retval; | ||
649 | 666 | ||
667 | exit_onqueue: | ||
668 | remove_wait_queue(&dev->write_wait, &waita); | ||
650 | return retval; | 669 | return retval; |
651 | } | 670 | } |
652 | 671 | ||
@@ -831,25 +850,22 @@ static void adu_disconnect(struct usb_interface *interface) | |||
831 | dbg(2," %s : enter", __FUNCTION__); | 850 | dbg(2," %s : enter", __FUNCTION__); |
832 | 851 | ||
833 | dev = usb_get_intfdata(interface); | 852 | dev = usb_get_intfdata(interface); |
834 | usb_set_intfdata(interface, NULL); | ||
835 | 853 | ||
854 | mutex_lock(&dev->mtx); /* not interruptible */ | ||
855 | dev->udev = NULL; /* poison */ | ||
836 | minor = dev->minor; | 856 | minor = dev->minor; |
837 | |||
838 | /* give back our minor */ | ||
839 | usb_deregister_dev(interface, &adu_class); | 857 | usb_deregister_dev(interface, &adu_class); |
840 | dev->minor = 0; | 858 | mutex_unlock(&dev->mtx); |
841 | 859 | ||
842 | mutex_lock(&dev->mtx); /* not interruptible */ | 860 | mutex_lock(&adutux_mutex); |
861 | usb_set_intfdata(interface, NULL); | ||
843 | 862 | ||
844 | /* if the device is not opened, then we clean up right now */ | 863 | /* if the device is not opened, then we clean up right now */ |
845 | dbg(2," %s : open count %d", __FUNCTION__, dev->open_count); | 864 | dbg(2," %s : open count %d", __FUNCTION__, dev->open_count); |
846 | if (!dev->open_count) { | 865 | if (!dev->open_count) |
847 | mutex_unlock(&dev->mtx); | ||
848 | adu_delete(dev); | 866 | adu_delete(dev); |
849 | } else { | 867 | |
850 | dev->udev = NULL; | 868 | mutex_unlock(&adutux_mutex); |
851 | mutex_unlock(&dev->mtx); | ||
852 | } | ||
853 | 869 | ||
854 | dev_info(&interface->dev, "ADU device adutux%d now disconnected\n", | 870 | dev_info(&interface->dev, "ADU device adutux%d now disconnected\n", |
855 | (minor - ADU_MINOR_BASE)); | 871 | (minor - ADU_MINOR_BASE)); |