From bd5afa9eac6daa408412a31a6c69e87e8bd28c7e Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Mon, 8 Mar 2010 21:50:12 -0600 Subject: usb-serial: Use tty_port version console instead of usb_serial_port Replace all instances of using the console variable in struct usb_serial_port with the struct tty_port version. CC: Alan Cox CC: Alan Stern CC: Oliver Neukum CC: Andrew Morton CC: linux-usb@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Jason Wessel Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index f804acb138ec..ba61c745df0b 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -447,7 +447,7 @@ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) /* The per character mucking around with sysrq path it too slow for stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases where the USB serial is not a console anyway */ - if (!port->console || !port->sysrq) + if (!port->port.console || !port->sysrq) tty_insert_flip_string(tty, ch, urb->actual_length); else { /* Push data to tty */ @@ -561,7 +561,7 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) int usb_serial_handle_sysrq_char(struct tty_struct *tty, struct usb_serial_port *port, unsigned int ch) { - if (port->sysrq && port->console) { + if (port->sysrq && port->port.console) { if (ch && time_before(jiffies, port->sysrq)) { handle_sysrq(ch, tty); port->sysrq = 0; -- cgit v1.2.3 From 7f0ae3a8eeb7f231dc99cee7c871ba64e07ebefe Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 25 Mar 2010 11:29:16 -0700 Subject: usb: fix serial build when SYSRQ is disabled Fix build error when CONFIG_MAGIC_SYSRQ is not enabled: drivers/usb/serial/generic.c:566: error: implicit declaration of function 'handle_sysrq' Signed-off-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index ba61c745df0b..a5e610976689 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -558,6 +559,7 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) } } +#ifdef CONFIG_MAGIC_SYSRQ int usb_serial_handle_sysrq_char(struct tty_struct *tty, struct usb_serial_port *port, unsigned int ch) { @@ -571,6 +573,13 @@ int usb_serial_handle_sysrq_char(struct tty_struct *tty, } return 0; } +#else +int usb_serial_handle_sysrq_char(struct tty_struct *tty, + struct usb_serial_port *port, unsigned int ch) +{ + return 0; +} +#endif EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char); int usb_serial_handle_break(struct usb_serial_port *port) -- cgit v1.2.3 From 30af7fb5a40f8724c130428473edffa73170e04c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:00:42 +0100 Subject: USB: serial: fix generic chars_in_buffer Make sure chars_in_buffer accounts also for data in host stack queues. This fixes the problem with tty_wait_until_sent returning too soon at close which could cause the final write urb to be cancelled. Reported-by: Pete Zaitcev Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index a5e610976689..8f78d7b8e888 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -309,10 +309,14 @@ static int usb_serial_generic_write_start(struct usb_serial_port *port) /* don't have to grab the lock here, as we will retry if != 0 */ port->write_urb_busy = 0; - } else - result = count; + return result; + } - return result; + spin_lock_irqsave(&port->lock, flags); + port->tx_bytes_flight += count; + spin_unlock_irqrestore(&port->lock, flags); + + return count; } /** @@ -400,7 +404,7 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) if (serial->type->max_in_flight_urbs) chars = port->tx_bytes_flight; else - chars = kfifo_len(&port->write_fifo); + chars = kfifo_len(&port->write_fifo) + port->tx_bytes_flight; spin_unlock_irqrestore(&port->lock, flags); dbg("%s - returns %d", __func__, chars); @@ -510,7 +514,10 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) port->urbs_in_flight = 0; spin_unlock_irqrestore(&port->lock, flags); } else { + spin_lock_irqsave(&port->lock, flags); + port->tx_bytes_flight -= urb->transfer_buffer_length; port->write_urb_busy = 0; + spin_unlock_irqrestore(&port->lock, flags); if (status) kfifo_reset_out(&port->write_fifo); -- cgit v1.2.3 From 50dbb8528757b1977efd5d270ed9d262cbbef87d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:00:43 +0100 Subject: USB: serial: fix missing locking on fifo in write callback On errors the fifo was reset without any locking. This could race with write which do kfifo_put and perhaps also chars_in_buffer and write_room. Every other access to the fifo is protected using the port lock so better add it to the error path as well. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 8f78d7b8e888..2a3196a2c66c 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -519,10 +519,13 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) port->write_urb_busy = 0; spin_unlock_irqrestore(&port->lock, flags); - if (status) + if (status) { + spin_lock_irqsave(&port->lock, flags); kfifo_reset_out(&port->write_fifo); - else + spin_unlock_irqrestore(&port->lock, flags); + } else { usb_serial_generic_write_start(port); + } } if (status) -- cgit v1.2.3 From ec3ee5086c1e1b883292eaf795b5c1b0c25bcffe Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:00:44 +0100 Subject: USB: serial: clear fifo on close Make sure fifo is emptied on close. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 2a3196a2c66c..583244bd84b1 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -156,13 +156,19 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_open); static void generic_cleanup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; + unsigned long flags; dbg("%s - port %d", __func__, port->number); if (serial->dev) { /* shutdown any bulk transfers that might be going on */ - if (port->bulk_out_size) + if (port->bulk_out_size) { usb_kill_urb(port->write_urb); + + spin_lock_irqsave(&port->lock, flags); + kfifo_reset_out(&port->write_fifo); + spin_unlock_irqrestore(&port->lock, flags); + } if (port->bulk_in_size) usb_kill_urb(port->read_urb); } -- cgit v1.2.3 From f26788da3b342099d2b02d99ba1cb7f154d6ef7b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:00:45 +0100 Subject: USB: serial: refactor generic close Export usb_serial_generic_close so that drivers can easily kill the read and write urb and make sure that the write fifo is reset. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ark3116.c | 7 ++----- drivers/usb/serial/belkin_sa.c | 4 +--- drivers/usb/serial/ch341.c | 5 +---- drivers/usb/serial/cp210x.c | 5 +---- drivers/usb/serial/generic.c | 1 + drivers/usb/serial/mct_u232.c | 7 ++----- 6 files changed, 8 insertions(+), 21 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 9b66bf19f751..0ae610efa23d 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -355,14 +355,11 @@ static void ark3116_close(struct usb_serial_port *port) /* deactivate interrupts */ ark3116_write_reg(serial, UART_IER, 0); - /* shutdown any bulk reads that might be going on */ - if (serial->num_bulk_out) - usb_kill_urb(port->write_urb); - if (serial->num_bulk_in) - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); if (serial->num_interrupt_in) usb_kill_urb(port->interrupt_in_urb); } + } static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port) diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 1295e44e3f1c..bdab31afc37d 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -246,9 +246,7 @@ static void belkin_sa_close(struct usb_serial_port *port) { dbg("%s port %d", __func__, port->number); - /* shutdown our bulk reads and writes */ - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); usb_kill_urb(port->interrupt_in_urb); } /* belkin_sa_close */ diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 7e8e39818414..63f7cc45bcac 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -305,10 +305,7 @@ static void ch341_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); - /* shutdown our urbs */ - dbg("%s - shutting down urbs", __func__); - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); usb_kill_urb(port->interrupt_in_urb); } diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 705a7e7a9746..fa4fb5e0cf12 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -422,10 +422,7 @@ static void cp210x_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); - /* shutdown our urbs */ - dbg("%s - shutting down urbs", __func__); - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); mutex_lock(&port->serial->disc_mutex); if (!port->serial->disconnected) diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 583244bd84b1..e1d245aa4a1d 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -179,6 +179,7 @@ void usb_serial_generic_close(struct usb_serial_port *port) dbg("%s - port %d", __func__, port->number); generic_cleanup(port); } +EXPORT_SYMBOL_GPL(usb_serial_generic_close); static int usb_serial_multi_urb_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 2849f8c32015..7aa01b95b1d4 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -549,12 +549,9 @@ static void mct_u232_close(struct usb_serial_port *port) { dbg("%s port %d", __func__, port->number); - if (port->serial->dev) { - /* shutdown our urbs */ - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); + if (port->serial->dev) usb_kill_urb(port->interrupt_in_urb); - } } /* mct_u232_close */ -- cgit v1.2.3 From 41bd72f9041def8e3334d3e3693862d078f5cb9a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:05:53 +0100 Subject: USB: serial: refactor read urb submission in generic driver Use the already exported function for submitting the read urb associated with a usb_serial_port. Make sure it returns the result of usb_submit_urb and rename to the more descriptive usb_serial_generic_submit_read_urb. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 32 ++++++++------------------------ drivers/usb/serial/usb_debug.c | 2 +- include/linux/usb/serial.h | 2 +- 3 files changed, 10 insertions(+), 26 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index e1d245aa4a1d..d8dd3a59f56a 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -118,7 +118,6 @@ void usb_serial_generic_deregister(void) int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; int result = 0; unsigned long flags; @@ -131,23 +130,8 @@ int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port spin_unlock_irqrestore(&port->lock, flags); /* if we have a bulk endpoint, start reading from it */ - if (port->bulk_in_size) { - /* Start reading from the device */ - usb_fill_bulk_urb(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ((serial->type->read_bulk_callback) ? - serial->type->read_bulk_callback : - usb_serial_generic_read_bulk_callback), - port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); - } + if (port->bulk_in_size) + result = usb_serial_generic_submit_read_urb(port, GFP_KERNEL); return result; } @@ -418,9 +402,8 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) return chars; } - -void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port, - gfp_t mem_flags) +int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, + gfp_t mem_flags) { struct urb *urb = port->read_urb; struct usb_serial *serial = port->serial; @@ -439,11 +422,12 @@ void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port, result = usb_submit_urb(urb, mem_flags); if (result && result != -EPERM) { dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", + "%s - failed submitting read urb, error %d\n", __func__, result); } + return result; } -EXPORT_SYMBOL_GPL(usb_serial_generic_resubmit_read_urb); +EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urb); /* Push data to tty layer and resubmit the bulk read URB */ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) @@ -471,7 +455,7 @@ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) tty_flip_buffer_push(tty); tty_kref_put(tty); done: - usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC); + usb_serial_generic_submit_read_urb(port, GFP_ATOMIC); } void usb_serial_generic_read_bulk_callback(struct urb *urb) diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index 28026b47344a..63c2734e764a 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -69,7 +69,7 @@ static void usb_debug_read_bulk_callback(struct urb *urb) memcmp(urb->transfer_buffer, USB_DEBUG_BRK, USB_DEBUG_BRK_SIZE) == 0) { usb_serial_handle_break(port); - usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC); + usb_serial_generic_submit_read_urb(port, GFP_ATOMIC); return; } diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index ab311dab3383..53f6dc65e87e 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -321,7 +321,7 @@ extern void usb_serial_generic_disconnect(struct usb_serial *serial); extern void usb_serial_generic_release(struct usb_serial *serial); extern int usb_serial_generic_register(int debug); extern void usb_serial_generic_deregister(void); -extern void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port, +extern int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, gfp_t mem_flags); extern int usb_serial_handle_sysrq_char(struct tty_struct *tty, struct usb_serial_port *port, -- cgit v1.2.3 From 056afc0f01701c4c779ac5b4f2dd9058063f337c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:05:54 +0100 Subject: USB: serial: remove unnecessary re-initialisation of generic urbs The generic read and write bulk urbs are initialised when allocated in usb_serial_probe. The only field that needs to be updated after that is the transfer_buffer_length of the write urb. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index d8dd3a59f56a..ffc2f163bb59 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -256,7 +256,6 @@ error_no_buffer: */ static int usb_serial_generic_write_start(struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; unsigned char *data; int result; int count; @@ -281,15 +280,7 @@ static int usb_serial_generic_write_start(struct usb_serial_port *port) count = kfifo_out_locked(&port->write_fifo, data, port->bulk_out_size, &port->lock); usb_serial_debug_data(debug, &port->dev, __func__, count, data); - /* set up our urb */ - usb_fill_bulk_urb(port->write_urb, serial->dev, - usb_sndbulkpipe(serial->dev, - port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, count, - ((serial->type->write_bulk_callback) ? - serial->type->write_bulk_callback : - usb_serial_generic_write_bulk_callback), - port); + port->write_urb->transfer_buffer_length = count; /* send the data out the bulk port */ result = usb_submit_urb(port->write_urb, GFP_ATOMIC); @@ -405,21 +396,9 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) { - struct urb *urb = port->read_urb; - struct usb_serial *serial = port->serial; int result; - /* Continue reading from device */ - usb_fill_bulk_urb(urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - urb->transfer_buffer, - urb->transfer_buffer_length, - ((serial->type->read_bulk_callback) ? - serial->type->read_bulk_callback : - usb_serial_generic_read_bulk_callback), port); - - result = usb_submit_urb(urb, mem_flags); + result = usb_submit_urb(port->read_urb, mem_flags); if (result && result != -EPERM) { dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", -- cgit v1.2.3 From 0f3d5bae2bdacce6c6c1d116809d6b3d50338df7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:05:56 +0100 Subject: USB: serial: clean up read processing in generic driver Always process and flush read urb, but only resubmit when not throttled. The new tty-layer supply plenty of slack so there is really no need to cache and delay processing of a single urb while throttled. Note that unthrottle now submits using GFP_KERNEL as we are not in atomic context (so there is no need to save irq state either). Note also that the process_read_urb function could be added to usb_serial_driver should any driver need to do any device specific processing. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index ffc2f163bb59..e16c0b234cc9 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -408,16 +408,16 @@ int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, } EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urb); -/* Push data to tty layer and resubmit the bulk read URB */ -static void flush_and_resubmit_read_urb(struct usb_serial_port *port) +static void usb_serial_generic_process_read_urb(struct urb *urb) { - struct urb *urb = port->read_urb; - struct tty_struct *tty = tty_port_tty_get(&port->port); + struct usb_serial_port *port = urb->context; + struct tty_struct *tty; char *ch = (char *)urb->transfer_buffer; int i; + tty = tty_port_tty_get(&port->port); if (!tty) - goto done; + return; /* The per character mucking around with sysrq path it too slow for stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases @@ -425,7 +425,6 @@ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) if (!port->port.console || !port->sysrq) tty_insert_flip_string(tty, ch, urb->actual_length); else { - /* Push data to tty */ for (i = 0; i < urb->actual_length; i++, ch++) { if (!usb_serial_handle_sysrq_char(tty, port, *ch)) tty_insert_flip_char(tty, *ch, TTY_NORMAL); @@ -433,8 +432,6 @@ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) } tty_flip_buffer_push(tty); tty_kref_put(tty); -done: - usb_serial_generic_submit_read_urb(port, GFP_ATOMIC); } void usb_serial_generic_read_bulk_callback(struct urb *urb) @@ -454,13 +451,14 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); + usb_serial_generic_process_read_urb(urb); /* Throttle the device if requested by tty */ spin_lock_irqsave(&port->lock, flags); port->throttled = port->throttle_req; if (!port->throttled) { spin_unlock_irqrestore(&port->lock, flags); - flush_and_resubmit_read_urb(port); + usb_serial_generic_submit_read_urb(port, GFP_ATOMIC); } else spin_unlock_irqrestore(&port->lock, flags); } @@ -523,20 +521,17 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; int was_throttled; - unsigned long flags; dbg("%s - port %d", __func__, port->number); /* Clear the throttle flags */ - spin_lock_irqsave(&port->lock, flags); + spin_lock_irq(&port->lock); was_throttled = port->throttled; port->throttled = port->throttle_req = 0; - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock_irq(&port->lock); - if (was_throttled) { - /* Resume reading from device */ - flush_and_resubmit_read_urb(port); - } + if (was_throttled) + usb_serial_generic_submit_read_urb(port, GFP_KERNEL); } #ifdef CONFIG_MAGIC_SYSRQ -- cgit v1.2.3 From 231543206452f5c42bace54b5c13dfe5a0313812 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:05:57 +0100 Subject: USB: serial: generalise generic read implementation Add process_read_urb to usb_serial_driver so that a driver can rely on the generic read (and throttle) mechanism but still do device specific processing of incoming data (such as adding tty_flags before pushing to line discipline). The default generic implementation handles sysrq for consoles but otherwise simply pushes to tty. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 5 +++-- drivers/usb/serial/usb-serial.c | 1 + include/linux/usb/serial.h | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index e16c0b234cc9..176f1257b664 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -408,7 +408,7 @@ int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, } EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urb); -static void usb_serial_generic_process_read_urb(struct urb *urb) +void usb_serial_generic_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; struct tty_struct *tty; @@ -433,6 +433,7 @@ static void usb_serial_generic_process_read_urb(struct urb *urb) tty_flip_buffer_push(tty); tty_kref_put(tty); } +EXPORT_SYMBOL_GPL(usb_serial_generic_process_read_urb); void usb_serial_generic_read_bulk_callback(struct urb *urb) { @@ -451,7 +452,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - usb_serial_generic_process_read_urb(urb); + port->serial->type->process_read_urb(urb); /* Throttle the device if requested by tty */ spin_lock_irqsave(&port->lock, flags); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 538924627eba..1b924425089f 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1298,6 +1298,7 @@ static void fixup_generic(struct usb_serial_driver *device) set_to_generic_if_null(device, write_bulk_callback); set_to_generic_if_null(device, disconnect); set_to_generic_if_null(device, release); + set_to_generic_if_null(device, process_read_urb); } int usb_serial_register(struct usb_serial_driver *driver) diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 53f6dc65e87e..ff8872eba3ac 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -272,6 +272,8 @@ struct usb_serial_driver { void (*write_int_callback)(struct urb *urb); void (*read_bulk_callback)(struct urb *urb); void (*write_bulk_callback)(struct urb *urb); + /* Called by the generic read bulk callback */ + void (*process_read_urb)(struct urb *urb); }; #define to_usb_serial_driver(d) \ container_of(d, struct usb_serial_driver, driver) @@ -323,6 +325,7 @@ extern int usb_serial_generic_register(int debug); extern void usb_serial_generic_deregister(void); extern int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, gfp_t mem_flags); +extern void usb_serial_generic_process_read_urb(struct urb *urb); extern int usb_serial_handle_sysrq_char(struct tty_struct *tty, struct usb_serial_port *port, unsigned int ch); -- cgit v1.2.3 From f1e949ac4ea1f4e37187949944899afeaff33e1e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:05:59 +0100 Subject: USB: serial: export generic throttle and unthrottle Allow drivers to use the generic throttle and unthrottle implementation. This makes sense for drivers using the generic read functionality. Note that drivers need to set these explicitly in order to enable them (i.e., we do not set them at port probe if not defined). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 176f1257b664..789a5af842a9 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -517,6 +517,7 @@ void usb_serial_generic_throttle(struct tty_struct *tty) port->throttle_req = 1; spin_unlock_irqrestore(&port->lock, flags); } +EXPORT_SYMBOL_GPL(usb_serial_generic_throttle); void usb_serial_generic_unthrottle(struct tty_struct *tty) { @@ -534,6 +535,7 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) if (was_throttled) usb_serial_generic_submit_read_urb(port, GFP_KERNEL); } +EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle); #ifdef CONFIG_MAGIC_SYSRQ int usb_serial_handle_sysrq_char(struct tty_struct *tty, -- cgit v1.2.3 From 1a1405e22563151de79fdc83aa5e5815d10f0291 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:06:01 +0100 Subject: USB: serial: clean up some error and debug messages in generic driver Clean up error messages on usb_submit_urb failure. Remove debug message on zero-length writes. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 789a5af842a9..63b43308e160 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -221,8 +221,7 @@ static int usb_serial_multi_urb_write(struct tty_struct *tty, status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", + dev_err(&port->dev, "%s - error submitting urb: %d\n", __func__, status); goto error; } @@ -285,8 +284,7 @@ static int usb_serial_generic_write_start(struct usb_serial_port *port) /* send the data out the bulk port */ result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", + 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 */ @@ -324,10 +322,8 @@ int usb_serial_generic_write(struct tty_struct *tty, if (!port->bulk_out_size) return -ENODEV; - if (count == 0) { - dbg("%s - write request of 0 bytes", __func__); + if (!count) return 0; - } if (serial->type->max_in_flight_urbs) return usb_serial_multi_urb_write(tty, port, @@ -400,8 +396,7 @@ int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, result = usb_submit_urb(port->read_urb, mem_flags); if (result && result != -EPERM) { - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", + dev_err(&port->dev, "%s - error submitting urb: %d\n", __func__, result); } return result; -- cgit v1.2.3 From 50a5f70cee11636908711abd339f5c5933375a7d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:06:02 +0100 Subject: USB: serial: clean up generic write start busy test Submit write urb if it is not already in use and we have buffered data. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 63b43308e160..9756b5c22d94 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -259,22 +259,15 @@ static int usb_serial_generic_write_start(struct usb_serial_port *port) int result; int count; unsigned long flags; - bool start_io; - /* Atomically determine whether we can and need to start a USB - * operation. */ spin_lock_irqsave(&port->lock, flags); - if (port->write_urb_busy) - start_io = false; - else { - start_io = (kfifo_len(&port->write_fifo) != 0); - port->write_urb_busy = start_io; + if (port->write_urb_busy || !kfifo_len(&port->write_fifo)) { + spin_unlock_irqrestore(&port->lock, flags); + return 0; } + port->write_urb_busy = 1; spin_unlock_irqrestore(&port->lock, flags); - if (!start_io) - return 0; - data = port->write_urb->transfer_buffer; count = kfifo_out_locked(&port->write_fifo, data, port->bulk_out_size, &port->lock); usb_serial_debug_data(debug, &port->dev, __func__, count, data); -- cgit v1.2.3 From 40f92f0dcd9b215c48c53a226328e8e36615e367 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:06:06 +0100 Subject: USB: serial: allow custom multi-urb write bulk callbacks Allow drivers to implement their own multi-urb write bulk callbacks as we do for single urb writes. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 9756b5c22d94..be52c748bccb 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -217,7 +217,7 @@ static int usb_serial_multi_urb_write(struct tty_struct *tty, usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), buffer, towrite, - usb_serial_generic_write_bulk_callback, port); + port->serial->type->write_bulk_callback, port); status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { -- cgit v1.2.3 From 25d514ca227e1ac81d0906a4ccf2aa171f50a600 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:06:07 +0100 Subject: USB: serial: re-implement multi-urb writes in generic driver Use dynamic transfer buffer sizes since it is more efficient to let the host controller do the partitioning to fit endpoint size. This way we also do not use more than one urb per write request. Replace max_in_flight_urbs with multi_urb_write flag in struct usb_serial_driver to enable multi-urb writes. Use MAX_TX_URBS=40 and a max buffer size of PAGE_SIZE to prevent DoS attacks. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 132 ++++++++++++++++++------------------------- include/linux/usb/serial.h | 9 ++- 2 files changed, 62 insertions(+), 79 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index be52c748bccb..ad4823bbfa19 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -25,6 +25,8 @@ static int debug; +#define MAX_TX_URBS 40 + #ifdef CONFIG_USB_SERIAL_GENERIC static int generic_probe(struct usb_interface *interface, @@ -172,78 +174,63 @@ static int usb_serial_multi_urb_write(struct tty_struct *tty, struct urb *urb; unsigned char *buffer; int status; - int towrite; - int bwrite = 0; - - dbg("%s - port %d", __func__, port->number); - - if (count == 0) - dbg("%s - write request of 0 bytes", __func__); - while (count > 0) { - towrite = (count > port->bulk_out_size) ? - port->bulk_out_size : count; - spin_lock_irqsave(&port->lock, flags); - if (port->urbs_in_flight > - port->serial->type->max_in_flight_urbs) { - spin_unlock_irqrestore(&port->lock, flags); - dbg("%s - write limit hit", __func__); - return bwrite; - } - port->tx_bytes_flight += towrite; - port->urbs_in_flight++; + spin_lock_irqsave(&port->lock, flags); + if (port->tx_urbs == MAX_TX_URBS) { spin_unlock_irqrestore(&port->lock, flags); + dbg("%s - write limit hit", __func__); + return 0; + } + port->tx_urbs++; + spin_unlock_irqrestore(&port->lock, flags); - buffer = kmalloc(towrite, GFP_ATOMIC); - if (!buffer) { - dev_err(&port->dev, - "%s ran out of kernel memory for urb ...\n", __func__); - goto error_no_buffer; - } + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + dev_err(&port->dev, "%s - no free urbs available\n", __func__); + status = -ENOMEM; + goto err_urb; + } - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - dev_err(&port->dev, "%s - no more free urbs\n", + count = min_t(int, count, PAGE_SIZE); + buffer = kmalloc(count, GFP_ATOMIC); + if (!buffer) { + dev_err(&port->dev, "%s - could not allocate buffer\n", __func__); - goto error_no_urb; - } + status = -ENOMEM; + goto err_buf; + } - /* Copy data */ - memcpy(buffer, buf + bwrite, towrite); - usb_serial_debug_data(debug, &port->dev, __func__, - towrite, buffer); - /* fill the buffer and send it */ - usb_fill_bulk_urb(urb, port->serial->dev, + memcpy(buffer, buf, count); + usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); + usb_fill_bulk_urb(urb, port->serial->dev, usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), - buffer, towrite, + buffer, count, port->serial->type->write_bulk_callback, port); - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - dev_err(&port->dev, "%s - error submitting urb: %d\n", + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + dev_err(&port->dev, "%s - error submitting urb: %d\n", __func__, status); - goto error; - } - - /* This urb is the responsibility of the host driver now */ - usb_free_urb(urb); - dbg("%s write: %d", __func__, towrite); - count -= towrite; - bwrite += towrite; + goto err; } - return bwrite; + spin_lock_irqsave(&port->lock, flags); + port->tx_bytes += urb->transfer_buffer_length; + spin_unlock_irqrestore(&port->lock, flags); -error: usb_free_urb(urb); -error_no_urb: + + return count; +err: kfree(buffer); -error_no_buffer: +err_buf: + usb_free_urb(urb); +err_urb: spin_lock_irqsave(&port->lock, flags); - port->urbs_in_flight--; - port->tx_bytes_flight -= towrite; + port->tx_urbs--; spin_unlock_irqrestore(&port->lock, flags); - return bwrite; + + return status; } /** @@ -286,7 +273,7 @@ static int usb_serial_generic_write_start(struct usb_serial_port *port) } spin_lock_irqsave(&port->lock, flags); - port->tx_bytes_flight += count; + port->tx_bytes += count; spin_unlock_irqrestore(&port->lock, flags); return count; @@ -318,9 +305,8 @@ int usb_serial_generic_write(struct tty_struct *tty, if (!count) return 0; - if (serial->type->max_in_flight_urbs) - return usb_serial_multi_urb_write(tty, port, - buf, count); + if (serial->type->multi_urb_write) + return usb_serial_multi_urb_write(tty, port, buf, count); count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock); result = usb_serial_generic_write_start(port); @@ -337,7 +323,7 @@ int usb_serial_generic_write_room(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; unsigned long flags; - int room = 0; + int room; dbg("%s - port %d", __func__, port->number); @@ -345,14 +331,10 @@ int usb_serial_generic_write_room(struct tty_struct *tty) return 0; spin_lock_irqsave(&port->lock, flags); - if (serial->type->max_in_flight_urbs) { - if (port->urbs_in_flight < serial->type->max_in_flight_urbs) - room = port->bulk_out_size * - (serial->type->max_in_flight_urbs - - port->urbs_in_flight); - } else { + if (serial->type->multi_urb_write) + room = (MAX_TX_URBS - port->tx_urbs) * PAGE_SIZE; + else room = kfifo_avail(&port->write_fifo); - } spin_unlock_irqrestore(&port->lock, flags); dbg("%s - returns %d", __func__, room); @@ -372,10 +354,10 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) return 0; spin_lock_irqsave(&port->lock, flags); - if (serial->type->max_in_flight_urbs) - chars = port->tx_bytes_flight; + if (serial->type->multi_urb_write) + chars = port->tx_bytes; else - chars = kfifo_len(&port->write_fifo) + port->tx_bytes_flight; + chars = kfifo_len(&port->write_fifo) + port->tx_bytes; spin_unlock_irqrestore(&port->lock, flags); dbg("%s - returns %d", __func__, chars); @@ -461,18 +443,16 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) dbg("%s - port %d", __func__, port->number); - if (port->serial->type->max_in_flight_urbs) { + if (port->serial->type->multi_urb_write) { kfree(urb->transfer_buffer); spin_lock_irqsave(&port->lock, flags); - --port->urbs_in_flight; - port->tx_bytes_flight -= urb->transfer_buffer_length; - if (port->urbs_in_flight < 0) - port->urbs_in_flight = 0; + port->tx_bytes -= urb->transfer_buffer_length; + port->tx_urbs--; spin_unlock_irqrestore(&port->lock, flags); } else { spin_lock_irqsave(&port->lock, flags); - port->tx_bytes_flight -= urb->transfer_buffer_length; + port->tx_bytes -= urb->transfer_buffer_length; port->write_urb_busy = 0; spin_unlock_irqrestore(&port->lock, flags); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index ff8872eba3ac..2a3283761600 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -60,6 +60,8 @@ enum port_dev_state { * @write_urb: pointer to the bulk out struct urb for this port. * @write_fifo: kfifo used to buffer outgoing data * @write_urb_busy: port`s writing status + * @tx_bytes: number of bytes currently in host stack queues + * @tx_urbs: number of urbs currently in host stack queues * @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this * port. * @write_wait: a wait_queue_head_t used by the port. @@ -98,8 +100,8 @@ struct usb_serial_port { int write_urb_busy; __u8 bulk_out_endpointAddress; - int tx_bytes_flight; - int urbs_in_flight; + int tx_bytes; + int tx_urbs; wait_queue_head_t write_wait; struct work_struct work; @@ -223,7 +225,8 @@ struct usb_serial_driver { struct device_driver driver; struct usb_driver *usb_driver; struct usb_dynids dynids; - int max_in_flight_urbs; + + unsigned char multi_urb_write:1; size_t bulk_in_size; size_t bulk_out_size; -- cgit v1.2.3 From eaa3bcb06aed1ac1d6d9e3edd3b5f72ea57a6ac0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Mar 2010 23:06:08 +0100 Subject: USB: serial: generalise write buffer preparation Generalise write buffer preparation. This allows for drivers to manipulate (e.g. add headers) to bulk out data before it is sent. This adds a new function pointer to usb_serial_driver: int (*prepare_write_buffer)(struct usb_serial_port *port, void **dest, size_t size, const void *src, size_t count); The function is generic and can be used with either kfifo-based or multi-urb writes: If *dest is NULL the implementation should allocate dest. If src is NULL the implementation should use the port write fifo. If not set, a generic implementation is used which simply uses memcpy or kfifo_out. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 47 +++++++++++++++++++++++++++++------------ drivers/usb/serial/usb-serial.c | 1 + include/linux/usb/serial.h | 5 +++++ 3 files changed, 40 insertions(+), 13 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index ad4823bbfa19..1a134f9c64f3 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -167,12 +167,35 @@ void usb_serial_generic_close(struct usb_serial_port *port) } EXPORT_SYMBOL_GPL(usb_serial_generic_close); +int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port, + void **dest, size_t size, const void *src, size_t count) +{ + if (!*dest) { + size = count; + *dest = kmalloc(count, GFP_ATOMIC); + if (!*dest) { + dev_err(&port->dev, "%s - could not allocate buffer\n", + __func__); + return -ENOMEM; + } + } + if (src) { + count = size; + memcpy(*dest, src, size); + } else { + count = kfifo_out_locked(&port->write_fifo, *dest, size, + &port->lock); + } + return count; +} +EXPORT_SYMBOL_GPL(usb_serial_generic_prepare_write_buffer); + static int usb_serial_multi_urb_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { unsigned long flags; struct urb *urb; - unsigned char *buffer; + void *buffer; int status; spin_lock_irqsave(&port->lock, flags); @@ -191,16 +214,14 @@ static int usb_serial_multi_urb_write(struct tty_struct *tty, goto err_urb; } + buffer = NULL; count = min_t(int, count, PAGE_SIZE); - buffer = kmalloc(count, GFP_ATOMIC); - if (!buffer) { - dev_err(&port->dev, "%s - could not allocate buffer\n", - __func__); - status = -ENOMEM; + count = port->serial->type->prepare_write_buffer(port, &buffer, 0, + buf, count); + if (count < 0) { + status = count; goto err_buf; } - - memcpy(buffer, buf, count); usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); usb_fill_bulk_urb(urb, port->serial->dev, usb_sndbulkpipe(port->serial->dev, @@ -242,7 +263,6 @@ err_urb: */ static int usb_serial_generic_write_start(struct usb_serial_port *port) { - unsigned char *data; int result; int count; unsigned long flags; @@ -255,10 +275,11 @@ static int usb_serial_generic_write_start(struct usb_serial_port *port) port->write_urb_busy = 1; spin_unlock_irqrestore(&port->lock, flags); - data = port->write_urb->transfer_buffer; - count = kfifo_out_locked(&port->write_fifo, data, port->bulk_out_size, &port->lock); - usb_serial_debug_data(debug, &port->dev, __func__, count, data); - + 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 */ diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 1b924425089f..8249fd8381fb 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1299,6 +1299,7 @@ static void fixup_generic(struct usb_serial_driver *device) set_to_generic_if_null(device, disconnect); set_to_generic_if_null(device, release); set_to_generic_if_null(device, process_read_urb); + set_to_generic_if_null(device, prepare_write_buffer); } int usb_serial_register(struct usb_serial_driver *driver) diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 2a3283761600..a4c99ea390e7 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -277,6 +277,9 @@ struct usb_serial_driver { void (*write_bulk_callback)(struct urb *urb); /* Called by the generic read bulk callback */ void (*process_read_urb)(struct urb *urb); + /* Called by the generic write implementation */ + int (*prepare_write_buffer)(struct usb_serial_port *port, + void **dest, size_t size, const void *src, size_t count); }; #define to_usb_serial_driver(d) \ container_of(d, struct usb_serial_driver, driver) @@ -329,6 +332,8 @@ extern void usb_serial_generic_deregister(void); extern int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, gfp_t mem_flags); extern void usb_serial_generic_process_read_urb(struct urb *urb); +extern int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port, + void **dest, size_t size, const void *src, size_t count); extern int usb_serial_handle_sysrq_char(struct tty_struct *tty, struct usb_serial_port *port, unsigned int ch); -- cgit v1.2.3 From 27c7acf22047fbe4ec4cc36b7c2610dba227697c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 5 May 2010 23:57:37 +0200 Subject: USB: serial: reimplement generic fifo-based writes Reimplement fifo-based writes in the generic driver using a multiple pre-allocated urb scheme. In contrast to multi-urb writes, no allocations (of urbs or buffers) are made during run-time and there is less pressure on the host stack queues as currently only two urbs are used (implementation is generic and can handle more than two urbs as well, though). Initial tests using ftdi_sio show that the implementation achieves the same (maximum) throughput at high baudrates as multi-urb writes. The CPU usage is much lower than for multi-urb writes for small write requests and only slightly higher for large (e.g. 2k) requests (due to extra copy via fifo?). Also outperforms multi-urb writes for small write requests on an embedded arm-9 system, where multi-urb writes are CPU-bound at high baudrates (perf reveals that a lot of time is spent in the host stack enqueue function -- could perhaps be a bug as well). Keeping the original write_urb, buffer and flag for now as there are other drivers depending on them. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 59 +++++++++++++++++++++++++++-------------- drivers/usb/serial/usb-serial.c | 33 +++++++++++++++++++++++ include/linux/usb/serial.h | 12 +++++++++ 3 files changed, 84 insertions(+), 20 deletions(-) (limited to 'drivers/usb/serial/generic.c') 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++; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 8249fd8381fb..941c2d409f85 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -548,8 +548,12 @@ static void usb_serial_port_work(struct work_struct *work) static void kill_traffic(struct usb_serial_port *port) { + int i; + usb_kill_urb(port->read_urb); usb_kill_urb(port->write_urb); + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) + usb_kill_urb(port->write_urbs[i]); /* * This is tricky. * Some drivers submit the read_urb in the @@ -568,6 +572,7 @@ static void kill_traffic(struct usb_serial_port *port) static void port_release(struct device *dev) { struct usb_serial_port *port = to_usb_serial_port(dev); + int i; dbg ("%s - %s", __func__, dev_name(dev)); @@ -582,6 +587,10 @@ static void port_release(struct device *dev) usb_free_urb(port->write_urb); usb_free_urb(port->interrupt_in_urb); usb_free_urb(port->interrupt_out_urb); + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) { + usb_free_urb(port->write_urbs[i]); + kfree(port->bulk_out_buffers[i]); + } kfifo_free(&port->write_fifo); kfree(port->bulk_in_buffer); kfree(port->bulk_out_buffer); @@ -920,6 +929,8 @@ int usb_serial_probe(struct usb_interface *interface, } for (i = 0; i < num_bulk_out; ++i) { + int j; + endpoint = bulk_out_endpoint[i]; port = serial->port[i]; port->write_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -945,6 +956,28 @@ int usb_serial_probe(struct usb_interface *interface, endpoint->bEndpointAddress), port->bulk_out_buffer, buffer_size, serial->type->write_bulk_callback, port); + for (j = 0; j < ARRAY_SIZE(port->write_urbs); ++j) { + set_bit(j, &port->write_urbs_free); + port->write_urbs[j] = usb_alloc_urb(0, GFP_KERNEL); + if (!port->write_urbs[j]) { + dev_err(&interface->dev, + "No free urbs available\n"); + goto probe_error; + } + port->bulk_out_buffers[j] = kmalloc(buffer_size, + GFP_KERNEL); + if (!port->bulk_out_buffers[j]) { + dev_err(&interface->dev, + "Couldn't allocate bulk_out_buffer\n"); + goto probe_error; + } + usb_fill_bulk_urb(port->write_urbs[j], dev, + usb_sndbulkpipe(dev, + endpoint->bEndpointAddress), + port->bulk_out_buffers[j], buffer_size, + serial->type->write_bulk_callback, + port); + } } if (serial->type->read_int_callback) { diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index a4c99ea390e7..70b6d6b28997 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -35,6 +35,9 @@ enum port_dev_state { PORT_UNREGISTERING, }; +/* USB serial flags */ +#define USB_SERIAL_WRITE_BUSY 0 + /** * usb_serial_port: structure for the specific ports of a device. * @serial: pointer back to the struct usb_serial owner of this port. @@ -60,10 +63,14 @@ enum port_dev_state { * @write_urb: pointer to the bulk out struct urb for this port. * @write_fifo: kfifo used to buffer outgoing data * @write_urb_busy: port`s writing status + * @bulk_out_buffers: pointers to the bulk out buffers for this port + * @write_urbs: pointers to the bulk out urbs for this port + * @write_urbs_free: status bitmap the for bulk out urbs * @tx_bytes: number of bytes currently in host stack queues * @tx_urbs: number of urbs currently in host stack queues * @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this * port. + * @flags: usb serial port flags * @write_wait: a wait_queue_head_t used by the port. * @work: work queue entry for the line discipline waking up. * @throttled: nonzero if the read urb is inactive to throttle the device @@ -98,11 +105,16 @@ struct usb_serial_port { struct urb *write_urb; struct kfifo write_fifo; int write_urb_busy; + + unsigned char *bulk_out_buffers[2]; + struct urb *write_urbs[2]; + unsigned long write_urbs_free; __u8 bulk_out_endpointAddress; int tx_bytes; int tx_urbs; + unsigned long flags; wait_queue_head_t write_wait; struct work_struct work; char throttled; -- cgit v1.2.3 From c23e5fc1f7dba228558b4a46e68f7af89515b13c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 5 May 2010 23:58:13 +0200 Subject: USB: serial: remove multi-urb write from generic driver Remove multi-urb write from the generic driver and simplify the prepare_write_buffer prototype: int (*prepare_write_buffer)(struct usb_serial_port *port, void *dest, size_t size); The default implementation simply fills dest with data from port write fifo but drivers can override it if they need to process the outgoing data (e.g. add headers). Turn ftdi_sio into a generic fifo-based driver, which lowers CPU usage significantly for small writes while retaining maximum throughput. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/aircable.c | 7 +- drivers/usb/serial/ftdi_sio.c | 59 ++++++----------- drivers/usb/serial/generic.c | 150 +++++++----------------------------------- include/linux/usb/serial.h | 8 +-- 4 files changed, 48 insertions(+), 176 deletions(-) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index cac5162937b3..8a990a763c21 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -84,9 +84,10 @@ static const struct usb_device_id id_table[] = { MODULE_DEVICE_TABLE(usb, id_table); static int aircable_prepare_write_buffer(struct usb_serial_port *port, - void **dest, size_t size, const void *src, size_t count) + void *dest, size_t size) { - unsigned char *buf = *dest; + int count; + unsigned char *buf = dest; count = kfifo_out_locked(&port->write_fifo, buf + HCI_HEADER_LENGTH, size - HCI_HEADER_LENGTH, &port->lock); @@ -185,8 +186,6 @@ static struct usb_serial_driver aircable_device = { .id_table = id_table, .num_ports = 1, .bulk_out_size = HCI_COMPLETE_FRAME, - /* Must modify prepare_write_buffer if multi_urb_write is changed. */ - .multi_urb_write = 0, .probe = aircable_probe, .process_read_urb = aircable_process_read_urb, .prepare_write_buffer = aircable_prepare_write_buffer, diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f515f32cde68..14f7a34d614c 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -781,7 +781,7 @@ static void ftdi_close(struct usb_serial_port *port); static void ftdi_dtr_rts(struct usb_serial_port *port, int on); static void ftdi_process_read_urb(struct urb *urb); static int ftdi_prepare_write_buffer(struct usb_serial_port *port, - void **dest, size_t size, const void *buf, size_t count); + void *dest, size_t size); static void ftdi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int ftdi_tiocmget(struct tty_struct *tty, struct file *file); @@ -808,8 +808,7 @@ static struct usb_serial_driver ftdi_sio_device = { .id_table = id_table_combined, .num_ports = 1, .bulk_in_size = 512, - /* Must modify prepare_write_buffer if multi_urb_write is changed. */ - .multi_urb_write = 1, + .bulk_out_size = 256, .probe = ftdi_sio_probe, .port_probe = ftdi_sio_port_probe, .port_remove = ftdi_sio_port_remove, @@ -1531,15 +1530,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) quirk->port_probe(priv); priv->port = port; - - /* Free port's existing write urb and transfer buffer. */ - if (port->write_urb) { - usb_free_urb(port->write_urb); - port->write_urb = NULL; - } - kfree(port->bulk_out_buffer); - port->bulk_out_buffer = NULL; - usb_set_serial_port_data(port, priv); ftdi_determine_type(port); @@ -1734,8 +1724,7 @@ static void ftdi_close(struct usb_serial_port *port) dbg("%s", __func__); - /* shutdown our bulk read */ - usb_kill_urb(port->read_urb); + usb_serial_generic_close(port); kref_put(&priv->kref, ftdi_sio_priv_release); } @@ -1747,40 +1736,34 @@ static void ftdi_close(struct usb_serial_port *port) * The new devices do not require this byte */ static int ftdi_prepare_write_buffer(struct usb_serial_port *port, - void **dest, size_t size, const void *src, size_t count) + void *dest, size_t size) { struct ftdi_private *priv; - unsigned char *buffer; - int len; + int count; + unsigned long flags; priv = usb_get_serial_port_data(port); - len = count; - if (priv->chip_type == SIO && count != 0) - len += ((count - 1) / (priv->max_packet_size - 1)) + 1; - - buffer = kmalloc(len, GFP_ATOMIC); - if (!buffer) { - dev_err(&port->dev, "%s - could not allocate buffer\n", - __func__); - return -ENOMEM; - } - if (priv->chip_type == SIO) { - int i, msg_len; - - for (i = 0; i < len; i += priv->max_packet_size) { - msg_len = min_t(int, len - i, priv->max_packet_size) - 1; - buffer[i] = (msg_len << 2) + 1; - memcpy(&buffer[i + 1], src, msg_len); - src += msg_len; + unsigned char *buffer = dest; + int i, len, c; + + count = 0; + spin_lock_irqsave(&port->lock, flags); + for (i = 0; i < size - 1; i += priv->max_packet_size) { + len = min_t(int, size - i, priv->max_packet_size) - 1; + buffer[i] = (len << 2) + 1; + c = kfifo_out(&port->write_fifo, &buffer[i + 1], len); + if (!c) + break; + count += c + 1; } + spin_unlock_irqrestore(&port->lock, flags); } else { - memcpy(buffer, src, count); + count = kfifo_out_locked(&port->write_fifo, dest, size, + &port->lock); } - *dest = buffer; - return count; } diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 3ae17840175c..fcd30b841559 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -26,8 +26,6 @@ static int debug; -#define MAX_TX_URBS 40 - #ifdef CONFIG_USB_SERIAL_GENERIC static int generic_probe(struct usb_interface *interface, @@ -172,90 +170,9 @@ void usb_serial_generic_close(struct usb_serial_port *port) EXPORT_SYMBOL_GPL(usb_serial_generic_close); int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port, - void **dest, size_t size, const void *src, size_t count) + void *dest, size_t size) { - if (!*dest) { - size = count; - *dest = kmalloc(count, GFP_ATOMIC); - if (!*dest) { - dev_err(&port->dev, "%s - could not allocate buffer\n", - __func__); - return -ENOMEM; - } - } - if (src) { - count = size; - memcpy(*dest, src, size); - } else { - count = kfifo_out_locked(&port->write_fifo, *dest, size, - &port->lock); - } - return count; -} -EXPORT_SYMBOL_GPL(usb_serial_generic_prepare_write_buffer); - -static int usb_serial_multi_urb_write(struct tty_struct *tty, - struct usb_serial_port *port, const unsigned char *buf, int count) -{ - unsigned long flags; - struct urb *urb; - void *buffer; - int status; - - spin_lock_irqsave(&port->lock, flags); - if (port->tx_urbs == MAX_TX_URBS) { - spin_unlock_irqrestore(&port->lock, flags); - dbg("%s - write limit hit", __func__); - return 0; - } - port->tx_urbs++; - spin_unlock_irqrestore(&port->lock, flags); - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - dev_err(&port->dev, "%s - no free urbs available\n", __func__); - status = -ENOMEM; - goto err_urb; - } - - buffer = NULL; - count = min_t(int, count, PAGE_SIZE); - count = port->serial->type->prepare_write_buffer(port, &buffer, 0, - buf, count); - if (count < 0) { - status = count; - goto err_buf; - } - usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); - usb_fill_bulk_urb(urb, port->serial->dev, - usb_sndbulkpipe(port->serial->dev, - port->bulk_out_endpointAddress), - buffer, count, - port->serial->type->write_bulk_callback, port); - - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - dev_err(&port->dev, "%s - error submitting urb: %d\n", - __func__, status); - goto err; - } - spin_lock_irqsave(&port->lock, flags); - port->tx_bytes += urb->transfer_buffer_length; - spin_unlock_irqrestore(&port->lock, flags); - - usb_free_urb(urb); - - return count; -err: - kfree(buffer); -err_buf: - usb_free_urb(urb); -err_urb: - spin_lock_irqsave(&port->lock, flags); - port->tx_urbs--; - spin_unlock_irqrestore(&port->lock, flags); - - return status; + return kfifo_out_locked(&port->write_fifo, dest, size, &port->lock); } /** @@ -286,8 +203,8 @@ retry: urb = port->write_urbs[i]; count = port->serial->type->prepare_write_buffer(port, - &urb->transfer_buffer, - port->bulk_out_size, NULL, 0); + urb->transfer_buffer, + port->bulk_out_size); urb->transfer_buffer_length = count; usb_serial_debug_data(debug, &port->dev, __func__, count, urb->transfer_buffer); @@ -328,7 +245,6 @@ retry: int usb_serial_generic_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { - struct usb_serial *serial = port->serial; int result; dbg("%s - port %d", __func__, port->number); @@ -340,23 +256,18 @@ int usb_serial_generic_write(struct tty_struct *tty, if (!count) return 0; - if (serial->type->multi_urb_write) - return usb_serial_multi_urb_write(tty, port, buf, count); - count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock); result = usb_serial_generic_write_start(port); + if (result) + return result; - if (result >= 0) - result = count; - - return result; + return count; } EXPORT_SYMBOL_GPL(usb_serial_generic_write); int usb_serial_generic_write_room(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct usb_serial *serial = port->serial; unsigned long flags; int room; @@ -366,10 +277,7 @@ int usb_serial_generic_write_room(struct tty_struct *tty) return 0; spin_lock_irqsave(&port->lock, flags); - if (serial->type->multi_urb_write) - room = (MAX_TX_URBS - port->tx_urbs) * PAGE_SIZE; - else - room = kfifo_avail(&port->write_fifo); + room = kfifo_avail(&port->write_fifo); spin_unlock_irqrestore(&port->lock, flags); dbg("%s - returns %d", __func__, room); @@ -379,7 +287,6 @@ int usb_serial_generic_write_room(struct tty_struct *tty) int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct usb_serial *serial = port->serial; unsigned long flags; int chars; @@ -389,10 +296,7 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) return 0; spin_lock_irqsave(&port->lock, flags); - if (serial->type->multi_urb_write) - chars = port->tx_bytes; - else - chars = kfifo_len(&port->write_fifo) + port->tx_bytes; + chars = kfifo_len(&port->write_fifo) + port->tx_bytes; spin_unlock_irqrestore(&port->lock, flags); dbg("%s - returns %d", __func__, chars); @@ -479,35 +383,25 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) dbg("%s - port %d", __func__, port->number); - if (port->serial->type->multi_urb_write) { - kfree(urb->transfer_buffer); + 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->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; + set_bit(i, &port->write_urbs_free); + spin_unlock_irqrestore(&port->lock, flags); + + if (status) { + dbg("%s - non-zero urb status: %d", __func__, status); spin_lock_irqsave(&port->lock, flags); - port->tx_bytes -= urb->transfer_buffer_length; - set_bit(i, &port->write_urbs_free); + kfifo_reset_out(&port->write_fifo); spin_unlock_irqrestore(&port->lock, flags); - - if (status) { - spin_lock_irqsave(&port->lock, flags); - kfifo_reset_out(&port->write_fifo); - spin_unlock_irqrestore(&port->lock, flags); - } else { - usb_serial_generic_write_start(port); - } + } else { + usb_serial_generic_write_start(port); } - if (status) - dbg("%s - non-zero urb status: %d", __func__, status); - usb_serial_port_softint(port); } EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 70b6d6b28997..061c997ae0cf 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -67,7 +67,6 @@ enum port_dev_state { * @write_urbs: pointers to the bulk out urbs for this port * @write_urbs_free: status bitmap the for bulk out urbs * @tx_bytes: number of bytes currently in host stack queues - * @tx_urbs: number of urbs currently in host stack queues * @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this * port. * @flags: usb serial port flags @@ -112,7 +111,6 @@ struct usb_serial_port { __u8 bulk_out_endpointAddress; int tx_bytes; - int tx_urbs; unsigned long flags; wait_queue_head_t write_wait; @@ -238,8 +236,6 @@ struct usb_serial_driver { struct usb_driver *usb_driver; struct usb_dynids dynids; - unsigned char multi_urb_write:1; - size_t bulk_in_size; size_t bulk_out_size; @@ -291,7 +287,7 @@ struct usb_serial_driver { void (*process_read_urb)(struct urb *urb); /* Called by the generic write implementation */ int (*prepare_write_buffer)(struct usb_serial_port *port, - void **dest, size_t size, const void *src, size_t count); + void *dest, size_t size); }; #define to_usb_serial_driver(d) \ container_of(d, struct usb_serial_driver, driver) @@ -345,7 +341,7 @@ extern int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, gfp_t mem_flags); extern void usb_serial_generic_process_read_urb(struct urb *urb); extern int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port, - void **dest, size_t size, const void *src, size_t count); + void *dest, size_t size); extern int usb_serial_handle_sysrq_char(struct tty_struct *tty, struct usb_serial_port *port, unsigned int ch); -- cgit v1.2.3 From 56a1df46c19150db0a9b0f0c14e0b1d42e7f32d4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 15 May 2010 17:53:44 +0200 Subject: USB: serial: add special case for processing of empty read urbs Return immediately from generic process_read_urb if urb is empty. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb/serial/generic.c') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index fcd30b841559..a817ced82835 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -324,6 +324,9 @@ void usb_serial_generic_process_read_urb(struct urb *urb) char *ch = (char *)urb->transfer_buffer; int i; + if (!urb->actual_length) + return; + tty = tty_port_tty_get(&port->port); if (!tty) return; -- cgit v1.2.3