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