diff options
| author | Oliver Neukum <oneukum@suse.de> | 2007-05-04 03:23:40 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-05-23 02:45:51 -0400 |
| commit | 9d33efd9a791041bbe3a9e879925ef8fbb94d812 (patch) | |
| tree | 28a53c2f115e21a25dc3d1747aca22b395792237 /drivers | |
| parent | 2adb80e9c52f35a4d63783b98d48386c38c90484 (diff) | |
USB: ldusb bugfix
This patch fixes a problem reported with consecutive reads in the ldusb
driver.
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/usb/misc/ldusb.c | 35 |
1 files changed, 31 insertions, 4 deletions
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index 11555bde655b..7bad49404762 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c | |||
| @@ -165,6 +165,8 @@ struct ld_usb { | |||
| 165 | size_t interrupt_in_endpoint_size; | 165 | size_t interrupt_in_endpoint_size; |
| 166 | int interrupt_in_running; | 166 | int interrupt_in_running; |
| 167 | int interrupt_in_done; | 167 | int interrupt_in_done; |
| 168 | int buffer_overflow; | ||
| 169 | spinlock_t rbsl; | ||
| 168 | 170 | ||
| 169 | char* interrupt_out_buffer; | 171 | char* interrupt_out_buffer; |
| 170 | struct usb_endpoint_descriptor* interrupt_out_endpoint; | 172 | struct usb_endpoint_descriptor* interrupt_out_endpoint; |
| @@ -230,10 +232,12 @@ static void ld_usb_interrupt_in_callback(struct urb *urb) | |||
| 230 | } else { | 232 | } else { |
| 231 | dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n", | 233 | dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n", |
| 232 | __FUNCTION__, urb->status); | 234 | __FUNCTION__, urb->status); |
| 235 | spin_lock(&dev->rbsl); | ||
| 233 | goto resubmit; /* maybe we can recover */ | 236 | goto resubmit; /* maybe we can recover */ |
| 234 | } | 237 | } |
| 235 | } | 238 | } |
| 236 | 239 | ||
| 240 | spin_lock(&dev->rbsl); | ||
| 237 | if (urb->actual_length > 0) { | 241 | if (urb->actual_length > 0) { |
| 238 | next_ring_head = (dev->ring_head+1) % ring_buffer_size; | 242 | next_ring_head = (dev->ring_head+1) % ring_buffer_size; |
| 239 | if (next_ring_head != dev->ring_tail) { | 243 | if (next_ring_head != dev->ring_tail) { |
| @@ -244,21 +248,25 @@ static void ld_usb_interrupt_in_callback(struct urb *urb) | |||
| 244 | dev->ring_head = next_ring_head; | 248 | dev->ring_head = next_ring_head; |
| 245 | dbg_info(&dev->intf->dev, "%s: received %d bytes\n", | 249 | dbg_info(&dev->intf->dev, "%s: received %d bytes\n", |
| 246 | __FUNCTION__, urb->actual_length); | 250 | __FUNCTION__, urb->actual_length); |
| 247 | } else | 251 | } else { |
| 248 | dev_warn(&dev->intf->dev, | 252 | dev_warn(&dev->intf->dev, |
| 249 | "Ring buffer overflow, %d bytes dropped\n", | 253 | "Ring buffer overflow, %d bytes dropped\n", |
| 250 | urb->actual_length); | 254 | urb->actual_length); |
| 255 | dev->buffer_overflow = 1; | ||
| 256 | } | ||
| 251 | } | 257 | } |
| 252 | 258 | ||
| 253 | resubmit: | 259 | resubmit: |
| 254 | /* resubmit if we're still running */ | 260 | /* resubmit if we're still running */ |
| 255 | if (dev->interrupt_in_running && dev->intf) { | 261 | if (dev->interrupt_in_running && !dev->buffer_overflow && dev->intf) { |
| 256 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); | 262 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); |
| 257 | if (retval) | 263 | if (retval) { |
| 258 | dev_err(&dev->intf->dev, | 264 | dev_err(&dev->intf->dev, |
| 259 | "usb_submit_urb failed (%d)\n", retval); | 265 | "usb_submit_urb failed (%d)\n", retval); |
| 266 | dev->buffer_overflow = 1; | ||
| 267 | } | ||
| 260 | } | 268 | } |
| 261 | 269 | spin_unlock(&dev->rbsl); | |
| 262 | exit: | 270 | exit: |
| 263 | dev->interrupt_in_done = 1; | 271 | dev->interrupt_in_done = 1; |
| 264 | wake_up_interruptible(&dev->read_wait); | 272 | wake_up_interruptible(&dev->read_wait); |
| @@ -330,6 +338,7 @@ static int ld_usb_open(struct inode *inode, struct file *file) | |||
| 330 | /* initialize in direction */ | 338 | /* initialize in direction */ |
| 331 | dev->ring_head = 0; | 339 | dev->ring_head = 0; |
| 332 | dev->ring_tail = 0; | 340 | dev->ring_tail = 0; |
| 341 | dev->buffer_overflow = 0; | ||
| 333 | usb_fill_int_urb(dev->interrupt_in_urb, | 342 | usb_fill_int_urb(dev->interrupt_in_urb, |
| 334 | interface_to_usbdev(interface), | 343 | interface_to_usbdev(interface), |
| 335 | usb_rcvintpipe(interface_to_usbdev(interface), | 344 | usb_rcvintpipe(interface_to_usbdev(interface), |
| @@ -439,6 +448,7 @@ static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count, | |||
| 439 | size_t *actual_buffer; | 448 | size_t *actual_buffer; |
| 440 | size_t bytes_to_read; | 449 | size_t bytes_to_read; |
| 441 | int retval = 0; | 450 | int retval = 0; |
| 451 | int rv; | ||
| 442 | 452 | ||
| 443 | dev = file->private_data; | 453 | dev = file->private_data; |
| 444 | 454 | ||
| @@ -460,7 +470,10 @@ static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count, | |||
| 460 | } | 470 | } |
| 461 | 471 | ||
| 462 | /* wait for data */ | 472 | /* wait for data */ |
| 473 | spin_lock_irq(&dev->rbsl); | ||
| 463 | if (dev->ring_head == dev->ring_tail) { | 474 | if (dev->ring_head == dev->ring_tail) { |
| 475 | dev->interrupt_in_done = 0; | ||
| 476 | spin_unlock_irq(&dev->rbsl); | ||
| 464 | if (file->f_flags & O_NONBLOCK) { | 477 | if (file->f_flags & O_NONBLOCK) { |
| 465 | retval = -EAGAIN; | 478 | retval = -EAGAIN; |
| 466 | goto unlock_exit; | 479 | goto unlock_exit; |
| @@ -468,6 +481,8 @@ static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count, | |||
| 468 | retval = wait_event_interruptible(dev->read_wait, dev->interrupt_in_done); | 481 | retval = wait_event_interruptible(dev->read_wait, dev->interrupt_in_done); |
| 469 | if (retval < 0) | 482 | if (retval < 0) |
| 470 | goto unlock_exit; | 483 | goto unlock_exit; |
| 484 | } else { | ||
| 485 | spin_unlock_irq(&dev->rbsl); | ||
| 471 | } | 486 | } |
| 472 | 487 | ||
| 473 | /* actual_buffer contains actual_length + interrupt_in_buffer */ | 488 | /* actual_buffer contains actual_length + interrupt_in_buffer */ |
| @@ -486,6 +501,17 @@ static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count, | |||
| 486 | 501 | ||
| 487 | retval = bytes_to_read; | 502 | retval = bytes_to_read; |
| 488 | 503 | ||
| 504 | spin_lock_irq(&dev->rbsl); | ||
| 505 | if (dev->buffer_overflow) { | ||
| 506 | dev->buffer_overflow = 0; | ||
| 507 | spin_unlock_irq(&dev->rbsl); | ||
| 508 | rv = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); | ||
| 509 | if (rv < 0) | ||
| 510 | dev->buffer_overflow = 1; | ||
| 511 | } else { | ||
| 512 | spin_unlock_irq(&dev->rbsl); | ||
| 513 | } | ||
| 514 | |||
| 489 | unlock_exit: | 515 | unlock_exit: |
| 490 | /* unlock the device */ | 516 | /* unlock the device */ |
| 491 | up(&dev->sem); | 517 | up(&dev->sem); |
| @@ -635,6 +661,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * | |||
| 635 | goto exit; | 661 | goto exit; |
| 636 | } | 662 | } |
| 637 | init_MUTEX(&dev->sem); | 663 | init_MUTEX(&dev->sem); |
| 664 | spin_lock_init(&dev->rbsl); | ||
| 638 | dev->intf = intf; | 665 | dev->intf = intf; |
| 639 | init_waitqueue_head(&dev->read_wait); | 666 | init_waitqueue_head(&dev->read_wait); |
| 640 | init_waitqueue_head(&dev->write_wait); | 667 | init_waitqueue_head(&dev->write_wait); |
