aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorJason Wessel <jason.wessel@windriver.com>2009-05-11 16:24:07 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-06-16 00:44:45 -0400
commit715b1dc01fe44537e8fce9566e4bb48d6821d84b (patch)
treeb1fc0b5c61a7317e7104468afddad544fb3fc34c /drivers/usb
parentb0cda8c5f7b652c6c27bcb3891d174534d2f1a91 (diff)
USB: usb_debug, usb_generic_serial: implement multi urb write
The usb_debug driver, when used as the console, will always fail to insert the carriage return and new line sequence as well as randomly drop console output. This is a result of only having the single write_urb and that the tty layer will have a lock that prevents the processing of the back to back urb requests. The solution is to allow more than one urb to be outstanding and have a slightly deeper transmit queue. The idea and some code is borrowed from the ftdi_sio usb driver. The generic usb serial driver was modified so as to allow the classic method of 1 write urb, or a multi write urb scheme with N allowed outstanding urbs where N is controlled by max_in_flight_urbs. When max_in_flight_urbs in a "struct usb_serial_driver" is non zero the multi write urb scheme will be used. The size of 4000 was selected for the usb_debug driver so that the driver lowers possibility of losing the queued console messages during the kernel startup. Signed-off-by: Jason Wessel <jason.wessel@windriver.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/serial/generic.c121
-rw-r--r--drivers/usb/serial/usb_debug.c2
2 files changed, 116 insertions, 7 deletions
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index be82ea956720..c919686521ff 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -190,6 +190,88 @@ void usb_serial_generic_close(struct usb_serial_port *port)
190 generic_cleanup(port); 190 generic_cleanup(port);
191} 191}
192 192
193static int usb_serial_multi_urb_write(struct tty_struct *tty,
194 struct usb_serial_port *port, const unsigned char *buf, int count)
195{
196 unsigned long flags;
197 struct urb *urb;
198 unsigned char *buffer;
199 int status;
200 int towrite;
201 int bwrite = 0;
202
203 dbg("%s - port %d", __func__, port->number);
204
205 if (count == 0)
206 dbg("%s - write request of 0 bytes", __func__);
207
208 while (count > 0) {
209 towrite = (count > port->bulk_out_size) ?
210 port->bulk_out_size : count;
211 spin_lock_irqsave(&port->lock, flags);
212 if (port->urbs_in_flight >
213 port->serial->type->max_in_flight_urbs) {
214 spin_unlock_irqrestore(&port->lock, flags);
215 dbg("%s - write limit hit\n", __func__);
216 return bwrite;
217 }
218 port->tx_bytes_flight += towrite;
219 port->urbs_in_flight++;
220 spin_unlock_irqrestore(&port->lock, flags);
221
222 buffer = kmalloc(towrite, GFP_ATOMIC);
223 if (!buffer) {
224 dev_err(&port->dev,
225 "%s ran out of kernel memory for urb ...\n", __func__);
226 goto error_no_buffer;
227 }
228
229 urb = usb_alloc_urb(0, GFP_ATOMIC);
230 if (!urb) {
231 dev_err(&port->dev, "%s - no more free urbs\n",
232 __func__);
233 goto error_no_urb;
234 }
235
236 /* Copy data */
237 memcpy(buffer, buf + bwrite, towrite);
238 usb_serial_debug_data(debug, &port->dev, __func__,
239 towrite, buffer);
240 /* fill the buffer and send it */
241 usb_fill_bulk_urb(urb, port->serial->dev,
242 usb_sndbulkpipe(port->serial->dev,
243 port->bulk_out_endpointAddress),
244 buffer, towrite,
245 usb_serial_generic_write_bulk_callback, port);
246
247 status = usb_submit_urb(urb, GFP_ATOMIC);
248 if (status) {
249 dev_err(&port->dev,
250 "%s - failed submitting write urb, error %d\n",
251 __func__, status);
252 goto error;
253 }
254
255 /* This urb is the responsibility of the host driver now */
256 usb_free_urb(urb);
257 dbg("%s write: %d", __func__, towrite);
258 count -= towrite;
259 bwrite += towrite;
260 }
261 return bwrite;
262
263error:
264 usb_free_urb(urb);
265error_no_urb:
266 kfree(buffer);
267error_no_buffer:
268 spin_lock_irqsave(&port->lock, flags);
269 port->urbs_in_flight--;
270 port->tx_bytes_flight -= towrite;
271 spin_unlock_irqrestore(&port->lock, flags);
272 return bwrite;
273}
274
193int usb_serial_generic_write(struct tty_struct *tty, 275int usb_serial_generic_write(struct tty_struct *tty,
194 struct usb_serial_port *port, const unsigned char *buf, int count) 276 struct usb_serial_port *port, const unsigned char *buf, int count)
195{ 277{
@@ -207,6 +289,11 @@ int usb_serial_generic_write(struct tty_struct *tty,
207 /* only do something if we have a bulk out endpoint */ 289 /* only do something if we have a bulk out endpoint */
208 if (serial->num_bulk_out) { 290 if (serial->num_bulk_out) {
209 unsigned long flags; 291 unsigned long flags;
292
293 if (serial->type->max_in_flight_urbs)
294 return usb_serial_multi_urb_write(tty, port,
295 buf, count);
296
210 spin_lock_irqsave(&port->lock, flags); 297 spin_lock_irqsave(&port->lock, flags);
211 if (port->write_urb_busy) { 298 if (port->write_urb_busy) {
212 spin_unlock_irqrestore(&port->lock, flags); 299 spin_unlock_irqrestore(&port->lock, flags);
@@ -257,15 +344,18 @@ int usb_serial_generic_write_room(struct tty_struct *tty)
257{ 344{
258 struct usb_serial_port *port = tty->driver_data; 345 struct usb_serial_port *port = tty->driver_data;
259 struct usb_serial *serial = port->serial; 346 struct usb_serial *serial = port->serial;
347 unsigned long flags;
260 int room = 0; 348 int room = 0;
261 349
262 dbg("%s - port %d", __func__, port->number); 350 dbg("%s - port %d", __func__, port->number);
263 351 spin_lock_irqsave(&port->lock, flags);
264 /* FIXME: Locking */ 352 if (serial->type->max_in_flight_urbs) {
265 if (serial->num_bulk_out) { 353 if (port->urbs_in_flight < serial->type->max_in_flight_urbs)
266 if (!(port->write_urb_busy))
267 room = port->bulk_out_size; 354 room = port->bulk_out_size;
355 } else if (serial->num_bulk_out && !(port->write_urb_busy)) {
356 room = port->bulk_out_size;
268 } 357 }
358 spin_unlock_irqrestore(&port->lock, flags);
269 359
270 dbg("%s - returns %d", __func__, room); 360 dbg("%s - returns %d", __func__, room);
271 return room; 361 return room;
@@ -276,11 +366,16 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
276 struct usb_serial_port *port = tty->driver_data; 366 struct usb_serial_port *port = tty->driver_data;
277 struct usb_serial *serial = port->serial; 367 struct usb_serial *serial = port->serial;
278 int chars = 0; 368 int chars = 0;
369 unsigned long flags;
279 370
280 dbg("%s - port %d", __func__, port->number); 371 dbg("%s - port %d", __func__, port->number);
281 372
282 /* FIXME: Locking */ 373 if (serial->type->max_in_flight_urbs) {
283 if (serial->num_bulk_out) { 374 spin_lock_irqsave(&port->lock, flags);
375 chars = port->tx_bytes_flight;
376 spin_unlock_irqrestore(&port->lock, flags);
377 } else if (serial->num_bulk_out) {
378 /* FIXME: Locking */
284 if (port->write_urb_busy) 379 if (port->write_urb_busy)
285 chars = port->write_urb->transfer_buffer_length; 380 chars = port->write_urb->transfer_buffer_length;
286 } 381 }
@@ -363,12 +458,24 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);
363 458
364void usb_serial_generic_write_bulk_callback(struct urb *urb) 459void usb_serial_generic_write_bulk_callback(struct urb *urb)
365{ 460{
461 unsigned long flags;
366 struct usb_serial_port *port = urb->context; 462 struct usb_serial_port *port = urb->context;
367 int status = urb->status; 463 int status = urb->status;
368 464
369 dbg("%s - port %d", __func__, port->number); 465 dbg("%s - port %d", __func__, port->number);
370 466
371 port->write_urb_busy = 0; 467 if (port->serial->type->max_in_flight_urbs) {
468 spin_lock_irqsave(&port->lock, flags);
469 --port->urbs_in_flight;
470 port->tx_bytes_flight -= urb->transfer_buffer_length;
471 if (port->urbs_in_flight < 0)
472 port->urbs_in_flight = 0;
473 spin_unlock_irqrestore(&port->lock, flags);
474 } else {
475 /* Handle the case for single urb mode */
476 port->write_urb_busy = 0;
477 }
478
372 if (status) { 479 if (status) {
373 dbg("%s - nonzero write bulk status received: %d", 480 dbg("%s - nonzero write bulk status received: %d",
374 __func__, status); 481 __func__, status);
diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c
index 6c9cbb59552a..a9427a8b8672 100644
--- a/drivers/usb/serial/usb_debug.c
+++ b/drivers/usb/serial/usb_debug.c
@@ -15,6 +15,7 @@
15#include <linux/usb.h> 15#include <linux/usb.h>
16#include <linux/usb/serial.h> 16#include <linux/usb/serial.h>
17 17
18#define URB_DEBUG_MAX_IN_FLIGHT_URBS 4000
18#define USB_DEBUG_MAX_PACKET_SIZE 8 19#define USB_DEBUG_MAX_PACKET_SIZE 8
19 20
20static struct usb_device_id id_table [] = { 21static struct usb_device_id id_table [] = {
@@ -46,6 +47,7 @@ static struct usb_serial_driver debug_device = {
46 .id_table = id_table, 47 .id_table = id_table,
47 .num_ports = 1, 48 .num_ports = 1,
48 .open = usb_debug_open, 49 .open = usb_debug_open,
50 .max_in_flight_urbs = URB_DEBUG_MAX_IN_FLIGHT_URBS,
49}; 51};
50 52
51static int __init debug_init(void) 53static int __init debug_init(void)