Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: add flow control to usb-serial generic driver.

I added two fields to struct usb_serial_port to keep track of the
throttle state. Other usb-serial drivers typically use private data for
such things, but the generic driver can not really do that because some
of its code is also used by other drivers (which may have their own
private data needs).

As it is, I am not sure that this patch is useful in all scenarios.
It is certainly helpful for low-bandwidth devices that can hold their
data in response to throttling. But for devices that pump data in
real-time as fast as possible (webcam, A/D converter, etc), throttling
may actually cause more data loss.

From: Joris van Rantwijk <jorispubl@xs4all.nl>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Joris van Rantwijk and committed by
Greg Kroah-Hartman
253ca923 b544d749

+90 -18
+84 -18
drivers/usb/serial/generic.c
··· 66 66 .num_bulk_out = NUM_DONT_CARE, 67 67 .num_ports = 1, 68 68 .shutdown = usb_serial_generic_shutdown, 69 + .throttle = usb_serial_generic_throttle, 70 + .unthrottle = usb_serial_generic_unthrottle, 69 71 }; 70 72 71 73 static int generic_probe(struct usb_interface *interface, ··· 117 115 { 118 116 struct usb_serial *serial = port->serial; 119 117 int result = 0; 118 + unsigned long flags; 120 119 121 120 dbg("%s - port %d", __FUNCTION__, port->number); 122 121 ··· 127 124 if (port->tty) 128 125 port->tty->low_latency = 1; 129 126 130 - /* if we have a bulk interrupt, start reading from it */ 127 + /* clear the throttle flags */ 128 + spin_lock_irqsave(&port->lock, flags); 129 + port->throttled = 0; 130 + port->throttle_req = 0; 131 + spin_unlock_irqrestore(&port->lock, flags); 132 + 133 + /* if we have a bulk endpoint, start reading from it */ 131 134 if (serial->num_bulk_in) { 132 135 /* Start reading from the device */ 133 136 usb_fill_bulk_urb (port->read_urb, serial->dev, ··· 262 253 return (chars); 263 254 } 264 255 265 - void usb_serial_generic_read_bulk_callback (struct urb *urb) 256 + /* Push data to tty layer and resubmit the bulk read URB */ 257 + static void flush_and_resubmit_read_urb (struct usb_serial_port *port) 266 258 { 267 - struct usb_serial_port *port = (struct usb_serial_port *)urb->context; 268 259 struct usb_serial *serial = port->serial; 269 - struct tty_struct *tty; 270 - unsigned char *data = urb->transfer_buffer; 260 + struct urb *urb = port->read_urb; 261 + struct tty_struct *tty = port->tty; 271 262 int result; 272 263 273 - dbg("%s - port %d", __FUNCTION__, port->number); 274 - 275 - if (urb->status) { 276 - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); 277 - return; 278 - } 279 - 280 - usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); 281 - 282 - tty = port->tty; 264 + /* Push data to tty */ 283 265 if (tty && urb->actual_length) { 284 266 tty_buffer_request_room(tty, urb->actual_length); 285 - tty_insert_flip_string(tty, data, urb->actual_length); 286 - tty_flip_buffer_push(tty); 267 + tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length); 268 + tty_flip_buffer_push(tty); /* is this allowed from an URB callback ? */ 287 269 } 288 270 289 - /* Continue trying to always read */ 271 + /* Continue reading from device */ 290 272 usb_fill_bulk_urb (port->read_urb, serial->dev, 291 273 usb_rcvbulkpipe (serial->dev, 292 274 port->bulk_in_endpointAddress), ··· 289 289 result = usb_submit_urb(port->read_urb, GFP_ATOMIC); 290 290 if (result) 291 291 dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); 292 + } 293 + 294 + void usb_serial_generic_read_bulk_callback (struct urb *urb) 295 + { 296 + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; 297 + unsigned char *data = urb->transfer_buffer; 298 + int is_throttled; 299 + unsigned long flags; 300 + 301 + dbg("%s - port %d", __FUNCTION__, port->number); 302 + 303 + if (urb->status) { 304 + dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); 305 + return; 306 + } 307 + 308 + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); 309 + 310 + /* Throttle the device if requested by tty */ 311 + if (urb->actual_length) { 312 + spin_lock_irqsave(&port->lock, flags); 313 + is_throttled = port->throttled = port->throttle_req; 314 + spin_unlock_irqrestore(&port->lock, flags); 315 + if (is_throttled) { 316 + /* Let the received data linger in the read URB; 317 + * usb_serial_generic_unthrottle() will pick it 318 + * up later. */ 319 + dbg("%s - throttling device", __FUNCTION__); 320 + return; 321 + } 322 + } 323 + 324 + /* Handle data and continue reading from device */ 325 + flush_and_resubmit_read_urb(port); 292 326 } 293 327 EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); 294 328 ··· 341 307 usb_serial_port_softint(port); 342 308 } 343 309 EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); 310 + 311 + void usb_serial_generic_throttle (struct usb_serial_port *port) 312 + { 313 + unsigned long flags; 314 + 315 + dbg("%s - port %d", __FUNCTION__, port->number); 316 + 317 + /* Set the throttle request flag. It will be picked up 318 + * by usb_serial_generic_read_bulk_callback(). */ 319 + spin_lock_irqsave(&port->lock, flags); 320 + port->throttle_req = 1; 321 + spin_unlock_irqrestore(&port->lock, flags); 322 + } 323 + 324 + void usb_serial_generic_unthrottle (struct usb_serial_port *port) 325 + { 326 + int was_throttled; 327 + unsigned long flags; 328 + 329 + dbg("%s - port %d", __FUNCTION__, port->number); 330 + 331 + /* Clear the throttle flags */ 332 + spin_lock_irqsave(&port->lock, flags); 333 + was_throttled = port->throttled; 334 + port->throttled = port->throttle_req = 0; 335 + spin_unlock_irqrestore(&port->lock, flags); 336 + 337 + if (was_throttled) { 338 + /* Handle pending data and resume reading from device */ 339 + flush_and_resubmit_read_urb(port); 340 + } 341 + } 344 342 345 343 void usb_serial_generic_shutdown (struct usb_serial *serial) 346 344 {
+6
include/linux/usb/serial.h
··· 54 54 * @write_wait: a wait_queue_head_t used by the port. 55 55 * @work: work queue entry for the line discipline waking up. 56 56 * @open_count: number of times this port has been opened. 57 + * @throttled: nonzero if the read urb is inactive to throttle the device 58 + * @throttle_req: nonzero if the tty wants to throttle us 57 59 * 58 60 * This structure is used by the usb-serial core and drivers for the specific 59 61 * ports of a device. ··· 90 88 wait_queue_head_t write_wait; 91 89 struct work_struct work; 92 90 int open_count; 91 + char throttled; 92 + char throttle_req; 93 93 struct device dev; 94 94 }; 95 95 #define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev) ··· 273 269 extern int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port); 274 270 extern void usb_serial_generic_read_bulk_callback (struct urb *urb); 275 271 extern void usb_serial_generic_write_bulk_callback (struct urb *urb); 272 + extern void usb_serial_generic_throttle (struct usb_serial_port *port); 273 + extern void usb_serial_generic_unthrottle (struct usb_serial_port *port); 276 274 extern void usb_serial_generic_shutdown (struct usb_serial *serial); 277 275 extern int usb_serial_generic_register (int debug); 278 276 extern void usb_serial_generic_deregister (void);