--- a/vizzini.c 2013-03-26 11:34:39.362546946 -0500 +++ b/vizzini.c 2013-03-27 10:43:03.123460275 -0500 @@ -92,6 +92,9 @@ #define N_OUT_URB 4 #define IN_BUFLEN 4096 +#define READ_URB_RUNNING 0 +#define READ_URB_STOPPED 1 + static int debug; @@ -169,6 +172,7 @@ struct urb *in_urbs[N_IN_URB]; char *in_buffer[N_IN_URB]; + int in_urb_flags[N_IN_URB]; int ctrlin; int ctrlout; @@ -995,6 +999,8 @@ char *transfer_buffer = urb->transfer_buffer; int length, room, have_extra_byte; int err; + unsigned long flags; + int j; if (debug) dev_dbg(&port->dev, "%s: %p\n", __func__, urb); @@ -1085,9 +1091,26 @@ portdata->extra_byte = transfer_buffer[urb->actual_length - 1]; } - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err) - dev_err(&port->dev, "resubmit read urb failed. (%d)\n", err); + /* throttle device if requested */ + spin_lock_irqsave(&port->lock, flags); + port->throttled = port->throttle_req; + if (! port->throttled) { + spin_unlock_irqrestore(&port->lock, flags); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) + dev_err(&port->dev, "resubmit read urb failed. (%d)\n", err); + } else { + spin_unlock_irqrestore(&port->lock, flags); + for (j = 0; j < N_IN_URB; j++) { + if (portdata->in_urbs[j] == urb) { + if (debug) dev_dbg(&port->dev, "throttling urb %d\n", j); + spin_lock_irqsave(&port->lock, flags); + portdata->in_urb_flags[j] = READ_URB_STOPPED; + spin_unlock_irqrestore(&port->lock, flags); + break; + } + } + } } @@ -1172,6 +1195,7 @@ int i; struct urb *urb; int result; + unsigned long flags; portdata = usb_get_serial_port_data(port); @@ -1179,10 +1203,19 @@ acm_set_control(port, portdata->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS); + /* unthrottle device in case TTY was closed while throttled */ + spin_lock_irqsave(&port->lock, flags); + port->throttled = 0; + port->throttle_req = 0; + spin_unlock_irqrestore(&port->lock, flags); + /* Reset low level data toggle and start reading from endpoints */ for (i = 0; i < N_IN_URB; i++) { if (debug) dev_dbg(&port->dev, "%s urb %d\n", __func__, i); + spin_lock_irqsave(&port->lock, flags); + portdata->in_urb_flags[i] = READ_URB_RUNNING; + spin_unlock_irqrestore(&port->lock, flags); urb = portdata->in_urbs[i]; if (!urb) continue; @@ -1207,8 +1240,6 @@ } } - tty->low_latency = 1; - /* start up the interrupt endpoint if we have one */ if (port->interrupt_in_urb) { result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); @@ -1592,6 +1623,51 @@ usb_serial_disconnect(interface); } +static void vizzini_throttle(struct tty_struct *tty) +{ + unsigned long flags; + struct usb_serial_port *port = tty->driver_data; + + if (debug) dev_dbg(&port->dev, "TTY buffer approaching limit, stopping USB reads\n"); + + spin_lock_irqsave(&port->lock, flags); + port->throttle_req = 1; + spin_unlock_irqrestore(&port->lock, flags); +} + +static void vizzini_unthrottle(struct tty_struct *tty) +{ + unsigned long flags; + unsigned int was_throttled; + struct usb_serial_port *port = tty->driver_data; + struct vizzini_port_private *portdata = usb_get_serial_port_data(port); + int err; + int i; + + if (debug) dev_dbg(&port->dev, "Resuming USB reads\n"); + + spin_lock_irqsave(&port->lock, flags); + was_throttled = port->throttled; + port->throttled = 0; + port->throttle_req = 0; + spin_unlock_irqrestore(&port->lock, flags); + + if (was_throttled) { + for (i = 0; i < N_IN_URB; i++) { + spin_lock_irqsave(&port->lock, flags); + if (portdata->in_urb_flags[i] == READ_URB_STOPPED) { + if (debug) dev_dbg(&port->dev, "unthrottling urb %d\n", i); + portdata->in_urb_flags[i] = READ_URB_RUNNING; + spin_unlock_irqrestore(&port->lock, flags); + err = usb_submit_urb(portdata->in_urbs[i], GFP_ATOMIC); + if (err) + dev_err(&port->dev, "resubmit read urb failed. (%d)\n", err); + } else { + spin_unlock_irqrestore(&port->lock, flags); + } + } + } +} static struct usb_serial_driver vizzini_device = { @@ -1606,6 +1682,8 @@ .probe = vizzini_probe, .open = vizzini_open, .close = vizzini_close, + .throttle = vizzini_throttle, + .unthrottle = vizzini_unthrottle, .write = vizzini_write, .write_room = vizzini_write_room, .ioctl = vizzini_ioctl,