aboutsummaryrefslogtreecommitdiffstats
path: root/usb.current
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2009-10-08 11:04:13 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2009-10-08 11:04:13 -0700
commitd45b8296a27902697867a18f638e846c84985cb0 (patch)
tree6137eaa4cfbf78d6de99716ccabb9fe919dbe60e /usb.current
parent5ac84e99a60c34d072b93427ce362057e7f6c518 (diff)
downloadpatches-d45b8296a27902697867a18f638e846c84985cb0.tar.gz
staging patches and other fixes
Diffstat (limited to 'usb.current')
-rw-r--r--usb.current/usb-ftdi_sio-clean-up-read-completion-handler.patch81
-rw-r--r--usb.current/usb-ftdi_sio-re-implement-read-processing.patch522
-rw-r--r--usb.current/usb-ftdi_sio-remove-tty-low_latency.patch40
-rw-r--r--usb.current/usb-ftdi_sio-remove-unused-rx_byte-counter.patch63
4 files changed, 706 insertions, 0 deletions
diff --git a/usb.current/usb-ftdi_sio-clean-up-read-completion-handler.patch b/usb.current/usb-ftdi_sio-clean-up-read-completion-handler.patch
new file mode 100644
index 00000000000000..6468726c5d914f
--- /dev/null
+++ b/usb.current/usb-ftdi_sio-clean-up-read-completion-handler.patch
@@ -0,0 +1,81 @@
+From jhovold@gmail.com Thu Oct 8 09:54:30 2009
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 7 Oct 2009 20:05:06 +0200
+Subject: USB: ftdi_sio: clean up read completion handler
+To: Greg Kroah-Hartman <gregkh@suse.de>, Alan Cox <alan@linux.intel.com>
+Cc: Oliver Neukum <oliver@neukum.org>, Alan Stern <stern@rowland.harvard.edu>, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1254938707-6996-4-git-send-email-jhovold@gmail.com>
+
+
+Remove superfluous error checks in completion handler:
+
+ - No need to check private data and urb pointers as we check urb-status
+ before dereferencing priv (which is not freed until urb has been killed
+ on close).
+ - No need to check tty as it is checked again when processing.
+ - No need to check urb->number_of_packets on bulk urb.
+
+Note that both private data and tty are checked again before processing
+(possibly from work queue which also is cancelled on close).
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Cc: stable <stable@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ftdi_sio.c | 28 +---------------------------
+ 1 file changed, 1 insertion(+), 27 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -2008,39 +2008,14 @@ static int ftdi_chars_in_buffer(struct t
+ static void ftdi_read_bulk_callback(struct urb *urb)
+ {
+ struct usb_serial_port *port = urb->context;
+- struct tty_struct *tty;
+ struct ftdi_private *priv;
+ int status = urb->status;
+
+- if (urb->number_of_packets > 0) {
+- dev_err(&port->dev, "%s transfer_buffer_length %d "
+- "actual_length %d number of packets %d\n", __func__,
+- urb->transfer_buffer_length,
+- urb->actual_length, urb->number_of_packets);
+- dev_err(&port->dev, "%s transfer_flags %x\n", __func__,
+- urb->transfer_flags);
+- }
+-
+ dbg("%s - port %d", __func__, port->number);
+
+ if (port->port.count <= 0)
+ return;
+
+- tty = tty_port_tty_get(&port->port);
+- if (!tty) {
+- dbg("%s - bad tty pointer - exiting", __func__);
+- return;
+- }
+-
+- priv = usb_get_serial_port_data(port);
+- if (!priv) {
+- dbg("%s - bad port private data pointer - exiting", __func__);
+- goto out;
+- }
+-
+- if (urb != port->read_urb)
+- dev_err(&port->dev, "%s - Not my urb!\n", __func__);
+-
+ if (status) {
+ /* This will happen at close every time so it is a dbg not an
+ err */
+@@ -2048,9 +2023,8 @@ static void ftdi_read_bulk_callback(stru
+ goto out;
+ }
+
++ priv = usb_get_serial_port_data(port);
+ ftdi_process_read(&priv->rx_work.work);
+-out:
+- tty_kref_put(tty);
+ } /* ftdi_read_bulk_callback */
+
+
diff --git a/usb.current/usb-ftdi_sio-re-implement-read-processing.patch b/usb.current/usb-ftdi_sio-re-implement-read-processing.patch
new file mode 100644
index 00000000000000..c3b66b67ab14b2
--- /dev/null
+++ b/usb.current/usb-ftdi_sio-re-implement-read-processing.patch
@@ -0,0 +1,522 @@
+From jhovold@gmail.com Thu Oct 8 09:54:58 2009
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 7 Oct 2009 20:05:07 +0200
+Subject: USB: ftdi_sio: re-implement read processing
+To: Greg Kroah-Hartman <gregkh@suse.de>, Alan Cox <alan@linux.intel.com>
+Cc: Oliver Neukum <oliver@neukum.org>, Alan Stern <stern@rowland.harvard.edu>, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1254938707-6996-5-git-send-email-jhovold@gmail.com>
+
+
+ - Re-structure read processing.
+ - Kill obsolete work queue and always push to tty in completion handler.
+ - Use tty_insert_flip_string instead of per character push when
+ possible.
+ - Fix stalled-read regression in 2.6.31 by using urb status to
+ determine when port is closed rather than port count.
+ - Fix race with open/close by checking ASYNCB_INITIALIZED in
+ unthrottle.
+ - Kill private rx_flag and lock and use throttle flags in
+ usb_serial_port instead.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Cc: stable <stable@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ftdi_sio.c | 383 ++++++++++++++----------------------------
+ 1 file changed, 131 insertions(+), 252 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -76,12 +76,7 @@ struct ftdi_private {
+ unsigned long last_dtr_rts; /* saved modem control outputs */
+ wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
+ char prev_status, diff_status; /* Used for TIOCMIWAIT */
+- __u8 rx_flags; /* receive state flags (throttling) */
+- spinlock_t rx_lock; /* spinlock for receive state */
+- struct delayed_work rx_work;
+ struct usb_serial_port *port;
+- int rx_processed;
+-
+ __u16 interface; /* FT2232C, FT2232H or FT4232H port interface
+ (0 for FT232/245) */
+
+@@ -736,10 +731,6 @@ static const char *ftdi_chip_name[] = {
+ /* Constants for read urb and write urb */
+ #define BUFSZ 512
+
+-/* rx_flags */
+-#define THROTTLED 0x01
+-#define ACTUALLY_THROTTLED 0x02
+-
+ /* Used for TIOCMIWAIT */
+ #define FTDI_STATUS_B0_MASK (FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD)
+ #define FTDI_STATUS_B1_MASK (FTDI_RS_BI)
+@@ -762,7 +753,7 @@ static int ftdi_write_room(struct tty_s
+ static int ftdi_chars_in_buffer(struct tty_struct *tty);
+ static void ftdi_write_bulk_callback(struct urb *urb);
+ static void ftdi_read_bulk_callback(struct urb *urb);
+-static void ftdi_process_read(struct work_struct *work);
++static void ftdi_process_read(struct usb_serial_port *port);
+ 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);
+@@ -1525,7 +1516,6 @@ static int ftdi_sio_port_probe(struct us
+ }
+
+ kref_init(&priv->kref);
+- spin_lock_init(&priv->rx_lock);
+ spin_lock_init(&priv->tx_lock);
+ init_waitqueue_head(&priv->delta_msr_wait);
+ /* This will push the characters through immediately rather
+@@ -1547,7 +1537,6 @@ static int ftdi_sio_port_probe(struct us
+ port->read_urb->transfer_buffer_length = BUFSZ;
+ }
+
+- INIT_DELAYED_WORK(&priv->rx_work, ftdi_process_read);
+ priv->port = port;
+
+ /* Free port's existing write urb and transfer buffer. */
+@@ -1684,6 +1673,26 @@ static int ftdi_sio_port_remove(struct u
+ return 0;
+ }
+
++static int ftdi_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;
++
++ usb_fill_bulk_urb(urb, serial->dev,
++ usb_rcvbulkpipe(serial->dev,
++ port->bulk_in_endpointAddress),
++ urb->transfer_buffer,
++ urb->transfer_buffer_length,
++ ftdi_read_bulk_callback, port);
++ result = usb_submit_urb(urb, mem_flags);
++ if (result)
++ dev_err(&port->dev,
++ "%s - failed submitting read urb, error %d\n",
++ __func__, result);
++ return result;
++}
++
+ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port)
+ { /* ftdi_open */
+ struct usb_device *dev = port->serial->dev;
+@@ -1717,23 +1726,14 @@ static int ftdi_open(struct tty_struct *
+ ftdi_set_termios(tty, port, tty->termios);
+
+ /* Not throttled */
+- spin_lock_irqsave(&priv->rx_lock, flags);
+- priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+- spin_unlock_irqrestore(&priv->rx_lock, flags);
++ spin_lock_irqsave(&port->lock, flags);
++ port->throttled = 0;
++ port->throttle_req = 0;
++ spin_unlock_irqrestore(&port->lock, flags);
+
+ /* Start reading from the device */
+- priv->rx_processed = 0;
+- usb_fill_bulk_urb(port->read_urb, dev,
+- usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress),
+- port->read_urb->transfer_buffer,
+- port->read_urb->transfer_buffer_length,
+- ftdi_read_bulk_callback, port);
+- result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+- if (result)
+- dev_err(&port->dev,
+- "%s - failed submitting read urb, error %d\n",
+- __func__, result);
+- else
++ result = ftdi_submit_read_urb(port, GFP_KERNEL);
++ if (!result)
+ kref_get(&priv->kref);
+
+ return result;
+@@ -1779,10 +1779,6 @@ static void ftdi_close(struct usb_serial
+
+ dbg("%s", __func__);
+
+-
+- /* cancel any scheduled reading */
+- cancel_delayed_work_sync(&priv->rx_work);
+-
+ /* shutdown our bulk read */
+ usb_kill_urb(port->read_urb);
+ kref_put(&priv->kref, ftdi_sio_priv_release);
+@@ -2005,236 +2001,121 @@ static int ftdi_chars_in_buffer(struct t
+ return buffered;
+ }
+
+-static void ftdi_read_bulk_callback(struct urb *urb)
++static int ftdi_process_packet(struct tty_struct *tty,
++ struct usb_serial_port *port, struct ftdi_private *priv,
++ char *packet, int len)
+ {
+- struct usb_serial_port *port = urb->context;
+- struct ftdi_private *priv;
+- int status = urb->status;
+-
+- dbg("%s - port %d", __func__, port->number);
+-
+- if (port->port.count <= 0)
+- return;
+-
+- if (status) {
+- /* This will happen at close every time so it is a dbg not an
+- err */
+- dbg("(this is ok on close) nonzero read bulk status received: %d", status);
+- goto out;
+- }
+-
+- priv = usb_get_serial_port_data(port);
+- ftdi_process_read(&priv->rx_work.work);
+-} /* ftdi_read_bulk_callback */
+-
+-
+-static void ftdi_process_read(struct work_struct *work)
+-{ /* ftdi_process_read */
+- struct ftdi_private *priv =
+- container_of(work, struct ftdi_private, rx_work.work);
+- struct usb_serial_port *port = priv->port;
+- struct urb *urb;
+- struct tty_struct *tty;
+- char error_flag;
+- unsigned char *data;
+-
+ int i;
+- int result;
+- int need_flip;
+- int packet_offset;
+- unsigned long flags;
++ char status;
++ char flag;
++ char *ch;
+
+ dbg("%s - port %d", __func__, port->number);
+
+- if (port->port.count <= 0)
+- return;
+-
+- tty = tty_port_tty_get(&port->port);
+- if (!tty) {
+- dbg("%s - bad tty pointer - exiting", __func__);
+- return;
++ if (len < 2) {
++ dbg("malformed packet");
++ return 0;
+ }
+
+- priv = usb_get_serial_port_data(port);
+- if (!priv) {
+- dbg("%s - bad port private data pointer - exiting", __func__);
+- goto out;
++ /* Compare new line status to the old one, signal if different/
++ N.B. packet may be processed more than once, but differences
++ are only processed once. */
++ status = packet[0] & FTDI_STATUS_B0_MASK;
++ if (status != priv->prev_status) {
++ priv->diff_status |= status ^ priv->prev_status;
++ wake_up_interruptible(&priv->delta_msr_wait);
++ priv->prev_status = status;
+ }
+
+- urb = port->read_urb;
+- if (!urb) {
+- dbg("%s - bad read_urb pointer - exiting", __func__);
+- goto out;
++ /*
++ * Although the device uses a bitmask and hence can have multiple
++ * errors on a packet - the order here sets the priority the error is
++ * returned to the tty layer.
++ */
++ flag = TTY_NORMAL;
++ if (packet[1] & FTDI_RS_OE) {
++ flag = TTY_OVERRUN;
++ dbg("OVERRRUN error");
+ }
+-
+- data = urb->transfer_buffer;
+-
+- if (priv->rx_processed) {
+- dbg("%s - already processed: %d bytes, %d remain", __func__,
+- priv->rx_processed,
+- urb->actual_length - priv->rx_processed);
+- } else {
+- /* The first two bytes of every read packet are status */
+- if (urb->actual_length > 2)
+- usb_serial_debug_data(debug, &port->dev, __func__,
+- urb->actual_length, data);
+- else
+- dbg("Status only: %03oo %03oo", data[0], data[1]);
++ if (packet[1] & FTDI_RS_BI) {
++ flag = TTY_BREAK;
++ dbg("BREAK received");
++ usb_serial_handle_break(port);
++ }
++ if (packet[1] & FTDI_RS_PE) {
++ flag = TTY_PARITY;
++ dbg("PARITY error");
++ }
++ if (packet[1] & FTDI_RS_FE) {
++ flag = TTY_FRAME;
++ dbg("FRAMING error");
+ }
+
++ len -= 2;
++ if (!len)
++ return 0; /* status only */
++ ch = packet + 2;
+
+- /* TO DO -- check for hung up line and handle appropriately: */
+- /* send hangup */
+- /* See acm.c - you do a tty_hangup - eg tty_hangup(tty) */
+- /* if CD is dropped and the line is not CLOCAL then we should hangup */
+-
+- need_flip = 0;
+- for (packet_offset = priv->rx_processed;
+- packet_offset < urb->actual_length; packet_offset += priv->max_packet_size) {
+- int length;
+-
+- /* Compare new line status to the old one, signal if different/
+- N.B. packet may be processed more than once, but differences
+- are only processed once. */
+- char new_status = data[packet_offset + 0] &
+- FTDI_STATUS_B0_MASK;
+- if (new_status != priv->prev_status) {
+- priv->diff_status |=
+- new_status ^ priv->prev_status;
+- wake_up_interruptible(&priv->delta_msr_wait);
+- priv->prev_status = new_status;
+- }
+-
+- length = min_t(u32, priv->max_packet_size, urb->actual_length-packet_offset)-2;
+- if (length < 0) {
+- dev_err(&port->dev, "%s - bad packet length: %d\n",
+- __func__, length+2);
+- length = 0;
++ if (!(port->console && port->sysrq) && flag == TTY_NORMAL)
++ tty_insert_flip_string(tty, ch, len);
++ else {
++ for (i = 0; i < len; i++, ch++) {
++ if (!usb_serial_handle_sysrq_char(tty, port, *ch))
++ tty_insert_flip_char(tty, *ch, flag);
+ }
++ }
++ return len;
++}
+
+- if (priv->rx_flags & THROTTLED) {
+- dbg("%s - throttled", __func__);
+- break;
+- }
+- if (tty_buffer_request_room(tty, length) < length) {
+- /* break out & wait for throttling/unthrottling to
+- happen */
+- dbg("%s - receive room low", __func__);
+- break;
+- }
++static void ftdi_process_read(struct usb_serial_port *port)
++{
++ struct urb *urb = port->read_urb;
++ struct tty_struct *tty;
++ struct ftdi_private *priv = usb_get_serial_port_data(port);
++ char *data = (char *)urb->transfer_buffer;
++ int i;
++ int len;
++ int count = 0;
+
+- /* Handle errors and break */
+- error_flag = TTY_NORMAL;
+- /* Although the device uses a bitmask and hence can have
+- multiple errors on a packet - the order here sets the
+- priority the error is returned to the tty layer */
+-
+- if (data[packet_offset+1] & FTDI_RS_OE) {
+- error_flag = TTY_OVERRUN;
+- dbg("OVERRRUN error");
+- }
+- if (data[packet_offset+1] & FTDI_RS_BI) {
+- error_flag = TTY_BREAK;
+- dbg("BREAK received");
+- usb_serial_handle_break(port);
+- }
+- if (data[packet_offset+1] & FTDI_RS_PE) {
+- error_flag = TTY_PARITY;
+- dbg("PARITY error");
+- }
+- if (data[packet_offset+1] & FTDI_RS_FE) {
+- error_flag = TTY_FRAME;
+- dbg("FRAMING error");
+- }
+- if (length > 0) {
+- for (i = 2; i < length+2; i++) {
+- /* Note that the error flag is duplicated for
+- every character received since we don't know
+- which character it applied to */
+- if (!usb_serial_handle_sysrq_char(tty, port,
+- data[packet_offset + i]))
+- tty_insert_flip_char(tty,
+- data[packet_offset + i],
+- error_flag);
+- }
+- need_flip = 1;
+- }
++ tty = tty_port_tty_get(&port->port);
++ if (!tty)
++ return;
+
+-#ifdef NOT_CORRECT_BUT_KEEPING_IT_FOR_NOW
+- /* if a parity error is detected you get status packets forever
+- until a character is sent without a parity error.
+- This doesn't work well since the application receives a
+- never ending stream of bad data - even though new data
+- hasn't been sent. Therefore I (bill) have taken this out.
+- However - this might make sense for framing errors and so on
+- so I am leaving the code in for now.
+- */
+- else {
+- if (error_flag != TTY_NORMAL) {
+- dbg("error_flag is not normal");
+- /* In this case it is just status - if that is
+- an error send a bad character */
+- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+- tty_flip_buffer_push(tty);
+- tty_insert_flip_char(tty, 0xff, error_flag);
+- need_flip = 1;
+- }
+- }
+-#endif
+- } /* "for(packet_offset=0..." */
++ for (i = 0; i < urb->actual_length; i += priv->max_packet_size) {
++ len = min_t(int, urb->actual_length - i, priv->max_packet_size);
++ count += ftdi_process_packet(tty, port, priv, &data[i], len);
++ }
+
+- /* Low latency */
+- if (need_flip)
++ if (count)
+ tty_flip_buffer_push(tty);
++ tty_kref_put(tty);
++}
+
+- if (packet_offset < urb->actual_length) {
+- /* not completely processed - record progress */
+- priv->rx_processed = packet_offset;
+- dbg("%s - incomplete, %d bytes processed, %d remain",
+- __func__, packet_offset,
+- urb->actual_length - packet_offset);
+- /* check if we were throttled while processing */
+- spin_lock_irqsave(&priv->rx_lock, flags);
+- if (priv->rx_flags & THROTTLED) {
+- priv->rx_flags |= ACTUALLY_THROTTLED;
+- spin_unlock_irqrestore(&priv->rx_lock, flags);
+- dbg("%s - deferring remainder until unthrottled",
+- __func__);
+- goto out;
+- }
+- spin_unlock_irqrestore(&priv->rx_lock, flags);
+- /* if the port is closed stop trying to read */
+- if (port->port.count > 0)
+- /* delay processing of remainder */
+- schedule_delayed_work(&priv->rx_work, 1);
+- else
+- dbg("%s - port is closed", __func__);
+- goto out;
+- }
+-
+- /* urb is completely processed */
+- priv->rx_processed = 0;
++static void ftdi_read_bulk_callback(struct urb *urb)
++{
++ struct usb_serial_port *port = urb->context;
++ unsigned long flags;
+
+- /* if the port is closed stop trying to read */
+- if (port->port.count > 0) {
+- /* Continue trying to always read */
+- usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+- usb_rcvbulkpipe(port->serial->dev,
+- port->bulk_in_endpointAddress),
+- port->read_urb->transfer_buffer,
+- port->read_urb->transfer_buffer_length,
+- ftdi_read_bulk_callback, port);
++ dbg("%s - port %d", __func__, port->number);
+
+- result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+- if (result)
+- dev_err(&port->dev,
+- "%s - failed resubmitting read urb, error %d\n",
+- __func__, result);
++ if (urb->status) {
++ dbg("%s - nonzero read bulk status received: %d",
++ __func__, urb->status);
++ return;
+ }
+-out:
+- tty_kref_put(tty);
+-} /* ftdi_process_read */
+
++ usb_serial_debug_data(debug, &port->dev, __func__,
++ urb->actual_length, urb->transfer_buffer);
++ ftdi_process_read(port);
++
++ spin_lock_irqsave(&port->lock, flags);
++ port->throttled = port->throttle_req;
++ if (!port->throttled) {
++ spin_unlock_irqrestore(&port->lock, flags);
++ ftdi_submit_read_urb(port, GFP_ATOMIC);
++ } else
++ spin_unlock_irqrestore(&port->lock, flags);
++}
+
+ static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
+ {
+@@ -2566,33 +2447,31 @@ static int ftdi_ioctl(struct tty_struct
+ static void ftdi_throttle(struct tty_struct *tty)
+ {
+ struct usb_serial_port *port = tty->driver_data;
+- struct ftdi_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ dbg("%s - port %d", __func__, port->number);
+
+- spin_lock_irqsave(&priv->rx_lock, flags);
+- priv->rx_flags |= THROTTLED;
+- spin_unlock_irqrestore(&priv->rx_lock, flags);
++ spin_lock_irqsave(&port->lock, flags);
++ port->throttle_req = 1;
++ spin_unlock_irqrestore(&port->lock, flags);
+ }
+
+-
+-static void ftdi_unthrottle(struct tty_struct *tty)
++void ftdi_unthrottle(struct tty_struct *tty)
+ {
+ struct usb_serial_port *port = tty->driver_data;
+- struct ftdi_private *priv = usb_get_serial_port_data(port);
+- int actually_throttled;
++ int was_throttled;
+ unsigned long flags;
+
+ dbg("%s - port %d", __func__, port->number);
+
+- spin_lock_irqsave(&priv->rx_lock, flags);
+- actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
+- priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+- spin_unlock_irqrestore(&priv->rx_lock, flags);
+-
+- if (actually_throttled)
+- schedule_delayed_work(&priv->rx_work, 0);
++ spin_lock_irqsave(&port->lock, flags);
++ was_throttled = port->throttled;
++ port->throttled = port->throttle_req = 0;
++ spin_unlock_irqrestore(&port->lock, flags);
++
++ /* Resubmit urb if throttled and open. */
++ if (was_throttled && test_bit(ASYNCB_INITIALIZED, &port->port.flags))
++ ftdi_submit_read_urb(port, GFP_KERNEL);
+ }
+
+ static int __init ftdi_init(void)
diff --git a/usb.current/usb-ftdi_sio-remove-tty-low_latency.patch b/usb.current/usb-ftdi_sio-remove-tty-low_latency.patch
new file mode 100644
index 00000000000000..d439b44e691d7f
--- /dev/null
+++ b/usb.current/usb-ftdi_sio-remove-tty-low_latency.patch
@@ -0,0 +1,40 @@
+From jhovold@gmail.com Thu Oct 8 09:53:57 2009
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 7 Oct 2009 20:05:04 +0200
+Subject: USB: ftdi_sio: remove tty->low_latency
+To: Greg Kroah-Hartman <gregkh@suse.de>, Alan Cox <alan@linux.intel.com>
+Cc: Oliver Neukum <oliver@neukum.org>, Alan Stern <stern@rowland.harvard.edu>, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1254938707-6996-2-git-send-email-jhovold@gmail.com>
+
+
+Fixes tty_flip_buffer_push being called from hard interrupt context with
+low_latency set.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Cc: stable <stable@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ftdi_sio.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -1234,7 +1234,6 @@ static int set_serial_info(struct tty_st
+ (new_serial.flags & ASYNC_FLAGS));
+ priv->custom_divisor = new_serial.custom_divisor;
+
+- tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ write_latency_timer(port);
+
+ check_and_exit:
+@@ -1704,9 +1703,6 @@ static int ftdi_open(struct tty_struct *
+ priv->rx_bytes = 0;
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
+
+- if (tty)
+- tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+-
+ write_latency_timer(port);
+
+ /* No error checking for this (will get errors later anyway) */
diff --git a/usb.current/usb-ftdi_sio-remove-unused-rx_byte-counter.patch b/usb.current/usb-ftdi_sio-remove-unused-rx_byte-counter.patch
new file mode 100644
index 00000000000000..3fb8440747317f
--- /dev/null
+++ b/usb.current/usb-ftdi_sio-remove-unused-rx_byte-counter.patch
@@ -0,0 +1,63 @@
+From jhovold@gmail.com Thu Oct 8 09:54:15 2009
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 7 Oct 2009 20:05:05 +0200
+Subject: USB: ftdi_sio: remove unused rx_byte counter
+To: Greg Kroah-Hartman <gregkh@suse.de>, Alan Cox <alan@linux.intel.com>
+Cc: Oliver Neukum <oliver@neukum.org>, Alan Stern <stern@rowland.harvard.edu>, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1254938707-6996-3-git-send-email-jhovold@gmail.com>
+
+
+Remove unused rx_byte counter which is never exposed as noted by Alan
+Cox.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Cc: stable <stable@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ftdi_sio.c | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -81,7 +81,6 @@ struct ftdi_private {
+ struct delayed_work rx_work;
+ struct usb_serial_port *port;
+ int rx_processed;
+- unsigned long rx_bytes;
+
+ __u16 interface; /* FT2232C, FT2232H or FT4232H port interface
+ (0 for FT232/245) */
+@@ -1699,9 +1698,6 @@ static int ftdi_open(struct tty_struct *
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ priv->tx_bytes = 0;
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+- spin_lock_irqsave(&priv->rx_lock, flags);
+- priv->rx_bytes = 0;
+- spin_unlock_irqrestore(&priv->rx_lock, flags);
+
+ write_latency_timer(port);
+
+@@ -2014,8 +2010,6 @@ static void ftdi_read_bulk_callback(stru
+ struct usb_serial_port *port = urb->context;
+ struct tty_struct *tty;
+ struct ftdi_private *priv;
+- unsigned long countread;
+- unsigned long flags;
+ int status = urb->status;
+
+ if (urb->number_of_packets > 0) {
+@@ -2054,13 +2048,6 @@ static void ftdi_read_bulk_callback(stru
+ goto out;
+ }
+
+- /* count data bytes, but not status bytes */
+- countread = urb->actual_length;
+- countread -= 2 * DIV_ROUND_UP(countread, priv->max_packet_size);
+- spin_lock_irqsave(&priv->rx_lock, flags);
+- priv->rx_bytes += countread;
+- spin_unlock_irqrestore(&priv->rx_lock, flags);
+-
+ ftdi_process_read(&priv->rx_work.work);
+ out:
+ tty_kref_put(tty);