aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoris van Rantwijk <jorispubl@xs4all.nl>2007-02-01 14:08:18 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2007-02-16 18:32:18 -0500
commit253ca923281aec6975ec4028ddbc58e865d8d13d (patch)
tree837b437f40ecb75174917e0623b6f4e2cdcc6f46
parentb544d7499cc47fc26e9dbacd7b9cabc67d2bdf2e (diff)
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>
-rw-r--r--drivers/usb/serial/generic.c102
-rw-r--r--include/linux/usb/serial.h6
2 files changed, 90 insertions, 18 deletions
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 601e0648dec6..53baeec8f265 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -66,6 +66,8 @@ struct usb_serial_driver usb_serial_generic_device = {
66 .num_bulk_out = NUM_DONT_CARE, 66 .num_bulk_out = NUM_DONT_CARE,
67 .num_ports = 1, 67 .num_ports = 1,
68 .shutdown = usb_serial_generic_shutdown, 68 .shutdown = usb_serial_generic_shutdown,
69 .throttle = usb_serial_generic_throttle,
70 .unthrottle = usb_serial_generic_unthrottle,
69}; 71};
70 72
71static int generic_probe(struct usb_interface *interface, 73static int generic_probe(struct usb_interface *interface,
@@ -115,6 +117,7 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)
115{ 117{
116 struct usb_serial *serial = port->serial; 118 struct usb_serial *serial = port->serial;
117 int result = 0; 119 int result = 0;
120 unsigned long flags;
118 121
119 dbg("%s - port %d", __FUNCTION__, port->number); 122 dbg("%s - port %d", __FUNCTION__, port->number);
120 123
@@ -124,7 +127,13 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)
124 if (port->tty) 127 if (port->tty)
125 port->tty->low_latency = 1; 128 port->tty->low_latency = 1;
126 129
127 /* if we have a bulk interrupt, start reading from it */ 130 /* clear the throttle flags */
131 spin_lock_irqsave(&port->lock, flags);
132 port->throttled = 0;
133 port->throttle_req = 0;
134 spin_unlock_irqrestore(&port->lock, flags);
135
136 /* if we have a bulk endpoint, start reading from it */
128 if (serial->num_bulk_in) { 137 if (serial->num_bulk_in) {
129 /* Start reading from the device */ 138 /* Start reading from the device */
130 usb_fill_bulk_urb (port->read_urb, serial->dev, 139 usb_fill_bulk_urb (port->read_urb, serial->dev,
@@ -253,31 +262,22 @@ int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port)
253 return (chars); 262 return (chars);
254} 263}
255 264
256void usb_serial_generic_read_bulk_callback (struct urb *urb) 265/* Push data to tty layer and resubmit the bulk read URB */
266static void flush_and_resubmit_read_urb (struct usb_serial_port *port)
257{ 267{
258 struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
259 struct usb_serial *serial = port->serial; 268 struct usb_serial *serial = port->serial;
260 struct tty_struct *tty; 269 struct urb *urb = port->read_urb;
261 unsigned char *data = urb->transfer_buffer; 270 struct tty_struct *tty = port->tty;
262 int result; 271 int result;
263 272
264 dbg("%s - port %d", __FUNCTION__, port->number); 273 /* Push data to tty */
265
266 if (urb->status) {
267 dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
268 return;
269 }
270
271 usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
272
273 tty = port->tty;
274 if (tty && urb->actual_length) { 274 if (tty && urb->actual_length) {
275 tty_buffer_request_room(tty, urb->actual_length); 275 tty_buffer_request_room(tty, urb->actual_length);
276 tty_insert_flip_string(tty, data, urb->actual_length); 276 tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
277 tty_flip_buffer_push(tty); 277 tty_flip_buffer_push(tty); /* is this allowed from an URB callback ? */
278 } 278 }
279 279
280 /* Continue trying to always read */ 280 /* Continue reading from device */
281 usb_fill_bulk_urb (port->read_urb, serial->dev, 281 usb_fill_bulk_urb (port->read_urb, serial->dev,
282 usb_rcvbulkpipe (serial->dev, 282 usb_rcvbulkpipe (serial->dev,
283 port->bulk_in_endpointAddress), 283 port->bulk_in_endpointAddress),
@@ -290,6 +290,40 @@ void usb_serial_generic_read_bulk_callback (struct urb *urb)
290 if (result) 290 if (result)
291 dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); 291 dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
292} 292}
293
294void 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);
326}
293EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); 327EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);
294 328
295void usb_serial_generic_write_bulk_callback (struct urb *urb) 329void usb_serial_generic_write_bulk_callback (struct urb *urb)
@@ -308,6 +342,38 @@ void usb_serial_generic_write_bulk_callback (struct urb *urb)
308} 342}
309EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); 343EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback);
310 344
345void usb_serial_generic_throttle (struct usb_serial_port *port)
346{
347 unsigned long flags;
348
349 dbg("%s - port %d", __FUNCTION__, port->number);
350
351 /* Set the throttle request flag. It will be picked up
352 * by usb_serial_generic_read_bulk_callback(). */
353 spin_lock_irqsave(&port->lock, flags);
354 port->throttle_req = 1;
355 spin_unlock_irqrestore(&port->lock, flags);
356}
357
358void usb_serial_generic_unthrottle (struct usb_serial_port *port)
359{
360 int was_throttled;
361 unsigned long flags;
362
363 dbg("%s - port %d", __FUNCTION__, port->number);
364
365 /* Clear the throttle flags */
366 spin_lock_irqsave(&port->lock, flags);
367 was_throttled = port->throttled;
368 port->throttled = port->throttle_req = 0;
369 spin_unlock_irqrestore(&port->lock, flags);
370
371 if (was_throttled) {
372 /* Handle pending data and resume reading from device */
373 flush_and_resubmit_read_urb(port);
374 }
375}
376
311void usb_serial_generic_shutdown (struct usb_serial *serial) 377void usb_serial_generic_shutdown (struct usb_serial *serial)
312{ 378{
313 int i; 379 int i;
diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h
index 33dcd8576696..32acbae28d24 100644
--- a/include/linux/usb/serial.h
+++ b/include/linux/usb/serial.h
@@ -54,6 +54,8 @@
54 * @write_wait: a wait_queue_head_t used by the port. 54 * @write_wait: a wait_queue_head_t used by the port.
55 * @work: work queue entry for the line discipline waking up. 55 * @work: work queue entry for the line discipline waking up.
56 * @open_count: number of times this port has been opened. 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 * This structure is used by the usb-serial core and drivers for the specific 60 * This structure is used by the usb-serial core and drivers for the specific
59 * ports of a device. 61 * ports of a device.
@@ -88,6 +90,8 @@ struct usb_serial_port {
88 wait_queue_head_t write_wait; 90 wait_queue_head_t write_wait;
89 struct work_struct work; 91 struct work_struct work;
90 int open_count; 92 int open_count;
93 char throttled;
94 char throttle_req;
91 struct device dev; 95 struct device dev;
92}; 96};
93#define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev) 97#define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev)
@@ -269,6 +273,8 @@ extern int usb_serial_generic_write_room (struct usb_serial_port *port);
269extern int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port); 273extern int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port);
270extern void usb_serial_generic_read_bulk_callback (struct urb *urb); 274extern void usb_serial_generic_read_bulk_callback (struct urb *urb);
271extern void usb_serial_generic_write_bulk_callback (struct urb *urb); 275extern void usb_serial_generic_write_bulk_callback (struct urb *urb);
276extern void usb_serial_generic_throttle (struct usb_serial_port *port);
277extern void usb_serial_generic_unthrottle (struct usb_serial_port *port);
272extern void usb_serial_generic_shutdown (struct usb_serial *serial); 278extern void usb_serial_generic_shutdown (struct usb_serial *serial);
273extern int usb_serial_generic_register (int debug); 279extern int usb_serial_generic_register (int debug);
274extern void usb_serial_generic_deregister (void); 280extern void usb_serial_generic_deregister (void);