diff options
Diffstat (limited to 'drivers/usb/serial/generic.c')
-rw-r--r-- | drivers/usb/serial/generic.c | 59 |
1 files changed, 39 insertions, 20 deletions
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 1a134f9c64f3..3ae17840175c 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -1,6 +1,7 @@ /* * USB Serial Converter Generic functions * + * Copyright (C) 2010 Johan Hovold (jhovold@gmail.com) * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or @@ -143,6 +144,7 @@ static void generic_cleanup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; unsigned long flags; + int i; dbg("%s - port %d", __func__, port->number); @@ -150,6 +152,8 @@ static void generic_cleanup(struct usb_serial_port *port) /* shutdown any bulk transfers that might be going on */ if (port->bulk_out_size) { usb_kill_urb(port->write_urb); + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) + usb_kill_urb(port->write_urbs[i]); spin_lock_irqsave(&port->lock, flags); kfifo_reset_out(&port->write_fifo); @@ -258,46 +262,56 @@ err_urb: * usb_serial_generic_write_start - kick off an URB write * @port: Pointer to the &struct usb_serial_port data * - * Returns the number of bytes queued on success. This will be zero if there - * was nothing to send. Otherwise, it returns a negative errno value + * Returns zero on success, or a negative errno value */ static int usb_serial_generic_write_start(struct usb_serial_port *port) { - int result; - int count; + struct urb *urb; + int count, result; unsigned long flags; + int i; + if (test_and_set_bit_lock(USB_SERIAL_WRITE_BUSY, &port->flags)) + return 0; +retry: spin_lock_irqsave(&port->lock, flags); - if (port->write_urb_busy || !kfifo_len(&port->write_fifo)) { + if (!port->write_urbs_free || !kfifo_len(&port->write_fifo)) { + clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); spin_unlock_irqrestore(&port->lock, flags); return 0; } - port->write_urb_busy = 1; + i = (int)find_first_bit(&port->write_urbs_free, + ARRAY_SIZE(port->write_urbs)); spin_unlock_irqrestore(&port->lock, flags); + urb = port->write_urbs[i]; count = port->serial->type->prepare_write_buffer(port, - &port->write_urb->transfer_buffer, - port->bulk_out_size, NULL, 0); - usb_serial_debug_data(debug, &port->dev, __func__, - count, port->write_urb->transfer_buffer); - port->write_urb->transfer_buffer_length = count; - - /* send the data out the bulk port */ - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + &urb->transfer_buffer, + port->bulk_out_size, NULL, 0); + urb->transfer_buffer_length = count; + usb_serial_debug_data(debug, &port->dev, __func__, count, + urb->transfer_buffer); + result = usb_submit_urb(urb, GFP_ATOMIC); if (result) { dev_err(&port->dev, "%s - error submitting urb: %d\n", __func__, result); - /* don't have to grab the lock here, as we will - retry if != 0 */ - port->write_urb_busy = 0; + clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); return result; } + clear_bit(i, &port->write_urbs_free); spin_lock_irqsave(&port->lock, flags); port->tx_bytes += count; spin_unlock_irqrestore(&port->lock, flags); - return count; + /* Try sending off another urb, unless in irq context (in which case + * there will be no free urb). */ + if (!in_irq()) + goto retry; + + clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); + + return 0; } /** @@ -461,6 +475,7 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) unsigned long flags; struct usb_serial_port *port = urb->context; int status = urb->status; + int i; dbg("%s - port %d", __func__, port->number); @@ -472,9 +487,13 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) port->tx_urbs--; spin_unlock_irqrestore(&port->lock, flags); } else { + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) + if (port->write_urbs[i] == urb) + break; + spin_lock_irqsave(&port->lock, flags); port->tx_bytes -= urb->transfer_buffer_length; - port->write_urb_busy = 0; + set_bit(i, &port->write_urbs_free); spin_unlock_irqrestore(&port->lock, flags); if (status) { @@ -576,7 +595,7 @@ int usb_serial_generic_resume(struct usb_serial *serial) c++; } - if (port->write_urb) { + if (port->bulk_out_size) { r = usb_serial_generic_write_start(port); if (r < 0) c++; |