aboutsummaryrefslogtreecommitdiffstats
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2010-04-23 15:48:46 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2010-04-23 15:48:46 -0700
commite059f9c1f697e4f2b69d0144b8dea8c938a97f14 (patch)
treec3ee13bb8e31bec78607a2218d06d50fbb40532d
parentd2a2fb8ae847442b92ce831d7e69ce809f84f598 (diff)
downloadpatches-e059f9c1f697e4f2b69d0144b8dea8c938a97f14.tar.gz
usb patches
-rw-r--r--series28
-rw-r--r--usb/usb-cp210x-increase-bulk-in-buffer-size-to-256-byte.patch29
-rw-r--r--usb/usb-cp210x-increase-the-bulk-out-buffer-size-to-256-byte.patch31
-rw-r--r--usb/usb-cp210x-use-generic-submit_read_urb-at-open.patch52
-rw-r--r--usb/usb-ftdi_sio-fix-some-coding-style-issues.patch191
-rw-r--r--usb/usb-ftdi_sio-switch-to-generic-read-implementation.patch185
-rw-r--r--usb/usb-gadget-add-hid-gadget-driver.patch1517
-rw-r--r--usb/usb-pl2303-increase-bulk-in-buffer-size-to-256-byte.patch29
-rw-r--r--usb/usb-pl2303-increase-the-bulk-out-buffer-size-to-256-byte.patch36
-rw-r--r--usb/usb-pl2303-switch-to-generic-read-implementation.patch143
-rw-r--r--usb/usb-pl2303-switch-to-generic-write-implementation.patch432
-rw-r--r--usb/usb-pl2303-use-generic-close.patch44
-rw-r--r--usb/usb-serial-allow-custom-multi-urb-write-bulk-callbacks.patch30
-rw-r--r--usb/usb-serial-allow-drivers-to-define-bulk-buffer-sizes.patch202
-rw-r--r--usb/usb-serial-clean-up-generic-write-start-busy-test.patch47
-rw-r--r--usb/usb-serial-clean-up-read-processing-in-generic-driver.patch109
-rw-r--r--usb/usb-serial-clean-up-some-error-and-debug-messages-in-generic-driver.patch63
-rw-r--r--usb/usb-serial-clear-fifo-on-close.patch41
-rw-r--r--usb/usb-serial-export-generic-throttle-and-unthrottle.patch40
-rw-r--r--usb/usb-serial-fix-generic-chars_in_buffer.patch62
-rw-r--r--usb/usb-serial-fix-missing-locking-on-fifo-in-write-callback.patch40
-rw-r--r--usb/usb-serial-generalise-generic-read-implementation.patch83
-rw-r--r--usb/usb-serial-generalise-write-buffer-preparation.patch153
-rw-r--r--usb/usb-serial-re-implement-multi-urb-writes-in-generic-driver.patch274
-rw-r--r--usb/usb-serial-refactor-generic-close.patch111
-rw-r--r--usb/usb-serial-refactor-read-urb-submission-in-generic-driver.patch118
-rw-r--r--usb/usb-serial-remove-unnecessary-re-initialisation-of-generic-urbs.patch70
-rw-r--r--usb/usb-usb_debug-use-the-generic-kfifo-based-write-implementation.patch38
28 files changed, 4198 insertions, 0 deletions
diff --git a/series b/series
index 14fe03adf6ede2..91ed62803c26a0 100644
--- a/series
+++ b/series
@@ -123,6 +123,33 @@ usb/usb-serial-driver-zio-motherboard.patch
usb/usb-fix-serial-build-when-sysrq-is-disabled.patch
usb/usb-add-a-new-quirk-usb_quirk_honor_bnuminterfaces.patch
+usb/usb-serial-allow-drivers-to-define-bulk-buffer-sizes.patch
+usb/usb-cp210x-increase-the-bulk-out-buffer-size-to-256-byte.patch
+usb/usb-cp210x-increase-bulk-in-buffer-size-to-256-byte.patch
+usb/usb-pl2303-increase-the-bulk-out-buffer-size-to-256-byte.patch
+usb/usb-pl2303-increase-bulk-in-buffer-size-to-256-byte.patch
+usb/usb-serial-fix-generic-chars_in_buffer.patch
+usb/usb-serial-fix-missing-locking-on-fifo-in-write-callback.patch
+usb/usb-serial-clear-fifo-on-close.patch
+usb/usb-serial-refactor-generic-close.patch
+usb/usb-serial-refactor-read-urb-submission-in-generic-driver.patch
+usb/usb-serial-remove-unnecessary-re-initialisation-of-generic-urbs.patch
+usb/usb-cp210x-use-generic-submit_read_urb-at-open.patch
+usb/usb-serial-clean-up-read-processing-in-generic-driver.patch
+usb/usb-serial-generalise-generic-read-implementation.patch
+usb/usb-pl2303-switch-to-generic-read-implementation.patch
+usb/usb-serial-export-generic-throttle-and-unthrottle.patch
+usb/usb-ftdi_sio-switch-to-generic-read-implementation.patch
+usb/usb-serial-clean-up-some-error-and-debug-messages-in-generic-driver.patch
+usb/usb-serial-clean-up-generic-write-start-busy-test.patch
+usb/usb-pl2303-switch-to-generic-write-implementation.patch
+usb/usb-pl2303-use-generic-close.patch
+usb/usb-usb_debug-use-the-generic-kfifo-based-write-implementation.patch
+usb/usb-serial-allow-custom-multi-urb-write-bulk-callbacks.patch
+usb/usb-serial-re-implement-multi-urb-writes-in-generic-driver.patch
+usb/usb-serial-generalise-write-buffer-preparation.patch
+usb/usb-ftdi_sio-fix-some-coding-style-issues.patch
+usb/usb-gadget-add-hid-gadget-driver.patch
#######################################
# Staging stuff for after 2.6.34 is out
@@ -143,3 +170,4 @@ staging/staging-rtl8192su-add-usb-id-for-0bda-8171.patch
# new stuff
staging/staging-intel-restricted-access-region-handler.patch
+
diff --git a/usb/usb-cp210x-increase-bulk-in-buffer-size-to-256-byte.patch b/usb/usb-cp210x-increase-bulk-in-buffer-size-to-256-byte.patch
new file mode 100644
index 00000000000000..af37f13ab0e2df
--- /dev/null
+++ b/usb/usb-cp210x-increase-bulk-in-buffer-size-to-256-byte.patch
@@ -0,0 +1,29 @@
+From jhovold@gmail.com Fri Apr 23 15:24:29 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:00:39 +0100
+Subject: USB: cp210x: increase bulk-in buffer size to 256 byte
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863245-17230-4-git-send-email-jhovold@gmail.com>
+
+
+The cp210x requires a bulk-in buffer larger than endpoint size to keep
+up at high baudrates without loosing data.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/cp210x.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/serial/cp210x.c
++++ b/drivers/usb/serial/cp210x.c
+@@ -138,6 +138,7 @@ static struct usb_serial_driver cp210x_d
+ .usb_driver = &cp210x_driver,
+ .id_table = id_table,
+ .num_ports = 1,
++ .bulk_in_size = 256,
+ .bulk_out_size = 256,
+ .open = cp210x_open,
+ .close = cp210x_close,
diff --git a/usb/usb-cp210x-increase-the-bulk-out-buffer-size-to-256-byte.patch b/usb/usb-cp210x-increase-the-bulk-out-buffer-size-to-256-byte.patch
new file mode 100644
index 00000000000000..4e3cdac59ce708
--- /dev/null
+++ b/usb/usb-cp210x-increase-the-bulk-out-buffer-size-to-256-byte.patch
@@ -0,0 +1,31 @@
+From jhovold@gmail.com Fri Apr 23 15:24:15 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:00:38 +0100
+Subject: USB: cp210x: increase the bulk-out buffer size to 256 byte
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863245-17230-3-git-send-email-jhovold@gmail.com>
+
+
+Increase the bulk-out buffer size from 64 to 256 byte.
+
+This gives a significant increase in throughput already at 1Mbaud
+(e.g. 710 instead of 640 KB/s) as well as lowered CPU usage.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/cp210x.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/serial/cp210x.c
++++ b/drivers/usb/serial/cp210x.c
+@@ -138,6 +138,7 @@ static struct usb_serial_driver cp210x_d
+ .usb_driver = &cp210x_driver,
+ .id_table = id_table,
+ .num_ports = 1,
++ .bulk_out_size = 256,
+ .open = cp210x_open,
+ .close = cp210x_close,
+ .break_ctl = cp210x_break_ctl,
diff --git a/usb/usb-cp210x-use-generic-submit_read_urb-at-open.patch b/usb/usb-cp210x-use-generic-submit_read_urb-at-open.patch
new file mode 100644
index 00000000000000..ebeef753eb6540
--- /dev/null
+++ b/usb/usb-cp210x-use-generic-submit_read_urb-at-open.patch
@@ -0,0 +1,52 @@
+From jhovold@gmail.com Fri Apr 23 15:28:58 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:05:55 +0100
+Subject: USB: cp210x: use generic submit_read_urb at open
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-4-git-send-email-jhovold@gmail.com>
+
+
+There is no need to initialise the read urb as this is done at port
+probe.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/cp210x.c | 16 ++--------------
+ 1 file changed, 2 insertions(+), 14 deletions(-)
+
+--- a/drivers/usb/serial/cp210x.c
++++ b/drivers/usb/serial/cp210x.c
+@@ -372,7 +372,6 @@ static unsigned int cp210x_quantise_baud
+
+ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
+ {
+- struct usb_serial *serial = port->serial;
+ int result;
+
+ dbg("%s - port %d", __func__, port->number);
+@@ -383,20 +382,9 @@ static int cp210x_open(struct tty_struct
+ return -EPROTO;
+ }
+
+- /* Start reading from the device */
+- usb_fill_bulk_urb(port->read_urb, serial->dev,
+- usb_rcvbulkpipe(serial->dev,
+- port->bulk_in_endpointAddress),
+- port->read_urb->transfer_buffer,
+- port->read_urb->transfer_buffer_length,
+- serial->type->read_bulk_callback,
+- port);
+- result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+- if (result) {
+- dev_err(&port->dev, "%s - failed resubmitting read urb, "
+- "error %d\n", __func__, result);
++ result = usb_serial_generic_submit_read_urb(port, GFP_KERNEL);
++ if (result)
+ return result;
+- }
+
+ /* Configure the termios structure */
+ cp210x_get_termios(tty, port);
diff --git a/usb/usb-ftdi_sio-fix-some-coding-style-issues.patch b/usb/usb-ftdi_sio-fix-some-coding-style-issues.patch
new file mode 100644
index 00000000000000..6914972114a8dd
--- /dev/null
+++ b/usb/usb-ftdi_sio-fix-some-coding-style-issues.patch
@@ -0,0 +1,191 @@
+From jhovold@gmail.com Fri Apr 23 15:37:16 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:06:09 +0100
+Subject: USB: ftdi_sio: fix some coding style issues
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-18-git-send-email-jhovold@gmail.com>
+
+
+Remove unnecessary comments stating function names at (some)
+opening/closing braces.
+Fix some whitespace issues.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ftdi_sio.c | 45 +++++++++++++-----------------------------
+ 1 file changed, 14 insertions(+), 31 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -982,7 +982,7 @@ static int update_mctrl(struct usb_seria
+
+ static __u32 get_ftdi_divisor(struct tty_struct *tty,
+ struct usb_serial_port *port)
+-{ /* get_ftdi_divisor */
++{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ __u32 div_value = 0;
+ int div_okay = 1;
+@@ -1206,12 +1206,11 @@ static int get_serial_info(struct usb_se
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+-} /* get_serial_info */
+-
++}
+
+ static int set_serial_info(struct tty_struct *tty,
+ struct usb_serial_port *port, struct serial_struct __user *newinfo)
+-{ /* set_serial_info */
++{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct serial_struct new_serial;
+ struct ftdi_private old_priv;
+@@ -1274,8 +1273,7 @@ check_and_exit:
+ else
+ mutex_unlock(&priv->cfg_lock);
+ return 0;
+-
+-} /* set_serial_info */
++}
+
+
+ /* Determine type of FTDI chip based on USB config and descriptor. */
+@@ -1577,7 +1575,7 @@ static void ftdi_USB_UIRT_setup(struct f
+ priv->flags |= ASYNC_SPD_CUST;
+ priv->custom_divisor = 77;
+ priv->force_baud = 38400;
+-} /* ftdi_USB_UIRT_setup */
++}
+
+ /* Setup for the HE-TIRA1 device, which requires hardwired
+ * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled. */
+@@ -1590,7 +1588,7 @@ static void ftdi_HE_TIRA1_setup(struct f
+ priv->custom_divisor = 240;
+ priv->force_baud = 38400;
+ priv->force_rtscts = 1;
+-} /* ftdi_HE_TIRA1_setup */
++}
+
+ /*
+ * Module parameter to control latency timer for NDI FTDI-based USB devices.
+@@ -1684,7 +1682,7 @@ static int ftdi_sio_port_remove(struct u
+ }
+
+ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port)
+-{ /* ftdi_open */
++{
+ struct usb_device *dev = port->serial->dev;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ int result;
+@@ -1714,8 +1712,7 @@ static int ftdi_open(struct tty_struct *
+ kref_get(&priv->kref);
+
+ return result;
+-} /* ftdi_open */
+-
++}
+
+ static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
+ {
+@@ -1745,12 +1742,9 @@ static void ftdi_dtr_rts(struct usb_seri
+ * usbserial:__serial_close only calls ftdi_close if the point is open
+ *
+ * This only gets called when it is the last close
+- *
+- *
+ */
+-
+ static void ftdi_close(struct usb_serial_port *port)
+-{ /* ftdi_close */
++{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+ dbg("%s", __func__);
+@@ -1758,9 +1752,7 @@ static void ftdi_close(struct usb_serial
+ /* shutdown our bulk read */
+ usb_kill_urb(port->read_urb);
+ kref_put(&priv->kref, ftdi_sio_priv_release);
+-} /* ftdi_close */
+-
+-
++}
+
+ /* The SIO requires the first byte to have:
+ * B0 1
+@@ -1771,7 +1763,7 @@ static void ftdi_close(struct usb_serial
+ */
+ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port,
+ const unsigned char *buf, int count)
+-{ /* ftdi_write */
++{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct urb *urb;
+ unsigned char *buffer;
+@@ -1887,11 +1879,9 @@ error_no_buffer:
+ priv->tx_outstanding_urbs--;
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+ return count;
+-} /* ftdi_write */
+-
++}
+
+ /* This function may get called when the device is closed */
+-
+ static void ftdi_write_bulk_callback(struct urb *urb)
+ {
+ unsigned long flags;
+@@ -1928,8 +1918,7 @@ static void ftdi_write_bulk_callback(str
+ }
+
+ usb_serial_port_softint(port);
+-} /* ftdi_write_bulk_callback */
+-
++}
+
+ static int ftdi_write_room(struct tty_struct *tty)
+ {
+@@ -2095,15 +2084,13 @@ static void ftdi_break_ctl(struct tty_st
+
+ }
+
+-
+ /* old_termios contains the original termios settings and tty->termios contains
+ * the new setting to be used
+ * WARNING: set_termios calls this with old_termios in kernel space
+ */
+-
+ static void ftdi_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port, struct ktermios *old_termios)
+-{ /* ftdi_termios */
++{
+ struct usb_device *dev = port->serial->dev;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct ktermios *termios = tty->termios;
+@@ -2331,7 +2318,6 @@ static int ftdi_tiocmset(struct tty_stru
+ return update_mctrl(port, set, clear);
+ }
+
+-
+ static int ftdi_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+ {
+@@ -2430,15 +2416,12 @@ failed_sio_register:
+ return retval;
+ }
+
+-
+ static void __exit ftdi_exit(void)
+ {
+-
+ dbg("%s", __func__);
+
+ usb_deregister(&ftdi_driver);
+ usb_serial_deregister(&ftdi_sio_device);
+-
+ }
+
+
diff --git a/usb/usb-ftdi_sio-switch-to-generic-read-implementation.patch b/usb/usb-ftdi_sio-switch-to-generic-read-implementation.patch
new file mode 100644
index 00000000000000..99319f43ed67c6
--- /dev/null
+++ b/usb/usb-ftdi_sio-switch-to-generic-read-implementation.patch
@@ -0,0 +1,185 @@
+From jhovold@gmail.com Fri Apr 23 15:32:40 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:06:00 +0100
+Subject: USB: ftdi_sio: switch to generic read implementation
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-9-git-send-email-jhovold@gmail.com>
+
+
+Switch to generic read implementation and use process_read_urb to do
+device specific processing (handle line status).
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ftdi_sio.c | 99 ++----------------------------------------
+ 1 file changed, 7 insertions(+), 92 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -789,8 +789,7 @@ static int ftdi_write(struct tty_struct
+ static int ftdi_write_room(struct tty_struct *tty);
+ 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 usb_serial_port *port);
++static void ftdi_process_read_urb(struct urb *urb);
+ 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);
+@@ -799,8 +798,6 @@ static int ftdi_tiocmset(struct tty_str
+ static int ftdi_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg);
+ static void ftdi_break_ctl(struct tty_struct *tty, int break_state);
+-static void ftdi_throttle(struct tty_struct *tty);
+-static void ftdi_unthrottle(struct tty_struct *tty);
+
+ static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base);
+ static unsigned short int ftdi_232am_baud_to_divisor(int baud);
+@@ -825,12 +822,12 @@ static struct usb_serial_driver ftdi_sio
+ .open = ftdi_open,
+ .close = ftdi_close,
+ .dtr_rts = ftdi_dtr_rts,
+- .throttle = ftdi_throttle,
+- .unthrottle = ftdi_unthrottle,
++ .throttle = usb_serial_generic_throttle,
++ .unthrottle = usb_serial_generic_unthrottle,
+ .write = ftdi_write,
+ .write_room = ftdi_write_room,
+ .chars_in_buffer = ftdi_chars_in_buffer,
+- .read_bulk_callback = ftdi_read_bulk_callback,
++ .process_read_urb = ftdi_process_read_urb,
+ .write_bulk_callback = ftdi_write_bulk_callback,
+ .tiocmget = ftdi_tiocmget,
+ .tiocmset = ftdi_tiocmset,
+@@ -1686,31 +1683,10 @@ 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 && result != -EPERM)
+- 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;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+- unsigned long flags;
+ int result;
+
+ dbg("%s", __func__);
+@@ -1732,14 +1708,8 @@ static int ftdi_open(struct tty_struct *
+ if (tty)
+ ftdi_set_termios(tty, port, tty->termios);
+
+- /* Not throttled */
+- spin_lock_irqsave(&port->lock, flags);
+- port->throttled = 0;
+- port->throttle_req = 0;
+- spin_unlock_irqrestore(&port->lock, flags);
+-
+ /* Start reading from the device */
+- result = ftdi_submit_read_urb(port, GFP_KERNEL);
++ result = usb_serial_generic_open(tty, port);
+ if (!result)
+ kref_get(&priv->kref);
+
+@@ -2071,9 +2041,9 @@ static int ftdi_process_packet(struct tt
+ return len;
+ }
+
+-static void ftdi_process_read(struct usb_serial_port *port)
++static void ftdi_process_read_urb(struct urb *urb)
+ {
+- struct urb *urb = port->read_urb;
++ struct usb_serial_port *port = urb->context;
+ struct tty_struct *tty;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ char *data = (char *)urb->transfer_buffer;
+@@ -2095,32 +2065,6 @@ static void ftdi_process_read(struct usb
+ tty_kref_put(tty);
+ }
+
+-static void ftdi_read_bulk_callback(struct urb *urb)
+-{
+- struct usb_serial_port *port = urb->context;
+- unsigned long flags;
+-
+- dbg("%s - port %d", __func__, port->number);
+-
+- if (urb->status) {
+- dbg("%s - nonzero read bulk status received: %d",
+- __func__, urb->status);
+- return;
+- }
+-
+- 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)
+ {
+ struct usb_serial_port *port = tty->driver_data;
+@@ -2456,35 +2400,6 @@ static int ftdi_ioctl(struct tty_struct
+ return -ENOIOCTLCMD;
+ }
+
+-static void ftdi_throttle(struct tty_struct *tty)
+-{
+- struct usb_serial_port *port = tty->driver_data;
+- unsigned long flags;
+-
+- dbg("%s - port %d", __func__, port->number);
+-
+- spin_lock_irqsave(&port->lock, flags);
+- port->throttle_req = 1;
+- spin_unlock_irqrestore(&port->lock, flags);
+-}
+-
+-void ftdi_unthrottle(struct tty_struct *tty)
+-{
+- struct usb_serial_port *port = tty->driver_data;
+- int was_throttled;
+- unsigned long flags;
+-
+- dbg("%s - port %d", __func__, port->number);
+-
+- spin_lock_irqsave(&port->lock, flags);
+- was_throttled = port->throttled;
+- port->throttled = port->throttle_req = 0;
+- spin_unlock_irqrestore(&port->lock, flags);
+-
+- if (was_throttled)
+- ftdi_submit_read_urb(port, GFP_KERNEL);
+-}
+-
+ static int __init ftdi_init(void)
+ {
+ int retval;
diff --git a/usb/usb-gadget-add-hid-gadget-driver.patch b/usb/usb-gadget-add-hid-gadget-driver.patch
new file mode 100644
index 00000000000000..0b515d30221330
--- /dev/null
+++ b/usb/usb-gadget-add-hid-gadget-driver.patch
@@ -0,0 +1,1517 @@
+From peter.korsgaard@barco.com Fri Apr 23 15:42:00 2010
+From: Peter Korsgaard <peter.korsgaard@barco.com>
+Date: Thu, 8 Apr 2010 09:31:15 +0200
+Subject: USB: gadget: add HID gadget driver
+To: greg@kroah.com, linux-usb@vger.kernel.org, dbrownell@users.sourceforge.net
+Cc: Fabien Chouteau <fabien.chouteau@barco.com>, Peter Korsgaard <peter.korsgaard@barco.com>
+Message-ID: <1270711875-31857-1-git-send-email-peter.korsgaard@barco.com>
+
+
+From: Fabien Chouteau <fabien.chouteau@barco.com>
+
+g_hid is a USB gadget driver implementing the Human Interface Device class
+specification. The driver handles basic HID protocol handling in the
+kernel, and allows userspace to read/write HID reports trough /dev/hidgX
+character devices.
+
+Signed-off-by: Fabien Chouteau <fabien.chouteau@barco.com>
+Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/usb/gadget_hid.txt | 445 +++++++++++++++++++++++++
+ drivers/usb/gadget/Kconfig | 11
+ drivers/usb/gadget/Makefile | 2
+ drivers/usb/gadget/f_hid.c | 673 +++++++++++++++++++++++++++++++++++++++
+ drivers/usb/gadget/hid.c | 288 ++++++++++++++++
+ include/linux/usb/g_hid.h | 32 +
+ 6 files changed, 1451 insertions(+)
+
+--- /dev/null
++++ b/Documentation/usb/gadget_hid.txt
+@@ -0,0 +1,445 @@
++
++ Linux USB HID gadget driver
++
++Introduction
++
++ The HID Gadget driver provides emulation of USB Human Interface
++ Devices (HID). The basic HID handling is done in the kernel,
++ and HID reports can be sent/received through I/O on the
++ /dev/hidgX character devices.
++
++ For more details about HID, see the developer page on
++ http://www.usb.org/developers/hidpage/
++
++Configuration
++
++ g_hid is a platform driver, so to use it you need to add
++ struct platform_device(s) to your platform code defining the
++ HID function descriptors you want to use - E.G. something
++ like:
++
++#include <linux/platform_device.h>
++#include <linux/usb/g_hid.h>
++
++/* hid descriptor for a keyboard */
++static struct hidg_func_descriptor my_hid_data = {
++ .subclass = 0, /* No subclass */
++ .protocol = 1, /* Keyboard */
++ .report_length = 8,
++ .report_desc_length = 63,
++ .report_desc = {
++ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
++ 0x09, 0x06, /* USAGE (Keyboard) */
++ 0xa1, 0x01, /* COLLECTION (Application) */
++ 0x05, 0x07, /* USAGE_PAGE (Keyboard) */
++ 0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */
++ 0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */
++ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
++ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
++ 0x75, 0x01, /* REPORT_SIZE (1) */
++ 0x95, 0x08, /* REPORT_COUNT (8) */
++ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
++ 0x95, 0x01, /* REPORT_COUNT (1) */
++ 0x75, 0x08, /* REPORT_SIZE (8) */
++ 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
++ 0x95, 0x05, /* REPORT_COUNT (5) */
++ 0x75, 0x01, /* REPORT_SIZE (1) */
++ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
++ 0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */
++ 0x29, 0x05, /* USAGE_MAXIMUM (Kana) */
++ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
++ 0x95, 0x01, /* REPORT_COUNT (1) */
++ 0x75, 0x03, /* REPORT_SIZE (3) */
++ 0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
++ 0x95, 0x06, /* REPORT_COUNT (6) */
++ 0x75, 0x08, /* REPORT_SIZE (8) */
++ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
++ 0x25, 0x65, /* LOGICAL_MAXIMUM (101) */
++ 0x05, 0x07, /* USAGE_PAGE (Keyboard) */
++ 0x19, 0x00, /* USAGE_MINIMUM (Reserved) */
++ 0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */
++ 0x81, 0x00, /* INPUT (Data,Ary,Abs) */
++ 0xc0 /* END_COLLECTION */
++ }
++};
++
++static struct platform_device my_hid = {
++ .name = "hidg",
++ .id = 0,
++ .num_resources = 0,
++ .resource = 0,
++ .dev.platform_data = &my_hid_data,
++};
++
++ You can add as many HID functions as you want, only limited by
++ the amount of interrupt endpoints your gadget driver supports.
++
++Send and receive HID reports
++
++ HID reports can be sent/received using read/write on the
++ /dev/hidgX character devices. See below for an example program
++ to do this.
++
++ hid_gadget_test is a small interactive program to test the HID
++ gadget driver. To use, point it at a hidg device and set the
++ device type (keyboard / mouse / joystick) - E.G.:
++
++ # hid_gadget_test /dev/hidg0 keyboard
++
++ You are now in the prompt of hid_gadget_test. You can type any
++ combination of options and values. Available options and
++ values are listed at program start. In keyboard mode you can
++ send up to six values.
++
++ For example type: g i s t r --left-shift
++
++ Hit return and the corresponding report will be sent by the
++ HID gadget.
++
++ Another interesting example is the caps lock test. Type
++ -–caps-lock and hit return. A report is then sent by the
++ gadget and you should receive the host answer, corresponding
++ to the caps lock LED status.
++
++ --caps-lock
++ recv report:2
++
++ With this command:
++
++ # hid_gadget_test /dev/hidg1 mouse
++
++ You can test the mouse emulation. Values are two signed numbers.
++
++
++Sample code
++
++/* hid_gadget_test */
++
++#include <pthread.h>
++#include <string.h>
++#include <stdio.h>
++#include <ctype.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++
++#define BUF_LEN 512
++
++struct options {
++ const char *opt;
++ unsigned char val;
++};
++
++static struct options kmod[] = {
++ {.opt = "--left-ctrl", .val = 0x01},
++ {.opt = "--right-ctrl", .val = 0x10},
++ {.opt = "--left-shift", .val = 0x02},
++ {.opt = "--right-shift", .val = 0x20},
++ {.opt = "--left-alt", .val = 0x04},
++ {.opt = "--right-alt", .val = 0x40},
++ {.opt = "--left-meta", .val = 0x08},
++ {.opt = "--right-meta", .val = 0x80},
++ {.opt = NULL}
++};
++
++static struct options kval[] = {
++ {.opt = "--return", .val = 0x28},
++ {.opt = "--esc", .val = 0x29},
++ {.opt = "--bckspc", .val = 0x2a},
++ {.opt = "--tab", .val = 0x2b},
++ {.opt = "--spacebar", .val = 0x2c},
++ {.opt = "--caps-lock", .val = 0x39},
++ {.opt = "--f1", .val = 0x3a},
++ {.opt = "--f2", .val = 0x3b},
++ {.opt = "--f3", .val = 0x3c},
++ {.opt = "--f4", .val = 0x3d},
++ {.opt = "--f5", .val = 0x3e},
++ {.opt = "--f6", .val = 0x3f},
++ {.opt = "--f7", .val = 0x40},
++ {.opt = "--f8", .val = 0x41},
++ {.opt = "--f9", .val = 0x42},
++ {.opt = "--f10", .val = 0x43},
++ {.opt = "--f11", .val = 0x44},
++ {.opt = "--f12", .val = 0x45},
++ {.opt = "--insert", .val = 0x49},
++ {.opt = "--home", .val = 0x4a},
++ {.opt = "--pageup", .val = 0x4b},
++ {.opt = "--del", .val = 0x4c},
++ {.opt = "--end", .val = 0x4d},
++ {.opt = "--pagedown", .val = 0x4e},
++ {.opt = "--right", .val = 0x4f},
++ {.opt = "--left", .val = 0x50},
++ {.opt = "--down", .val = 0x51},
++ {.opt = "--kp-enter", .val = 0x58},
++ {.opt = "--up", .val = 0x52},
++ {.opt = "--num-lock", .val = 0x53},
++ {.opt = NULL}
++};
++
++int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
++{
++ char *tok = strtok(buf, " ");
++ int key = 0;
++ int i = 0;
++
++ for (; tok != NULL; tok = strtok(NULL, " ")) {
++
++ if (strcmp(tok, "--quit") == 0)
++ return -1;
++
++ if (strcmp(tok, "--hold") == 0) {
++ *hold = 1;
++ continue;
++ }
++
++ if (key < 6) {
++ for (i = 0; kval[i].opt != NULL; i++)
++ if (strcmp(tok, kval[i].opt) == 0) {
++ report[2 + key++] = kval[i].val;
++ break;
++ }
++ if (kval[i].opt != NULL)
++ continue;
++ }
++
++ if (key < 6)
++ if (islower(tok[0])) {
++ report[2 + key++] = (tok[0] - ('a' - 0x04));
++ continue;
++ }
++
++ for (i = 0; kmod[i].opt != NULL; i++)
++ if (strcmp(tok, kmod[i].opt) == 0) {
++ report[0] = report[0] | kmod[i].val;
++ break;
++ }
++ if (kmod[i].opt != NULL)
++ continue;
++
++ if (key < 6)
++ fprintf(stderr, "unknown option: %s\n", tok);
++ }
++ return 8;
++}
++
++static struct options mmod[] = {
++ {.opt = "--b1", .val = 0x01},
++ {.opt = "--b2", .val = 0x02},
++ {.opt = "--b3", .val = 0x04},
++ {.opt = NULL}
++};
++
++int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
++{
++ char *tok = strtok(buf, " ");
++ int mvt = 0;
++ int i = 0;
++ for (; tok != NULL; tok = strtok(NULL, " ")) {
++
++ if (strcmp(tok, "--quit") == 0)
++ return -1;
++
++ if (strcmp(tok, "--hold") == 0) {
++ *hold = 1;
++ continue;
++ }
++
++ for (i = 0; mmod[i].opt != NULL; i++)
++ if (strcmp(tok, mmod[i].opt) == 0) {
++ report[0] = report[0] | mmod[i].val;
++ break;
++ }
++ if (mmod[i].opt != NULL)
++ continue;
++
++ if (!(tok[0] == '-' && tok[1] == '-') && mvt < 2) {
++ errno = 0;
++ report[1 + mvt++] = (char)strtol(tok, NULL, 0);
++ if (errno != 0) {
++ fprintf(stderr, "Bad value:'%s'\n", tok);
++ report[1 + mvt--] = 0;
++ }
++ continue;
++ }
++
++ fprintf(stderr, "unknown option: %s\n", tok);
++ }
++ return 3;
++}
++
++static struct options jmod[] = {
++ {.opt = "--b1", .val = 0x10},
++ {.opt = "--b2", .val = 0x20},
++ {.opt = "--b3", .val = 0x40},
++ {.opt = "--b4", .val = 0x80},
++ {.opt = "--hat1", .val = 0x00},
++ {.opt = "--hat2", .val = 0x01},
++ {.opt = "--hat3", .val = 0x02},
++ {.opt = "--hat4", .val = 0x03},
++ {.opt = "--hatneutral", .val = 0x04},
++ {.opt = NULL}
++};
++
++int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold)
++{
++ char *tok = strtok(buf, " ");
++ int mvt = 0;
++ int i = 0;
++
++ *hold = 1;
++
++ /* set default hat position: neutral */
++ report[3] = 0x04;
++
++ for (; tok != NULL; tok = strtok(NULL, " ")) {
++
++ if (strcmp(tok, "--quit") == 0)
++ return -1;
++
++ for (i = 0; jmod[i].opt != NULL; i++)
++ if (strcmp(tok, jmod[i].opt) == 0) {
++ report[3] = (report[3] & 0xF0) | jmod[i].val;
++ break;
++ }
++ if (jmod[i].opt != NULL)
++ continue;
++
++ if (!(tok[0] == '-' && tok[1] == '-') && mvt < 3) {
++ errno = 0;
++ report[mvt++] = (char)strtol(tok, NULL, 0);
++ if (errno != 0) {
++ fprintf(stderr, "Bad value:'%s'\n", tok);
++ report[mvt--] = 0;
++ }
++ continue;
++ }
++
++ fprintf(stderr, "unknown option: %s\n", tok);
++ }
++ return 4;
++}
++
++void print_options(char c)
++{
++ int i = 0;
++
++ if (c == 'k') {
++ printf(" keyboard options:\n"
++ " --hold\n");
++ for (i = 0; kmod[i].opt != NULL; i++)
++ printf("\t\t%s\n", kmod[i].opt);
++ printf("\n keyboard values:\n"
++ " [a-z] or\n");
++ for (i = 0; kval[i].opt != NULL; i++)
++ printf("\t\t%-8s%s", kval[i].opt, i % 2 ? "\n" : "");
++ printf("\n");
++ } else if (c == 'm') {
++ printf(" mouse options:\n"
++ " --hold\n");
++ for (i = 0; mmod[i].opt != NULL; i++)
++ printf("\t\t%s\n", mmod[i].opt);
++ printf("\n mouse values:\n"
++ " Two signed numbers\n"
++ "--quit to close\n");
++ } else {
++ printf(" joystick options:\n");
++ for (i = 0; jmod[i].opt != NULL; i++)
++ printf("\t\t%s\n", jmod[i].opt);
++ printf("\n joystick values:\n"
++ " three signed numbers\n"
++ "--quit to close\n");
++ }
++}
++
++int main(int argc, const char *argv[])
++{
++ const char *filename = NULL;
++ int fd = 0;
++ char buf[BUF_LEN];
++ int cmd_len;
++ char report[8];
++ int to_send = 8;
++ int hold = 0;
++ fd_set rfds;
++ int retval, i;
++
++ if (argc < 3) {
++ fprintf(stderr, "Usage: %s devname mouse|keyboard|joystick\n",
++ argv[0]);
++ return 1;
++ }
++
++ if (argv[2][0] != 'k' && argv[2][0] != 'm' && argv[2][0] != 'j')
++ return 2;
++
++ filename = argv[1];
++
++ if ((fd = open(filename, O_RDWR, 0666)) == -1) {
++ perror(filename);
++ return 3;
++ }
++
++ print_options(argv[2][0]);
++
++ while (42) {
++
++ FD_ZERO(&rfds);
++ FD_SET(STDIN_FILENO, &rfds);
++ FD_SET(fd, &rfds);
++
++ retval = select(fd + 1, &rfds, NULL, NULL, NULL);
++ if (retval == -1 && errno == EINTR)
++ continue;
++ if (retval < 0) {
++ perror("select()");
++ return 4;
++ }
++
++ if (FD_ISSET(fd, &rfds)) {
++ cmd_len = read(fd, buf, BUF_LEN - 1);
++ printf("recv report:");
++ for (i = 0; i < cmd_len; i++)
++ printf(" %02x", buf[i]);
++ printf("\n");
++ }
++
++ if (FD_ISSET(STDIN_FILENO, &rfds)) {
++ memset(report, 0x0, sizeof(report));
++ cmd_len = read(STDIN_FILENO, buf, BUF_LEN - 1);
++
++ if (cmd_len == 0)
++ break;
++
++ buf[cmd_len - 1] = '\0';
++ hold = 0;
++
++ memset(report, 0x0, sizeof(report));
++ if (argv[2][0] == 'k')
++ to_send = keyboard_fill_report(report, buf, &hold);
++ else if (argv[2][0] == 'm')
++ to_send = mouse_fill_report(report, buf, &hold);
++ else
++ to_send = joystick_fill_report(report, buf, &hold);
++
++ if (to_send == -1)
++ break;
++
++ if (write(fd, report, to_send) != to_send) {
++ perror(filename);
++ return 5;
++ }
++ if (!hold) {
++ memset(report, 0x0, sizeof(report));
++ if (write(fd, report, to_send) != to_send) {
++ perror(filename);
++ return 6;
++ }
++ }
++ }
++ }
++
++ close(fd);
++ return 0;
++}
+--- a/drivers/usb/gadget/Kconfig
++++ b/drivers/usb/gadget/Kconfig
+@@ -863,6 +863,17 @@ config USB_G_MULTI_CDC
+
+ If unsure, say "y".
+
++config USB_G_HID
++ tristate "HID Gadget"
++ help
++ The HID gadget driver provides generic emulation of USB
++ Human Interface Devices (HID).
++
++ For more information, see Documentation/usb/gadget_hid.txt which
++ includes sample code for accessing the device files.
++
++ Say "y" to link the driver statically, or "m" to build a
++ dynamically linked module called "g_hid".
+
+ # put drivers that need isochronous transfer support (for audio
+ # or video class gadget drivers), or specific hardware, here.
+--- a/drivers/usb/gadget/Makefile
++++ b/drivers/usb/gadget/Makefile
+@@ -43,6 +43,7 @@ g_mass_storage-objs := mass_storage.o
+ g_printer-objs := printer.o
+ g_cdc-objs := cdc2.o
+ g_multi-objs := multi.o
++g_hid-objs := hid.o
+ g_nokia-objs := nokia.o
+
+ obj-$(CONFIG_USB_ZERO) += g_zero.o
+@@ -55,6 +56,7 @@ obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
+ obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
+ obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
+ obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
++obj-$(CONFIG_USB_G_HID) += g_hid.o
+ obj-$(CONFIG_USB_G_MULTI) += g_multi.o
+ obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
+
+--- /dev/null
++++ b/drivers/usb/gadget/f_hid.c
+@@ -0,0 +1,673 @@
++/*
++ * f_hid.c -- USB HID function driver
++ *
++ * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/kernel.h>
++#include <linux/utsname.h>
++#include <linux/module.h>
++#include <linux/hid.h>
++#include <linux/cdev.h>
++#include <linux/mutex.h>
++#include <linux/poll.h>
++#include <linux/smp_lock.h>
++#include <linux/uaccess.h>
++#include <linux/wait.h>
++#include <linux/usb/g_hid.h>
++
++static int major, minors;
++static struct class *hidg_class;
++
++/*-------------------------------------------------------------------------*/
++/* HID gadget struct */
++
++struct f_hidg {
++ /* configuration */
++ unsigned char bInterfaceSubClass;
++ unsigned char bInterfaceProtocol;
++ unsigned short report_desc_length;
++ char *report_desc;
++ unsigned short report_length;
++
++ /* recv report */
++ char *set_report_buff;
++ unsigned short set_report_length;
++ spinlock_t spinlock;
++ wait_queue_head_t read_queue;
++
++ /* send report */
++ struct mutex lock;
++ bool write_pending;
++ wait_queue_head_t write_queue;
++ struct usb_request *req;
++
++ int minor;
++ struct cdev cdev;
++ struct usb_function func;
++ struct usb_ep *in_ep;
++ struct usb_endpoint_descriptor *fs_in_ep_desc;
++ struct usb_endpoint_descriptor *hs_in_ep_desc;
++};
++
++static inline struct f_hidg *func_to_hidg(struct usb_function *f)
++{
++ return container_of(f, struct f_hidg, func);
++}
++
++/*-------------------------------------------------------------------------*/
++/* Static descriptors */
++
++static struct usb_interface_descriptor hidg_interface_desc = {
++ .bLength = sizeof hidg_interface_desc,
++ .bDescriptorType = USB_DT_INTERFACE,
++ /* .bInterfaceNumber = DYNAMIC */
++ .bAlternateSetting = 0,
++ .bNumEndpoints = 1,
++ .bInterfaceClass = USB_CLASS_HID,
++ /* .bInterfaceSubClass = DYNAMIC */
++ /* .bInterfaceProtocol = DYNAMIC */
++ /* .iInterface = DYNAMIC */
++};
++
++static struct hid_descriptor hidg_desc = {
++ .bLength = sizeof hidg_desc,
++ .bDescriptorType = HID_DT_HID,
++ .bcdHID = 0x0101,
++ .bCountryCode = 0x00,
++ .bNumDescriptors = 0x1,
++ /*.desc[0].bDescriptorType = DYNAMIC */
++ /*.desc[0].wDescriptorLenght = DYNAMIC */
++};
++
++/* High-Speed Support */
++
++static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ /*.wMaxPacketSize = DYNAMIC */
++ .bInterval = 4, /* FIXME: Add this field in the
++ * HID gadget configuration?
++ * (struct hidg_func_descriptor)
++ */
++};
++
++static struct usb_descriptor_header *hidg_hs_descriptors[] = {
++ (struct usb_descriptor_header *)&hidg_interface_desc,
++ (struct usb_descriptor_header *)&hidg_desc,
++ (struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
++ NULL,
++};
++
++/* Full-Speed Support */
++
++static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ /*.wMaxPacketSize = DYNAMIC */
++ .bInterval = 10, /* FIXME: Add this field in the
++ * HID gadget configuration?
++ * (struct hidg_func_descriptor)
++ */
++};
++
++static struct usb_descriptor_header *hidg_fs_descriptors[] = {
++ (struct usb_descriptor_header *)&hidg_interface_desc,
++ (struct usb_descriptor_header *)&hidg_desc,
++ (struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
++ NULL,
++};
++
++/*-------------------------------------------------------------------------*/
++/* Char Device */
++
++static ssize_t f_hidg_read(struct file *file, char __user *buffer,
++ size_t count, loff_t *ptr)
++{
++ struct f_hidg *hidg = (struct f_hidg *)file->private_data;
++ char *tmp_buff = NULL;
++ unsigned long flags;
++
++ if (!count)
++ return 0;
++
++ if (!access_ok(VERIFY_WRITE, buffer, count))
++ return -EFAULT;
++
++ spin_lock_irqsave(&hidg->spinlock, flags);
++
++#define READ_COND (hidg->set_report_buff != NULL)
++
++ while (!READ_COND) {
++ spin_unlock_irqrestore(&hidg->spinlock, flags);
++ if (file->f_flags & O_NONBLOCK)
++ return -EAGAIN;
++
++ if (wait_event_interruptible(hidg->read_queue, READ_COND))
++ return -ERESTARTSYS;
++
++ spin_lock_irqsave(&hidg->spinlock, flags);
++ }
++
++
++ count = min_t(unsigned, count, hidg->set_report_length);
++ tmp_buff = hidg->set_report_buff;
++ hidg->set_report_buff = NULL;
++
++ spin_unlock_irqrestore(&hidg->spinlock, flags);
++
++ if (tmp_buff != NULL) {
++ /* copy to user outside spinlock */
++ count -= copy_to_user(buffer, tmp_buff, count);
++ kfree(tmp_buff);
++ } else
++ count = -ENOMEM;
++
++ return count;
++}
++
++static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
++
++ if (req->status != 0) {
++ ERROR(hidg->func.config->cdev,
++ "End Point Request ERROR: %d\n", req->status);
++ }
++
++ hidg->write_pending = 0;
++ wake_up(&hidg->write_queue);
++}
++
++static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
++ size_t count, loff_t *offp)
++{
++ struct f_hidg *hidg = (struct f_hidg *)file->private_data;
++ ssize_t status = -ENOMEM;
++
++ if (!access_ok(VERIFY_READ, buffer, count))
++ return -EFAULT;
++
++ mutex_lock(&hidg->lock);
++
++#define WRITE_COND (!hidg->write_pending)
++
++ /* write queue */
++ while (!WRITE_COND) {
++ mutex_unlock(&hidg->lock);
++ if (file->f_flags & O_NONBLOCK)
++ return -EAGAIN;
++
++ if (wait_event_interruptible_exclusive(
++ hidg->write_queue, WRITE_COND))
++ return -ERESTARTSYS;
++
++ mutex_lock(&hidg->lock);
++ }
++
++ count = min_t(unsigned, count, hidg->report_length);
++ status = copy_from_user(hidg->req->buf, buffer, count);
++
++ if (status != 0) {
++ ERROR(hidg->func.config->cdev,
++ "copy_from_user error\n");
++ mutex_unlock(&hidg->lock);
++ return -EINVAL;
++ }
++
++ hidg->req->status = 0;
++ hidg->req->zero = 0;
++ hidg->req->length = count;
++ hidg->req->complete = f_hidg_req_complete;
++ hidg->req->context = hidg;
++ hidg->write_pending = 1;
++
++ status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC);
++ if (status < 0) {
++ ERROR(hidg->func.config->cdev,
++ "usb_ep_queue error on int endpoint %zd\n", status);
++ hidg->write_pending = 0;
++ wake_up(&hidg->write_queue);
++ } else {
++ status = count;
++ }
++
++ mutex_unlock(&hidg->lock);
++
++ return status;
++}
++
++static unsigned int f_hidg_poll(struct file *file, poll_table *wait)
++{
++ struct f_hidg *hidg = (struct f_hidg *)file->private_data;
++ unsigned int ret = 0;
++
++ poll_wait(file, &hidg->read_queue, wait);
++ poll_wait(file, &hidg->write_queue, wait);
++
++ if (WRITE_COND)
++ ret |= POLLOUT | POLLWRNORM;
++
++ if (READ_COND)
++ ret |= POLLIN | POLLRDNORM;
++
++ return ret;
++}
++
++#undef WRITE_COND
++#undef READ_COND
++
++static int f_hidg_release(struct inode *inode, struct file *fd)
++{
++ fd->private_data = NULL;
++ return 0;
++}
++
++static int f_hidg_open(struct inode *inode, struct file *fd)
++{
++ struct f_hidg *hidg =
++ container_of(inode->i_cdev, struct f_hidg, cdev);
++
++ fd->private_data = hidg;
++
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++/* usb_function */
++
++static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct f_hidg *hidg = (struct f_hidg *)req->context;
++
++ if (req->status != 0 || req->buf == NULL || req->actual == 0) {
++ ERROR(hidg->func.config->cdev, "%s FAILED\n", __func__);
++ return;
++ }
++
++ spin_lock(&hidg->spinlock);
++
++ hidg->set_report_buff = krealloc(hidg->set_report_buff,
++ req->actual, GFP_ATOMIC);
++
++ if (hidg->set_report_buff == NULL) {
++ spin_unlock(&hidg->spinlock);
++ return;
++ }
++ hidg->set_report_length = req->actual;
++ memcpy(hidg->set_report_buff, req->buf, req->actual);
++
++ spin_unlock(&hidg->spinlock);
++
++ wake_up(&hidg->read_queue);
++
++ return;
++}
++
++static int hidg_setup(struct usb_function *f,
++ const struct usb_ctrlrequest *ctrl)
++{
++ struct f_hidg *hidg = func_to_hidg(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++ struct usb_request *req = cdev->req;
++ int status = 0;
++ __u16 value, length;
++
++ value = __le16_to_cpu(ctrl->wValue);
++ length = __le16_to_cpu(ctrl->wLength);
++
++ VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x "
++ "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value);
++
++ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
++ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
++ | HID_REQ_GET_REPORT):
++ VDBG(cdev, "get_report\n");
++
++ /* send an empty report */
++ length = min_t(unsigned, length, hidg->report_length);
++ memset(req->buf, 0x0, length);
++
++ goto respond;
++ break;
++
++ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
++ | HID_REQ_GET_PROTOCOL):
++ VDBG(cdev, "get_protocol\n");
++ goto stall;
++ break;
++
++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
++ | HID_REQ_SET_REPORT):
++ VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength);
++ req->context = hidg;
++ req->complete = hidg_set_report_complete;
++ goto respond;
++ break;
++
++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
++ | HID_REQ_SET_PROTOCOL):
++ VDBG(cdev, "set_protocol\n");
++ goto stall;
++ break;
++
++ case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
++ | USB_REQ_GET_DESCRIPTOR):
++ switch (value >> 8) {
++ case HID_DT_REPORT:
++ VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
++ length = min_t(unsigned short, length,
++ hidg->report_desc_length);
++ memcpy(req->buf, hidg->report_desc, length);
++ goto respond;
++ break;
++
++ default:
++ VDBG(cdev, "Unknown decriptor request 0x%x\n",
++ value >> 8);
++ goto stall;
++ break;
++ }
++ break;
++
++ default:
++ VDBG(cdev, "Unknown request 0x%x\n",
++ ctrl->bRequest);
++ goto stall;
++ break;
++ }
++
++stall:
++ return -EOPNOTSUPP;
++
++respond:
++ req->zero = 0;
++ req->length = length;
++ status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
++ if (status < 0)
++ ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value);
++ return status;
++}
++
++static void hidg_disable(struct usb_function *f)
++{
++ struct f_hidg *hidg = func_to_hidg(f);
++
++ usb_ep_disable(hidg->in_ep);
++ hidg->in_ep->driver_data = NULL;
++
++ return;
++}
++
++static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
++{
++ struct usb_composite_dev *cdev = f->config->cdev;
++ struct f_hidg *hidg = func_to_hidg(f);
++ const struct usb_endpoint_descriptor *ep_desc;
++ int status = 0;
++
++ VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
++
++ if (hidg->in_ep != NULL) {
++ /* restart endpoint */
++ if (hidg->in_ep->driver_data != NULL)
++ usb_ep_disable(hidg->in_ep);
++
++ ep_desc = ep_choose(f->config->cdev->gadget,
++ hidg->hs_in_ep_desc, hidg->fs_in_ep_desc);
++ status = usb_ep_enable(hidg->in_ep, ep_desc);
++ if (status < 0) {
++ ERROR(cdev, "Enable endpoint FAILED!\n");
++ goto fail;
++ }
++ hidg->in_ep->driver_data = hidg;
++ }
++fail:
++ return status;
++}
++
++const struct file_operations f_hidg_fops = {
++ .owner = THIS_MODULE,
++ .open = f_hidg_open,
++ .release = f_hidg_release,
++ .write = f_hidg_write,
++ .read = f_hidg_read,
++ .poll = f_hidg_poll,
++};
++
++static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct usb_ep *ep;
++ struct f_hidg *hidg = func_to_hidg(f);
++ int status;
++ dev_t dev;
++
++ /* allocate instance-specific interface IDs, and patch descriptors */
++ status = usb_interface_id(c, f);
++ if (status < 0)
++ goto fail;
++ hidg_interface_desc.bInterfaceNumber = status;
++
++
++ /* allocate instance-specific endpoints */
++ status = -ENODEV;
++ ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc);
++ if (!ep)
++ goto fail;
++ ep->driver_data = c->cdev; /* claim */
++ hidg->in_ep = ep;
++
++ /* preallocate request and buffer */
++ status = -ENOMEM;
++ hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
++ if (!hidg->req)
++ goto fail;
++
++
++ hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
++ if (!hidg->req->buf)
++ goto fail;
++
++ /* set descriptor dynamic values */
++ hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
++ hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
++ hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
++ hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
++ hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
++ hidg_desc.desc[0].wDescriptorLength =
++ cpu_to_le16(hidg->report_desc_length);
++
++ hidg->set_report_buff = NULL;
++
++ /* copy descriptors */
++ f->descriptors = usb_copy_descriptors(hidg_fs_descriptors);
++ if (!f->descriptors)
++ goto fail;
++
++ hidg->fs_in_ep_desc = usb_find_endpoint(hidg_fs_descriptors,
++ f->descriptors,
++ &hidg_fs_in_ep_desc);
++
++ if (gadget_is_dualspeed(c->cdev->gadget)) {
++ hidg_hs_in_ep_desc.bEndpointAddress =
++ hidg_fs_in_ep_desc.bEndpointAddress;
++ f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors);
++ if (!f->hs_descriptors)
++ goto fail;
++ hidg->hs_in_ep_desc = usb_find_endpoint(hidg_hs_descriptors,
++ f->hs_descriptors,
++ &hidg_hs_in_ep_desc);
++ } else {
++ hidg->hs_in_ep_desc = NULL;
++ }
++
++ mutex_init(&hidg->lock);
++ spin_lock_init(&hidg->spinlock);
++ init_waitqueue_head(&hidg->write_queue);
++ init_waitqueue_head(&hidg->read_queue);
++
++ /* create char device */
++ cdev_init(&hidg->cdev, &f_hidg_fops);
++ dev = MKDEV(major, hidg->minor);
++ status = cdev_add(&hidg->cdev, dev, 1);
++ if (status)
++ goto fail;
++
++ device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
++
++ return 0;
++
++fail:
++ ERROR(f->config->cdev, "hidg_bind FAILED\n");
++ if (hidg->req != NULL) {
++ kfree(hidg->req->buf);
++ if (hidg->in_ep != NULL)
++ usb_ep_free_request(hidg->in_ep, hidg->req);
++ }
++
++ usb_free_descriptors(f->hs_descriptors);
++ usb_free_descriptors(f->descriptors);
++
++ return status;
++}
++
++static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct f_hidg *hidg = func_to_hidg(f);
++
++ device_destroy(hidg_class, MKDEV(major, hidg->minor));
++ cdev_del(&hidg->cdev);
++
++ /* disable/free request and end point */
++ usb_ep_disable(hidg->in_ep);
++ usb_ep_dequeue(hidg->in_ep, hidg->req);
++ kfree(hidg->req->buf);
++ usb_ep_free_request(hidg->in_ep, hidg->req);
++
++ /* free descriptors copies */
++ usb_free_descriptors(f->hs_descriptors);
++ usb_free_descriptors(f->descriptors);
++
++ kfree(hidg->report_desc);
++ kfree(hidg->set_report_buff);
++ kfree(hidg);
++}
++
++/*-------------------------------------------------------------------------*/
++/* Strings */
++
++#define CT_FUNC_HID_IDX 0
++
++static struct usb_string ct_func_string_defs[] = {
++ [CT_FUNC_HID_IDX].s = "HID Interface",
++ {}, /* end of list */
++};
++
++static struct usb_gadget_strings ct_func_string_table = {
++ .language = 0x0409, /* en-US */
++ .strings = ct_func_string_defs,
++};
++
++static struct usb_gadget_strings *ct_func_strings[] = {
++ &ct_func_string_table,
++ NULL,
++};
++
++/*-------------------------------------------------------------------------*/
++/* usb_configuration */
++
++int __init hidg_bind_config(struct usb_configuration *c,
++ struct hidg_func_descriptor *fdesc, int index)
++{
++ struct f_hidg *hidg;
++ int status;
++
++ if (index >= minors)
++ return -ENOENT;
++
++ /* maybe allocate device-global string IDs, and patch descriptors */
++ if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) {
++ status = usb_string_id(c->cdev);
++ if (status < 0)
++ return status;
++ ct_func_string_defs[CT_FUNC_HID_IDX].id = status;
++ hidg_interface_desc.iInterface = status;
++ }
++
++ /* allocate and initialize one new instance */
++ hidg = kzalloc(sizeof *hidg, GFP_KERNEL);
++ if (!hidg)
++ return -ENOMEM;
++
++ hidg->minor = index;
++ hidg->bInterfaceSubClass = fdesc->subclass;
++ hidg->bInterfaceProtocol = fdesc->protocol;
++ hidg->report_length = fdesc->report_length;
++ hidg->report_desc_length = fdesc->report_desc_length;
++ hidg->report_desc = kmemdup(fdesc->report_desc,
++ fdesc->report_desc_length,
++ GFP_KERNEL);
++ if (!hidg->report_desc) {
++ kfree(hidg);
++ return -ENOMEM;
++ }
++
++ hidg->func.name = "hid";
++ hidg->func.strings = ct_func_strings;
++ hidg->func.bind = hidg_bind;
++ hidg->func.unbind = hidg_unbind;
++ hidg->func.set_alt = hidg_set_alt;
++ hidg->func.disable = hidg_disable;
++ hidg->func.setup = hidg_setup;
++
++ status = usb_add_function(c, &hidg->func);
++ if (status)
++ kfree(hidg);
++
++ return status;
++}
++
++int __init ghid_setup(struct usb_gadget *g, int count)
++{
++ int status;
++ dev_t dev;
++
++ hidg_class = class_create(THIS_MODULE, "hidg");
++
++ status = alloc_chrdev_region(&dev, 0, count, "hidg");
++ if (!status) {
++ major = MAJOR(dev);
++ minors = count;
++ }
++
++ return status;
++}
++
++void ghid_cleanup(void)
++{
++ if (major) {
++ unregister_chrdev_region(MKDEV(major, 0), minors);
++ major = minors = 0;
++ }
++
++ class_destroy(hidg_class);
++ hidg_class = NULL;
++}
+--- /dev/null
++++ b/drivers/usb/gadget/hid.c
+@@ -0,0 +1,288 @@
++/*
++ * hid.c -- HID Composite driver
++ *
++ * Based on multi.c
++ *
++ * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/list.h>
++
++#define DRIVER_DESC "HID Gadget"
++#define DRIVER_VERSION "2010/03/16"
++
++/*-------------------------------------------------------------------------*/
++
++#define HIDG_VENDOR_NUM 0x0525 /* XXX NetChip */
++#define HIDG_PRODUCT_NUM 0xa4ac /* Linux-USB HID gadget */
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * kbuild is not very cooperative with respect to linking separately
++ * compiled library objects into one module. So for now we won't use
++ * separate compilation ... ensuring init/exit sections work to shrink
++ * the runtime footprint, and giving us at least some parts of what
++ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
++ */
++
++#include "composite.c"
++#include "usbstring.c"
++#include "config.c"
++#include "epautoconf.c"
++
++#include "f_hid.c"
++
++
++struct hidg_func_node {
++ struct list_head node;
++ struct hidg_func_descriptor *func;
++};
++
++static LIST_HEAD(hidg_func_list);
++
++/*-------------------------------------------------------------------------*/
++
++static struct usb_device_descriptor device_desc = {
++ .bLength = sizeof device_desc,
++ .bDescriptorType = USB_DT_DEVICE,
++
++ .bcdUSB = cpu_to_le16(0x0200),
++
++ /* .bDeviceClass = USB_CLASS_COMM, */
++ /* .bDeviceSubClass = 0, */
++ /* .bDeviceProtocol = 0, */
++ .bDeviceClass = 0xEF,
++ .bDeviceSubClass = 2,
++ .bDeviceProtocol = 1,
++ /* .bMaxPacketSize0 = f(hardware) */
++
++ /* Vendor and product id can be overridden by module parameters. */
++ .idVendor = cpu_to_le16(HIDG_VENDOR_NUM),
++ .idProduct = cpu_to_le16(HIDG_PRODUCT_NUM),
++ /* .bcdDevice = f(hardware) */
++ /* .iManufacturer = DYNAMIC */
++ /* .iProduct = DYNAMIC */
++ /* NO SERIAL NUMBER */
++ .bNumConfigurations = 1,
++};
++
++static struct usb_otg_descriptor otg_descriptor = {
++ .bLength = sizeof otg_descriptor,
++ .bDescriptorType = USB_DT_OTG,
++
++ /* REVISIT SRP-only hardware is possible, although
++ * it would not be called "OTG" ...
++ */
++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
++};
++
++static const struct usb_descriptor_header *otg_desc[] = {
++ (struct usb_descriptor_header *) &otg_descriptor,
++ NULL,
++};
++
++
++/* string IDs are assigned dynamically */
++
++#define STRING_MANUFACTURER_IDX 0
++#define STRING_PRODUCT_IDX 1
++
++static char manufacturer[50];
++
++static struct usb_string strings_dev[] = {
++ [STRING_MANUFACTURER_IDX].s = manufacturer,
++ [STRING_PRODUCT_IDX].s = DRIVER_DESC,
++ { } /* end of list */
++};
++
++static struct usb_gadget_strings stringtab_dev = {
++ .language = 0x0409, /* en-us */
++ .strings = strings_dev,
++};
++
++static struct usb_gadget_strings *dev_strings[] = {
++ &stringtab_dev,
++ NULL,
++};
++
++
++
++/****************************** Configurations ******************************/
++
++static int __init do_config(struct usb_configuration *c)
++{
++ struct hidg_func_node *e;
++ int func = 0, status = 0;
++
++ if (gadget_is_otg(c->cdev->gadget)) {
++ c->descriptors = otg_desc;
++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
++ }
++
++ list_for_each_entry(e, &hidg_func_list, node) {
++ status = hidg_bind_config(c, e->func, func++);
++ if (status)
++ break;
++ }
++
++ return status;
++}
++
++static struct usb_configuration config_driver = {
++ .label = "HID Gadget",
++ .bind = do_config,
++ .bConfigurationValue = 1,
++ /* .iConfiguration = DYNAMIC */
++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
++};
++
++/****************************** Gadget Bind ******************************/
++
++static int __init hid_bind(struct usb_composite_dev *cdev)
++{
++ struct usb_gadget *gadget = cdev->gadget;
++ struct list_head *tmp;
++ int status, gcnum, funcs = 0;
++
++ list_for_each(tmp, &hidg_func_list)
++ funcs++;
++
++ if (!funcs)
++ return -ENODEV;
++
++ /* set up HID */
++ status = ghid_setup(cdev->gadget, funcs);
++ if (status < 0)
++ return status;
++
++ gcnum = usb_gadget_controller_number(gadget);
++ if (gcnum >= 0)
++ device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
++ else
++ device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
++
++
++ /* Allocate string descriptor numbers ... note that string
++ * contents can be overridden by the composite_dev glue.
++ */
++
++ /* device descriptor strings: manufacturer, product */
++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
++ init_utsname()->sysname, init_utsname()->release,
++ gadget->name);
++ status = usb_string_id(cdev);
++ if (status < 0)
++ return status;
++ strings_dev[STRING_MANUFACTURER_IDX].id = status;
++ device_desc.iManufacturer = status;
++
++ status = usb_string_id(cdev);
++ if (status < 0)
++ return status;
++ strings_dev[STRING_PRODUCT_IDX].id = status;
++ device_desc.iProduct = status;
++
++ /* register our configuration */
++ status = usb_add_config(cdev, &config_driver);
++ if (status < 0)
++ return status;
++
++ dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
++
++ return 0;
++}
++
++static int __exit hid_unbind(struct usb_composite_dev *cdev)
++{
++ ghid_cleanup();
++ return 0;
++}
++
++static int __init hidg_plat_driver_probe(struct platform_device *pdev)
++{
++ struct hidg_func_descriptor *func = pdev->dev.platform_data;
++ struct hidg_func_node *entry;
++
++ if (!func) {
++ dev_err(&pdev->dev, "Platform data missing\n");
++ return -ENODEV;
++ }
++
++ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
++ if (!entry)
++ return -ENOMEM;
++
++ entry->func = func;
++ list_add_tail(&entry->node, &hidg_func_list);
++
++ return 0;
++}
++
++static int __devexit hidg_plat_driver_remove(struct platform_device *pdev)
++{
++ struct hidg_func_node *e, *n;
++
++ list_for_each_entry_safe(e, n, &hidg_func_list, node) {
++ list_del(&e->node);
++ kfree(e);
++ }
++
++ return 0;
++}
++
++
++/****************************** Some noise ******************************/
++
++
++static struct usb_composite_driver hidg_driver = {
++ .name = "g_hid",
++ .dev = &device_desc,
++ .strings = dev_strings,
++ .bind = hid_bind,
++ .unbind = __exit_p(hid_unbind),
++};
++
++static struct platform_driver hidg_plat_driver = {
++ .remove = __devexit_p(hidg_plat_driver_remove),
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "hidg",
++ },
++};
++
++
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_AUTHOR("Fabien Chouteau, Peter Korsgaard");
++MODULE_LICENSE("GPL");
++
++static int __init hidg_init(void)
++{
++ platform_driver_probe(&hidg_plat_driver, hidg_plat_driver_probe);
++ return usb_composite_register(&hidg_driver);
++}
++module_init(hidg_init);
++
++static void __exit hidg_cleanup(void)
++{
++ platform_driver_unregister(&hidg_plat_driver);
++ usb_composite_unregister(&hidg_driver);
++}
++module_exit(hidg_cleanup);
+--- /dev/null
++++ b/include/linux/usb/g_hid.h
+@@ -0,0 +1,32 @@
++/*
++ * g_hid.h -- Header file for USB HID gadget driver
++ *
++ * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#ifndef __LINUX_USB_G_HID_H
++#define __LINUX_USB_G_HID_H
++
++struct hidg_func_descriptor {
++ unsigned char subclass;
++ unsigned char protocol;
++ unsigned short report_length;
++ unsigned short report_desc_length;
++ unsigned char report_desc[];
++};
++
++#endif /* __LINUX_USB_G_HID_H */
diff --git a/usb/usb-pl2303-increase-bulk-in-buffer-size-to-256-byte.patch b/usb/usb-pl2303-increase-bulk-in-buffer-size-to-256-byte.patch
new file mode 100644
index 00000000000000..cae75080d47992
--- /dev/null
+++ b/usb/usb-pl2303-increase-bulk-in-buffer-size-to-256-byte.patch
@@ -0,0 +1,29 @@
+From jhovold@gmail.com Fri Apr 23 15:25:03 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:00:41 +0100
+Subject: USB: pl2303: increase bulk-in buffer size to 256 byte
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863245-17230-6-git-send-email-jhovold@gmail.com>
+
+
+The pl2303 requires a bulk-in buffer larger than endpoint size to keep
+up at high baudrates without loosing data.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/pl2303.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/serial/pl2303.c
++++ b/drivers/usb/serial/pl2303.c
+@@ -1169,6 +1169,7 @@ static struct usb_serial_driver pl2303_d
+ .id_table = id_table,
+ .usb_driver = &pl2303_driver,
+ .num_ports = 1,
++ .bulk_in_size = 256,
+ .bulk_out_size = 256,
+ .open = pl2303_open,
+ .close = pl2303_close,
diff --git a/usb/usb-pl2303-increase-the-bulk-out-buffer-size-to-256-byte.patch b/usb/usb-pl2303-increase-the-bulk-out-buffer-size-to-256-byte.patch
new file mode 100644
index 00000000000000..18499c1509a3ef
--- /dev/null
+++ b/usb/usb-pl2303-increase-the-bulk-out-buffer-size-to-256-byte.patch
@@ -0,0 +1,36 @@
+From jhovold@gmail.com Fri Apr 23 15:24:43 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:00:40 +0100
+Subject: USB: pl2303: increase the bulk-out buffer size to 256 byte
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863245-17230-5-git-send-email-jhovold@gmail.com>
+
+
+Increase the bulk-out buffer size from 64 to 256 byte.
+
+This gives a significant increase in throughput already at 1Mbaud as well
+as lowered CPU usage. The buffer is big enough to keep up also at 3Mbaud
+(128b would not suffice).
+
+ 64b 256b
+921k: 640 KB/s 870 KB/s
+3M: 640 KB/s 2520 KB/s
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/pl2303.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/serial/pl2303.c
++++ b/drivers/usb/serial/pl2303.c
+@@ -1169,6 +1169,7 @@ static struct usb_serial_driver pl2303_d
+ .id_table = id_table,
+ .usb_driver = &pl2303_driver,
+ .num_ports = 1,
++ .bulk_out_size = 256,
+ .open = pl2303_open,
+ .close = pl2303_close,
+ .dtr_rts = pl2303_dtr_rts,
diff --git a/usb/usb-pl2303-switch-to-generic-read-implementation.patch b/usb/usb-pl2303-switch-to-generic-read-implementation.patch
new file mode 100644
index 00000000000000..875bad4cf9b079
--- /dev/null
+++ b/usb/usb-pl2303-switch-to-generic-read-implementation.patch
@@ -0,0 +1,143 @@
+From jhovold@gmail.com Fri Apr 23 15:31:50 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:05:58 +0100
+Subject: USB: pl2303: switch to generic read implementation
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-7-git-send-email-jhovold@gmail.com>
+
+
+Use generic read implementation and use process_read_urb to do device
+specific processing (handle line status).
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/pl2303.c | 84 +++++++++++++-------------------------------
+ 1 file changed, 25 insertions(+), 59 deletions(-)
+
+--- a/drivers/usb/serial/pl2303.c
++++ b/drivers/usb/serial/pl2303.c
+@@ -769,10 +769,8 @@ static int pl2303_open(struct tty_struct
+ pl2303_set_termios(tty, port, &tmp_termios);
+
+ dbg("%s - submitting read urb", __func__);
+- result = usb_submit_urb(port->read_urb, GFP_KERNEL);
++ result = usb_serial_generic_submit_read_urb(port, GFP_KERNEL);
+ if (result) {
+- dev_err(&port->dev, "%s - failed submitting read urb,"
+- " error %d\n", __func__, result);
+ pl2303_close(port);
+ return -EPROTO;
+ }
+@@ -1036,13 +1034,31 @@ exit:
+ __func__, retval);
+ }
+
+-static void pl2303_push_data(struct tty_struct *tty,
+- struct usb_serial_port *port, struct urb *urb,
+- u8 line_status)
++static void pl2303_process_read_urb(struct urb *urb)
+ {
++ struct usb_serial_port *port = urb->context;
++ struct pl2303_private *priv = usb_get_serial_port_data(port);
++ struct tty_struct *tty;
+ unsigned char *data = urb->transfer_buffer;
+- /* get tty_flag from status */
+ char tty_flag = TTY_NORMAL;
++ unsigned long flags;
++ u8 line_status;
++ int i;
++
++ /* update line status */
++ spin_lock_irqsave(&priv->lock, flags);
++ line_status = priv->line_status;
++ priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
++ spin_unlock_irqrestore(&priv->lock, flags);
++ wake_up_interruptible(&priv->delta_msr_wait);
++
++ if (!urb->actual_length)
++ return;
++
++ tty = tty_port_tty_get(&port->port);
++ if (!tty)
++ return;
++
+ /* break takes precedence over parity, */
+ /* which takes precedence over framing errors */
+ if (line_status & UART_BREAK_ERROR)
+@@ -1060,63 +1076,13 @@ static void pl2303_push_data(struct tty_
+ if (tty_flag == TTY_NORMAL && !(port->port.console && port->sysrq))
+ tty_insert_flip_string(tty, data, urb->actual_length);
+ else {
+- int i;
+ for (i = 0; i < urb->actual_length; ++i)
+ if (!usb_serial_handle_sysrq_char(tty, port, data[i]))
+ tty_insert_flip_char(tty, data[i], tty_flag);
+ }
+- tty_flip_buffer_push(tty);
+-}
+-
+-static void pl2303_read_bulk_callback(struct urb *urb)
+-{
+- struct usb_serial_port *port = urb->context;
+- struct pl2303_private *priv = usb_get_serial_port_data(port);
+- struct tty_struct *tty;
+- unsigned long flags;
+- int result;
+- int status = urb->status;
+- u8 line_status;
+-
+- dbg("%s - port %d", __func__, port->number);
+-
+- if (status) {
+- dbg("%s - urb status = %d", __func__, status);
+- if (status == -EPROTO) {
+- /* PL2303 mysteriously fails with -EPROTO reschedule
+- * the read */
+- dbg("%s - caught -EPROTO, resubmitting the urb",
+- __func__);
+- result = usb_submit_urb(urb, GFP_ATOMIC);
+- if (result)
+- dev_err(&urb->dev->dev, "%s - failed"
+- " resubmitting read urb, error %d\n",
+- __func__, result);
+- return;
+- }
+- dbg("%s - unable to handle the error, exiting.", __func__);
+- return;
+- }
+
+- usb_serial_debug_data(debug, &port->dev, __func__,
+- urb->actual_length, urb->transfer_buffer);
+-
+- spin_lock_irqsave(&priv->lock, flags);
+- line_status = priv->line_status;
+- priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
+- spin_unlock_irqrestore(&priv->lock, flags);
+- wake_up_interruptible(&priv->delta_msr_wait);
+-
+- tty = tty_port_tty_get(&port->port);
+- if (tty && urb->actual_length) {
+- pl2303_push_data(tty, port, urb, line_status);
+- }
++ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+- /* Schedule the next read _if_ we are still open */
+- result = usb_submit_urb(urb, GFP_ATOMIC);
+- if (result && result != -EPERM)
+- dev_err(&urb->dev->dev, "%s - failed resubmitting"
+- " read urb, error %d\n", __func__, result);
+ }
+
+ static void pl2303_write_bulk_callback(struct urb *urb)
+@@ -1181,7 +1147,7 @@ static struct usb_serial_driver pl2303_d
+ .set_termios = pl2303_set_termios,
+ .tiocmget = pl2303_tiocmget,
+ .tiocmset = pl2303_tiocmset,
+- .read_bulk_callback = pl2303_read_bulk_callback,
++ .process_read_urb = pl2303_process_read_urb,
+ .read_int_callback = pl2303_read_int_callback,
+ .write_bulk_callback = pl2303_write_bulk_callback,
+ .write_room = pl2303_write_room,
diff --git a/usb/usb-pl2303-switch-to-generic-write-implementation.patch b/usb/usb-pl2303-switch-to-generic-write-implementation.patch
new file mode 100644
index 00000000000000..74c0faf434af11
--- /dev/null
+++ b/usb/usb-pl2303-switch-to-generic-write-implementation.patch
@@ -0,0 +1,432 @@
+From jhovold@gmail.com Fri Apr 23 15:34:34 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:06:03 +0100
+Subject: USB: pl2303: switch to generic write implementation
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-12-git-send-email-jhovold@gmail.com>
+
+
+Replace custom fifo-based write implementation with the generic
+kfifo-based one.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/pl2303.c | 330 --------------------------------------------
+ 1 file changed, 4 insertions(+), 326 deletions(-)
+
+--- a/drivers/usb/serial/pl2303.c
++++ b/drivers/usb/serial/pl2303.c
+@@ -40,16 +40,6 @@ static int debug;
+
+ #define PL2303_CLOSING_WAIT (30*HZ)
+
+-#define PL2303_BUF_SIZE 1024
+-#define PL2303_TMP_BUF_SIZE 1024
+-
+-struct pl2303_buf {
+- unsigned int buf_size;
+- char *buf_buf;
+- char *buf_get;
+- char *buf_put;
+-};
+-
+ static const struct usb_device_id id_table[] = {
+ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
+ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
+@@ -156,173 +146,12 @@ enum pl2303_type {
+
+ struct pl2303_private {
+ spinlock_t lock;
+- struct pl2303_buf *buf;
+- int write_urb_in_use;
+ wait_queue_head_t delta_msr_wait;
+ u8 line_control;
+ u8 line_status;
+ enum pl2303_type type;
+ };
+
+-/*
+- * pl2303_buf_alloc
+- *
+- * Allocate a circular buffer and all associated memory.
+- */
+-static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
+-{
+- struct pl2303_buf *pb;
+-
+- if (size == 0)
+- return NULL;
+-
+- pb = kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
+- if (pb == NULL)
+- return NULL;
+-
+- pb->buf_buf = kmalloc(size, GFP_KERNEL);
+- if (pb->buf_buf == NULL) {
+- kfree(pb);
+- return NULL;
+- }
+-
+- pb->buf_size = size;
+- pb->buf_get = pb->buf_put = pb->buf_buf;
+-
+- return pb;
+-}
+-
+-/*
+- * pl2303_buf_free
+- *
+- * Free the buffer and all associated memory.
+- */
+-static void pl2303_buf_free(struct pl2303_buf *pb)
+-{
+- if (pb) {
+- kfree(pb->buf_buf);
+- kfree(pb);
+- }
+-}
+-
+-/*
+- * pl2303_buf_clear
+- *
+- * Clear out all data in the circular buffer.
+- */
+-static void pl2303_buf_clear(struct pl2303_buf *pb)
+-{
+- if (pb != NULL)
+- pb->buf_get = pb->buf_put;
+- /* equivalent to a get of all data available */
+-}
+-
+-/*
+- * pl2303_buf_data_avail
+- *
+- * Return the number of bytes of data available in the circular
+- * buffer.
+- */
+-static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
+-{
+- if (pb == NULL)
+- return 0;
+-
+- return (pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size;
+-}
+-
+-/*
+- * pl2303_buf_space_avail
+- *
+- * Return the number of bytes of space available in the circular
+- * buffer.
+- */
+-static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
+-{
+- if (pb == NULL)
+- return 0;
+-
+- return (pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size;
+-}
+-
+-/*
+- * pl2303_buf_put
+- *
+- * Copy data data from a user buffer and put it into the circular buffer.
+- * Restrict to the amount of space available.
+- *
+- * Return the number of bytes copied.
+- */
+-static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
+- unsigned int count)
+-{
+- unsigned int len;
+-
+- if (pb == NULL)
+- return 0;
+-
+- len = pl2303_buf_space_avail(pb);
+- if (count > len)
+- count = len;
+-
+- if (count == 0)
+- return 0;
+-
+- len = pb->buf_buf + pb->buf_size - pb->buf_put;
+- if (count > len) {
+- memcpy(pb->buf_put, buf, len);
+- memcpy(pb->buf_buf, buf+len, count - len);
+- pb->buf_put = pb->buf_buf + count - len;
+- } else {
+- memcpy(pb->buf_put, buf, count);
+- if (count < len)
+- pb->buf_put += count;
+- else /* count == len */
+- pb->buf_put = pb->buf_buf;
+- }
+-
+- return count;
+-}
+-
+-/*
+- * pl2303_buf_get
+- *
+- * Get data from the circular buffer and copy to the given buffer.
+- * Restrict to the amount of data available.
+- *
+- * Return the number of bytes copied.
+- */
+-static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
+- unsigned int count)
+-{
+- unsigned int len;
+-
+- if (pb == NULL)
+- return 0;
+-
+- len = pl2303_buf_data_avail(pb);
+- if (count > len)
+- count = len;
+-
+- if (count == 0)
+- return 0;
+-
+- len = pb->buf_buf + pb->buf_size - pb->buf_get;
+- if (count > len) {
+- memcpy(buf, pb->buf_get, len);
+- memcpy(buf+len, pb->buf_buf, count - len);
+- pb->buf_get = pb->buf_buf + count - len;
+- } else {
+- memcpy(buf, pb->buf_get, count);
+- if (count < len)
+- pb->buf_get += count;
+- else /* count == len */
+- pb->buf_get = pb->buf_buf;
+- }
+-
+- return count;
+-}
+-
+ static int pl2303_vendor_read(__u16 value, __u16 index,
+ struct usb_serial *serial, unsigned char *buf)
+ {
+@@ -371,11 +200,6 @@ static int pl2303_startup(struct usb_ser
+ if (!priv)
+ goto cleanup;
+ spin_lock_init(&priv->lock);
+- priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE);
+- if (priv->buf == NULL) {
+- kfree(priv);
+- goto cleanup;
+- }
+ init_waitqueue_head(&priv->delta_msr_wait);
+ priv->type = type;
+ usb_set_serial_port_data(serial->port[i], priv);
+@@ -403,7 +227,6 @@ cleanup:
+ kfree(buf);
+ for (--i; i >= 0; --i) {
+ priv = usb_get_serial_port_data(serial->port[i]);
+- pl2303_buf_free(priv->buf);
+ kfree(priv);
+ usb_set_serial_port_data(serial->port[i], NULL);
+ }
+@@ -421,102 +244,6 @@ static int set_control_lines(struct usb_
+ return retval;
+ }
+
+-static void pl2303_send(struct usb_serial_port *port)
+-{
+- int count, result;
+- struct pl2303_private *priv = usb_get_serial_port_data(port);
+- unsigned long flags;
+-
+- dbg("%s - port %d", __func__, port->number);
+-
+- spin_lock_irqsave(&priv->lock, flags);
+-
+- if (priv->write_urb_in_use) {
+- spin_unlock_irqrestore(&priv->lock, flags);
+- return;
+- }
+-
+- count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer,
+- port->bulk_out_size);
+-
+- if (count == 0) {
+- spin_unlock_irqrestore(&priv->lock, flags);
+- return;
+- }
+-
+- priv->write_urb_in_use = 1;
+-
+- spin_unlock_irqrestore(&priv->lock, flags);
+-
+- usb_serial_debug_data(debug, &port->dev, __func__, count,
+- port->write_urb->transfer_buffer);
+-
+- port->write_urb->transfer_buffer_length = count;
+- result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+- if (result) {
+- dev_err(&port->dev, "%s - failed submitting write urb,"
+- " error %d\n", __func__, result);
+- priv->write_urb_in_use = 0;
+- /* TODO: reschedule pl2303_send */
+- }
+-
+- usb_serial_port_softint(port);
+-}
+-
+-static int pl2303_write(struct tty_struct *tty, struct usb_serial_port *port,
+- const unsigned char *buf, int count)
+-{
+- struct pl2303_private *priv = usb_get_serial_port_data(port);
+- unsigned long flags;
+-
+- dbg("%s - port %d, %d bytes", __func__, port->number, count);
+-
+- if (!count)
+- return count;
+-
+- spin_lock_irqsave(&priv->lock, flags);
+- count = pl2303_buf_put(priv->buf, buf, count);
+- spin_unlock_irqrestore(&priv->lock, flags);
+-
+- pl2303_send(port);
+-
+- return count;
+-}
+-
+-static int pl2303_write_room(struct tty_struct *tty)
+-{
+- struct usb_serial_port *port = tty->driver_data;
+- struct pl2303_private *priv = usb_get_serial_port_data(port);
+- int room = 0;
+- unsigned long flags;
+-
+- dbg("%s - port %d", __func__, port->number);
+-
+- spin_lock_irqsave(&priv->lock, flags);
+- room = pl2303_buf_space_avail(priv->buf);
+- spin_unlock_irqrestore(&priv->lock, flags);
+-
+- dbg("%s - returns %d", __func__, room);
+- return room;
+-}
+-
+-static int pl2303_chars_in_buffer(struct tty_struct *tty)
+-{
+- struct usb_serial_port *port = tty->driver_data;
+- struct pl2303_private *priv = usb_get_serial_port_data(port);
+- int chars = 0;
+- unsigned long flags;
+-
+- dbg("%s - port %d", __func__, port->number);
+-
+- spin_lock_irqsave(&priv->lock, flags);
+- chars = pl2303_buf_data_avail(priv->buf);
+- spin_unlock_irqrestore(&priv->lock, flags);
+-
+- dbg("%s - returns %d", __func__, chars);
+- return chars;
+-}
+-
+ static void pl2303_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port, struct ktermios *old_termios)
+ {
+@@ -728,15 +455,14 @@ static void pl2303_dtr_rts(struct usb_se
+
+ static void pl2303_close(struct usb_serial_port *port)
+ {
+- struct pl2303_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ dbg("%s - port %d", __func__, port->number);
+
+- spin_lock_irqsave(&priv->lock, flags);
++ spin_lock_irqsave(&port->lock, flags);
+ /* clear out any remaining data in the buffer */
+- pl2303_buf_clear(priv->buf);
+- spin_unlock_irqrestore(&priv->lock, flags);
++ kfifo_reset_out(&port->write_fifo);
++ spin_unlock_irqrestore(&port->lock, flags);
+
+ /* shutdown our urbs */
+ dbg("%s - shutting down urbs", __func__);
+@@ -950,10 +676,7 @@ static void pl2303_release(struct usb_se
+
+ for (i = 0; i < serial->num_ports; ++i) {
+ priv = usb_get_serial_port_data(serial->port[i]);
+- if (priv) {
+- pl2303_buf_free(priv->buf);
+- kfree(priv);
+- }
++ kfree(priv);
+ }
+ }
+
+@@ -1085,47 +808,6 @@ static void pl2303_process_read_urb(stru
+ tty_kref_put(tty);
+ }
+
+-static void pl2303_write_bulk_callback(struct urb *urb)
+-{
+- struct usb_serial_port *port = urb->context;
+- struct pl2303_private *priv = usb_get_serial_port_data(port);
+- int result;
+- int status = urb->status;
+-
+- dbg("%s - port %d", __func__, port->number);
+-
+- switch (status) {
+- case 0:
+- /* success */
+- break;
+- case -ECONNRESET:
+- case -ENOENT:
+- case -ESHUTDOWN:
+- /* this urb is terminated, clean up */
+- dbg("%s - urb shutting down with status: %d", __func__,
+- status);
+- priv->write_urb_in_use = 0;
+- return;
+- default:
+- /* error in the urb, so we have to resubmit it */
+- dbg("%s - Overflow in write", __func__);
+- dbg("%s - nonzero write bulk status received: %d", __func__,
+- status);
+- port->write_urb->transfer_buffer_length = 1;
+- result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+- if (result)
+- dev_err(&urb->dev->dev, "%s - failed resubmitting write"
+- " urb, error %d\n", __func__, result);
+- else
+- return;
+- }
+-
+- priv->write_urb_in_use = 0;
+-
+- /* send any buffered data */
+- pl2303_send(port);
+-}
+-
+ /* All of the device info needed for the PL2303 SIO serial converter */
+ static struct usb_serial_driver pl2303_device = {
+ .driver = {
+@@ -1141,7 +823,6 @@ static struct usb_serial_driver pl2303_d
+ .close = pl2303_close,
+ .dtr_rts = pl2303_dtr_rts,
+ .carrier_raised = pl2303_carrier_raised,
+- .write = pl2303_write,
+ .ioctl = pl2303_ioctl,
+ .break_ctl = pl2303_break_ctl,
+ .set_termios = pl2303_set_termios,
+@@ -1149,9 +830,6 @@ static struct usb_serial_driver pl2303_d
+ .tiocmset = pl2303_tiocmset,
+ .process_read_urb = pl2303_process_read_urb,
+ .read_int_callback = pl2303_read_int_callback,
+- .write_bulk_callback = pl2303_write_bulk_callback,
+- .write_room = pl2303_write_room,
+- .chars_in_buffer = pl2303_chars_in_buffer,
+ .attach = pl2303_startup,
+ .release = pl2303_release,
+ };
diff --git a/usb/usb-pl2303-use-generic-close.patch b/usb/usb-pl2303-use-generic-close.patch
new file mode 100644
index 00000000000000..db07b764eed74b
--- /dev/null
+++ b/usb/usb-pl2303-use-generic-close.patch
@@ -0,0 +1,44 @@
+From jhovold@gmail.com Fri Apr 23 15:34:47 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:06:04 +0100
+Subject: USB: pl2303: use generic close
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-13-git-send-email-jhovold@gmail.com>
+
+
+Use usb_serial_generic_close to kill the read and write urbs and to
+reset the write fifo.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/pl2303.c | 13 +------------
+ 1 file changed, 1 insertion(+), 12 deletions(-)
+
+--- a/drivers/usb/serial/pl2303.c
++++ b/drivers/usb/serial/pl2303.c
+@@ -455,21 +455,10 @@ static void pl2303_dtr_rts(struct usb_se
+
+ static void pl2303_close(struct usb_serial_port *port)
+ {
+- unsigned long flags;
+-
+ dbg("%s - port %d", __func__, port->number);
+
+- spin_lock_irqsave(&port->lock, flags);
+- /* clear out any remaining data in the buffer */
+- kfifo_reset_out(&port->write_fifo);
+- spin_unlock_irqrestore(&port->lock, flags);
+-
+- /* shutdown our urbs */
+- dbg("%s - shutting down urbs", __func__);
+- usb_kill_urb(port->write_urb);
+- usb_kill_urb(port->read_urb);
++ usb_serial_generic_close(port);
+ usb_kill_urb(port->interrupt_in_urb);
+-
+ }
+
+ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
diff --git a/usb/usb-serial-allow-custom-multi-urb-write-bulk-callbacks.patch b/usb/usb-serial-allow-custom-multi-urb-write-bulk-callbacks.patch
new file mode 100644
index 00000000000000..99921379161b37
--- /dev/null
+++ b/usb/usb-serial-allow-custom-multi-urb-write-bulk-callbacks.patch
@@ -0,0 +1,30 @@
+From jhovold@gmail.com Fri Apr 23 15:35:42 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:06:06 +0100
+Subject: USB: serial: allow custom multi-urb write bulk callbacks
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-15-git-send-email-jhovold@gmail.com>
+
+
+Allow drivers to implement their own multi-urb write bulk callbacks as
+we do for single urb writes.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -217,7 +217,7 @@ static int usb_serial_multi_urb_write(st
+ usb_sndbulkpipe(port->serial->dev,
+ port->bulk_out_endpointAddress),
+ buffer, towrite,
+- usb_serial_generic_write_bulk_callback, port);
++ port->serial->type->write_bulk_callback, port);
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status) {
diff --git a/usb/usb-serial-allow-drivers-to-define-bulk-buffer-sizes.patch b/usb/usb-serial-allow-drivers-to-define-bulk-buffer-sizes.patch
new file mode 100644
index 00000000000000..8eab01358f9913
--- /dev/null
+++ b/usb/usb-serial-allow-drivers-to-define-bulk-buffer-sizes.patch
@@ -0,0 +1,202 @@
+From jhovold@gmail.com Fri Apr 23 15:23:49 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:00:37 +0100
+Subject: [PATCH 1/9] USB: serial: allow drivers to define bulk buffer sizes
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863245-17230-2-git-send-email-jhovold@gmail.com>
+
+
+Allow drivers to define custom bulk in/out buffer sizes in struct
+usb_serial_driver. If not set, fall back to the default buffer size
+which matches the endpoint size.
+
+Three drivers are currently freeing the pre-allocated buffers and
+allocating larger ones to achieve this at port probe (ftdi_sio) or even
+at port open (ipaq and iuu_phoenix), which needless to say is suboptimal.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ftdi_sio.c | 16 +---------------
+ drivers/usb/serial/ipaq.c | 28 ++--------------------------
+ drivers/usb/serial/iuu_phoenix.c | 30 ++----------------------------
+ drivers/usb/serial/usb-serial.c | 8 ++++++--
+ include/linux/usb/serial.h | 5 +++++
+ 5 files changed, 16 insertions(+), 71 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -768,9 +768,6 @@ static const char *ftdi_chip_name[] = {
+ };
+
+
+-/* Constants for read urb and write urb */
+-#define BUFSZ 512
+-
+ /* 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)
+@@ -821,6 +818,7 @@ static struct usb_serial_driver ftdi_sio
+ .usb_driver = &ftdi_driver,
+ .id_table = id_table_combined,
+ .num_ports = 1,
++ .bulk_in_size = 512,
+ .probe = ftdi_sio_probe,
+ .port_probe = ftdi_sio_port_probe,
+ .port_remove = ftdi_sio_port_remove,
+@@ -1552,18 +1550,6 @@ static int ftdi_sio_port_probe(struct us
+ if (quirk && quirk->port_probe)
+ quirk->port_probe(priv);
+
+- /* Increase the size of read buffers */
+- kfree(port->bulk_in_buffer);
+- port->bulk_in_buffer = kmalloc(BUFSZ, GFP_KERNEL);
+- if (!port->bulk_in_buffer) {
+- kfree(priv);
+- return -ENOMEM;
+- }
+- if (port->read_urb) {
+- port->read_urb->transfer_buffer = port->bulk_in_buffer;
+- port->read_urb->transfer_buffer_length = BUFSZ;
+- }
+-
+ priv->port = port;
+
+ /* Free port's existing write urb and transfer buffer. */
+--- a/drivers/usb/serial/ipaq.c
++++ b/drivers/usb/serial/ipaq.c
+@@ -571,6 +571,8 @@ static struct usb_serial_driver ipaq_dev
+ .description = "PocketPC PDA",
+ .usb_driver = &ipaq_driver,
+ .id_table = ipaq_id_table,
++ .bulk_in_size = URBDATA_SIZE,
++ .bulk_out_size = URBDATA_SIZE,
+ .open = ipaq_open,
+ .close = ipaq_close,
+ .attach = ipaq_startup,
+@@ -628,32 +630,6 @@ static int ipaq_open(struct tty_struct *
+ priv->free_len += PACKET_SIZE;
+ }
+
+- /*
+- * Lose the small buffers usbserial provides. Make larger ones.
+- */
+-
+- kfree(port->bulk_in_buffer);
+- kfree(port->bulk_out_buffer);
+- /* make sure the generic serial code knows */
+- port->bulk_out_buffer = NULL;
+-
+- port->bulk_in_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
+- if (port->bulk_in_buffer == NULL)
+- goto enomem;
+-
+- port->bulk_out_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
+- if (port->bulk_out_buffer == NULL) {
+- /* the buffer is useless, free it */
+- kfree(port->bulk_in_buffer);
+- port->bulk_in_buffer = NULL;
+- goto enomem;
+- }
+- port->read_urb->transfer_buffer = port->bulk_in_buffer;
+- port->write_urb->transfer_buffer = port->bulk_out_buffer;
+- port->read_urb->transfer_buffer_length = URBDATA_SIZE;
+- port->bulk_out_size = port->write_urb->transfer_buffer_length
+- = URBDATA_SIZE;
+-
+ msleep(1000*initial_wait);
+
+ /*
+--- a/drivers/usb/serial/iuu_phoenix.c
++++ b/drivers/usb/serial/iuu_phoenix.c
+@@ -1044,34 +1044,6 @@ static int iuu_open(struct tty_struct *t
+ if (buf == NULL)
+ return -ENOMEM;
+
+- /* fixup the endpoint buffer size */
+- kfree(port->bulk_out_buffer);
+- port->bulk_out_buffer = kmalloc(512, GFP_KERNEL);
+- port->bulk_out_size = 512;
+- kfree(port->bulk_in_buffer);
+- port->bulk_in_buffer = kmalloc(512, GFP_KERNEL);
+- port->bulk_in_size = 512;
+-
+- if (!port->bulk_out_buffer || !port->bulk_in_buffer) {
+- kfree(port->bulk_out_buffer);
+- kfree(port->bulk_in_buffer);
+- kfree(buf);
+- return -ENOMEM;
+- }
+-
+- usb_fill_bulk_urb(port->write_urb, port->serial->dev,
+- usb_sndbulkpipe(port->serial->dev,
+- port->bulk_out_endpointAddress),
+- port->bulk_out_buffer, 512,
+- NULL, NULL);
+-
+-
+- usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+- usb_rcvbulkpipe(port->serial->dev,
+- port->bulk_in_endpointAddress),
+- port->bulk_in_buffer, 512,
+- NULL, NULL);
+-
+ priv->poll = 0;
+
+ /* initialize writebuf */
+@@ -1277,6 +1249,8 @@ static struct usb_serial_driver iuu_devi
+ },
+ .id_table = id_table,
+ .num_ports = 1,
++ .bulk_in_size = 512,
++ .bulk_out_size = 512,
+ .port_probe = iuu_create_sysfs_attrs,
+ .port_remove = iuu_remove_sysfs_attrs,
+ .open = iuu_open,
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -901,7 +901,9 @@ int usb_serial_probe(struct usb_interfac
+ dev_err(&interface->dev, "No free urbs available\n");
+ goto probe_error;
+ }
+- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
++ buffer_size = serial->type->bulk_in_size;
++ if (!buffer_size)
++ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ port->bulk_in_size = buffer_size;
+ port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
+ port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+@@ -927,7 +929,9 @@ int usb_serial_probe(struct usb_interfac
+ }
+ if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
+ goto probe_error;
+- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
++ buffer_size = serial->type->bulk_out_size;
++ if (!buffer_size)
++ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ port->bulk_out_size = buffer_size;
+ port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
+ port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
+--- a/include/linux/usb/serial.h
++++ b/include/linux/usb/serial.h
+@@ -179,6 +179,8 @@ static inline void usb_set_serial_data(s
+ * @id_table: pointer to a list of usb_device_id structures that define all
+ * of the devices this structure can support.
+ * @num_ports: the number of different ports this device will have.
++ * @bulk_in_size: bytes to allocate for bulk-in buffer (0 = end-point size)
++ * @bulk_out_size: bytes to allocate for bulk-out buffer (0 = end-point size)
+ * @calc_num_ports: pointer to a function to determine how many ports this
+ * device has dynamically. It will be called after the probe()
+ * callback is called, but before attach()
+@@ -223,6 +225,9 @@ struct usb_serial_driver {
+ struct usb_dynids dynids;
+ int max_in_flight_urbs;
+
++ size_t bulk_in_size;
++ size_t bulk_out_size;
++
+ int (*probe)(struct usb_serial *serial, const struct usb_device_id *id);
+ int (*attach)(struct usb_serial *serial);
+ int (*calc_num_ports) (struct usb_serial *serial);
diff --git a/usb/usb-serial-clean-up-generic-write-start-busy-test.patch b/usb/usb-serial-clean-up-generic-write-start-busy-test.patch
new file mode 100644
index 00000000000000..898e839c7947ed
--- /dev/null
+++ b/usb/usb-serial-clean-up-generic-write-start-busy-test.patch
@@ -0,0 +1,47 @@
+From jhovold@gmail.com Fri Apr 23 15:34:12 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:06:02 +0100
+Subject: USB: serial: clean up generic write start busy test
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-11-git-send-email-jhovold@gmail.com>
+
+
+Submit write urb if it is not already in use and we have buffered data.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 15 ++++-----------
+ 1 file changed, 4 insertions(+), 11 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -259,22 +259,15 @@ static int usb_serial_generic_write_star
+ int result;
+ int count;
+ unsigned long flags;
+- bool start_io;
+
+- /* Atomically determine whether we can and need to start a USB
+- * operation. */
+ spin_lock_irqsave(&port->lock, flags);
+- if (port->write_urb_busy)
+- start_io = false;
+- else {
+- start_io = (kfifo_len(&port->write_fifo) != 0);
+- port->write_urb_busy = start_io;
++ if (port->write_urb_busy || !kfifo_len(&port->write_fifo)) {
++ spin_unlock_irqrestore(&port->lock, flags);
++ return 0;
+ }
++ port->write_urb_busy = 1;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+- if (!start_io)
+- return 0;
+-
+ data = port->write_urb->transfer_buffer;
+ count = kfifo_out_locked(&port->write_fifo, data, port->bulk_out_size, &port->lock);
+ usb_serial_debug_data(debug, &port->dev, __func__, count, data);
diff --git a/usb/usb-serial-clean-up-read-processing-in-generic-driver.patch b/usb/usb-serial-clean-up-read-processing-in-generic-driver.patch
new file mode 100644
index 00000000000000..5384337b87ad16
--- /dev/null
+++ b/usb/usb-serial-clean-up-read-processing-in-generic-driver.patch
@@ -0,0 +1,109 @@
+From jhovold@gmail.com Fri Apr 23 15:29:55 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:05:56 +0100
+Subject: USB: serial: clean up read processing in generic driver
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-5-git-send-email-jhovold@gmail.com>
+
+
+Always process and flush read urb, but only resubmit when not throttled.
+The new tty-layer supply plenty of slack so there is really no need to
+cache and delay processing of a single urb while throttled.
+
+Note that unthrottle now submits using GFP_KERNEL as we are not in
+atomic context (so there is no need to save irq state either).
+
+Note also that the process_read_urb function could be added to
+usb_serial_driver should any driver need to do any device specific
+processing.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 27 +++++++++++----------------
+ 1 file changed, 11 insertions(+), 16 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -408,16 +408,16 @@ int usb_serial_generic_submit_read_urb(s
+ }
+ EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urb);
+
+-/* Push data to tty layer and resubmit the bulk read URB */
+-static void flush_and_resubmit_read_urb(struct usb_serial_port *port)
++static void usb_serial_generic_process_read_urb(struct urb *urb)
+ {
+- struct urb *urb = port->read_urb;
+- struct tty_struct *tty = tty_port_tty_get(&port->port);
++ struct usb_serial_port *port = urb->context;
++ struct tty_struct *tty;
+ char *ch = (char *)urb->transfer_buffer;
+ int i;
+
++ tty = tty_port_tty_get(&port->port);
+ if (!tty)
+- goto done;
++ return;
+
+ /* The per character mucking around with sysrq path it too slow for
+ stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases
+@@ -425,7 +425,6 @@ static void flush_and_resubmit_read_urb(
+ if (!port->port.console || !port->sysrq)
+ tty_insert_flip_string(tty, ch, urb->actual_length);
+ else {
+- /* Push data to tty */
+ for (i = 0; i < urb->actual_length; i++, ch++) {
+ if (!usb_serial_handle_sysrq_char(tty, port, *ch))
+ tty_insert_flip_char(tty, *ch, TTY_NORMAL);
+@@ -433,8 +432,6 @@ static void flush_and_resubmit_read_urb(
+ }
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+-done:
+- usb_serial_generic_submit_read_urb(port, GFP_ATOMIC);
+ }
+
+ void usb_serial_generic_read_bulk_callback(struct urb *urb)
+@@ -454,13 +451,14 @@ void usb_serial_generic_read_bulk_callba
+
+ usb_serial_debug_data(debug, &port->dev, __func__,
+ urb->actual_length, data);
++ usb_serial_generic_process_read_urb(urb);
+
+ /* Throttle the device if requested by tty */
+ spin_lock_irqsave(&port->lock, flags);
+ port->throttled = port->throttle_req;
+ if (!port->throttled) {
+ spin_unlock_irqrestore(&port->lock, flags);
+- flush_and_resubmit_read_urb(port);
++ usb_serial_generic_submit_read_urb(port, GFP_ATOMIC);
+ } else
+ spin_unlock_irqrestore(&port->lock, flags);
+ }
+@@ -523,20 +521,17 @@ void usb_serial_generic_unthrottle(struc
+ {
+ struct usb_serial_port *port = tty->driver_data;
+ int was_throttled;
+- unsigned long flags;
+
+ dbg("%s - port %d", __func__, port->number);
+
+ /* Clear the throttle flags */
+- spin_lock_irqsave(&port->lock, flags);
++ spin_lock_irq(&port->lock);
+ was_throttled = port->throttled;
+ port->throttled = port->throttle_req = 0;
+- spin_unlock_irqrestore(&port->lock, flags);
++ spin_unlock_irq(&port->lock);
+
+- if (was_throttled) {
+- /* Resume reading from device */
+- flush_and_resubmit_read_urb(port);
+- }
++ if (was_throttled)
++ usb_serial_generic_submit_read_urb(port, GFP_KERNEL);
+ }
+
+ #ifdef CONFIG_MAGIC_SYSRQ
diff --git a/usb/usb-serial-clean-up-some-error-and-debug-messages-in-generic-driver.patch b/usb/usb-serial-clean-up-some-error-and-debug-messages-in-generic-driver.patch
new file mode 100644
index 00000000000000..316e9678933a80
--- /dev/null
+++ b/usb/usb-serial-clean-up-some-error-and-debug-messages-in-generic-driver.patch
@@ -0,0 +1,63 @@
+From jhovold@gmail.com Fri Apr 23 15:32:52 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:06:01 +0100
+Subject: USB: serial: clean up some error and debug messages in generic driver
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-10-git-send-email-jhovold@gmail.com>
+
+
+Clean up error messages on usb_submit_urb failure.
+Remove debug message on zero-length writes.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 13 ++++---------
+ 1 file changed, 4 insertions(+), 9 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -221,8 +221,7 @@ static int usb_serial_multi_urb_write(st
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status) {
+- dev_err(&port->dev,
+- "%s - failed submitting write urb, error %d\n",
++ dev_err(&port->dev, "%s - error submitting urb: %d\n",
+ __func__, status);
+ goto error;
+ }
+@@ -285,8 +284,7 @@ static int usb_serial_generic_write_star
+ /* send the data out the bulk port */
+ result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ if (result) {
+- dev_err(&port->dev,
+- "%s - failed submitting write urb, error %d\n",
++ dev_err(&port->dev, "%s - error submitting urb: %d\n",
+ __func__, result);
+ /* don't have to grab the lock here, as we will
+ retry if != 0 */
+@@ -324,10 +322,8 @@ int usb_serial_generic_write(struct tty_
+ if (!port->bulk_out_size)
+ return -ENODEV;
+
+- if (count == 0) {
+- dbg("%s - write request of 0 bytes", __func__);
++ if (!count)
+ return 0;
+- }
+
+ if (serial->type->max_in_flight_urbs)
+ return usb_serial_multi_urb_write(tty, port,
+@@ -400,8 +396,7 @@ int usb_serial_generic_submit_read_urb(s
+
+ result = usb_submit_urb(port->read_urb, mem_flags);
+ if (result && result != -EPERM) {
+- dev_err(&port->dev,
+- "%s - failed submitting read urb, error %d\n",
++ dev_err(&port->dev, "%s - error submitting urb: %d\n",
+ __func__, result);
+ }
+ return result;
diff --git a/usb/usb-serial-clear-fifo-on-close.patch b/usb/usb-serial-clear-fifo-on-close.patch
new file mode 100644
index 00000000000000..3d8adcc7e4e9a3
--- /dev/null
+++ b/usb/usb-serial-clear-fifo-on-close.patch
@@ -0,0 +1,41 @@
+From jhovold@gmail.com Fri Apr 23 15:25:52 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:00:44 +0100
+Subject: USB: serial: clear fifo on close
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863245-17230-9-git-send-email-jhovold@gmail.com>
+
+
+Make sure fifo is emptied on close.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -156,13 +156,19 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_ope
+ static void generic_cleanup(struct usb_serial_port *port)
+ {
+ struct usb_serial *serial = port->serial;
++ unsigned long flags;
+
+ dbg("%s - port %d", __func__, port->number);
+
+ if (serial->dev) {
+ /* shutdown any bulk transfers that might be going on */
+- if (port->bulk_out_size)
++ if (port->bulk_out_size) {
+ usb_kill_urb(port->write_urb);
++
++ spin_lock_irqsave(&port->lock, flags);
++ kfifo_reset_out(&port->write_fifo);
++ spin_unlock_irqrestore(&port->lock, flags);
++ }
+ if (port->bulk_in_size)
+ usb_kill_urb(port->read_urb);
+ }
diff --git a/usb/usb-serial-export-generic-throttle-and-unthrottle.patch b/usb/usb-serial-export-generic-throttle-and-unthrottle.patch
new file mode 100644
index 00000000000000..4283bdc0ab3161
--- /dev/null
+++ b/usb/usb-serial-export-generic-throttle-and-unthrottle.patch
@@ -0,0 +1,40 @@
+From jhovold@gmail.com Fri Apr 23 15:32:19 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:05:59 +0100
+Subject: USB: serial: export generic throttle and unthrottle
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-8-git-send-email-jhovold@gmail.com>
+
+
+Allow drivers to use the generic throttle and unthrottle implementation.
+This makes sense for drivers using the generic read functionality.
+
+Note that drivers need to set these explicitly in order to enable them
+(i.e., we do not set them at port probe if not defined).
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -517,6 +517,7 @@ void usb_serial_generic_throttle(struct
+ port->throttle_req = 1;
+ spin_unlock_irqrestore(&port->lock, flags);
+ }
++EXPORT_SYMBOL_GPL(usb_serial_generic_throttle);
+
+ void usb_serial_generic_unthrottle(struct tty_struct *tty)
+ {
+@@ -534,6 +535,7 @@ void usb_serial_generic_unthrottle(struc
+ if (was_throttled)
+ usb_serial_generic_submit_read_urb(port, GFP_KERNEL);
+ }
++EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle);
+
+ #ifdef CONFIG_MAGIC_SYSRQ
+ int usb_serial_handle_sysrq_char(struct tty_struct *tty,
diff --git a/usb/usb-serial-fix-generic-chars_in_buffer.patch b/usb/usb-serial-fix-generic-chars_in_buffer.patch
new file mode 100644
index 00000000000000..caa0c41e076bb8
--- /dev/null
+++ b/usb/usb-serial-fix-generic-chars_in_buffer.patch
@@ -0,0 +1,62 @@
+From jhovold@gmail.com Fri Apr 23 15:25:24 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:00:42 +0100
+Subject: USB: serial: fix generic chars_in_buffer
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863245-17230-7-git-send-email-jhovold@gmail.com>
+
+
+Make sure chars_in_buffer accounts also for data in host stack queues.
+
+This fixes the problem with tty_wait_until_sent returning too soon at
+close which could cause the final write urb to be cancelled.
+
+Reported-by: Pete Zaitcev <zaitcev@redhat.com>
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -309,10 +309,14 @@ static int usb_serial_generic_write_star
+ /* don't have to grab the lock here, as we will
+ retry if != 0 */
+ port->write_urb_busy = 0;
+- } else
+- result = count;
++ return result;
++ }
+
+- return result;
++ spin_lock_irqsave(&port->lock, flags);
++ port->tx_bytes_flight += count;
++ spin_unlock_irqrestore(&port->lock, flags);
++
++ return count;
+ }
+
+ /**
+@@ -400,7 +404,7 @@ int usb_serial_generic_chars_in_buffer(s
+ if (serial->type->max_in_flight_urbs)
+ chars = port->tx_bytes_flight;
+ else
+- chars = kfifo_len(&port->write_fifo);
++ chars = kfifo_len(&port->write_fifo) + port->tx_bytes_flight;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ dbg("%s - returns %d", __func__, chars);
+@@ -510,7 +514,10 @@ void usb_serial_generic_write_bulk_callb
+ port->urbs_in_flight = 0;
+ spin_unlock_irqrestore(&port->lock, flags);
+ } else {
++ spin_lock_irqsave(&port->lock, flags);
++ port->tx_bytes_flight -= urb->transfer_buffer_length;
+ port->write_urb_busy = 0;
++ spin_unlock_irqrestore(&port->lock, flags);
+
+ if (status)
+ kfifo_reset_out(&port->write_fifo);
diff --git a/usb/usb-serial-fix-missing-locking-on-fifo-in-write-callback.patch b/usb/usb-serial-fix-missing-locking-on-fifo-in-write-callback.patch
new file mode 100644
index 00000000000000..c5971a002a7a2b
--- /dev/null
+++ b/usb/usb-serial-fix-missing-locking-on-fifo-in-write-callback.patch
@@ -0,0 +1,40 @@
+From jhovold@gmail.com Fri Apr 23 15:25:36 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:00:43 +0100
+Subject: USB: serial: fix missing locking on fifo in write callback
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863245-17230-8-git-send-email-jhovold@gmail.com>
+
+
+On errors the fifo was reset without any locking. This could race with
+write which do kfifo_put and perhaps also chars_in_buffer and write_room.
+
+Every other access to the fifo is protected using the port lock so
+better add it to the error path as well.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -519,10 +519,13 @@ void usb_serial_generic_write_bulk_callb
+ port->write_urb_busy = 0;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+- if (status)
++ if (status) {
++ spin_lock_irqsave(&port->lock, flags);
+ kfifo_reset_out(&port->write_fifo);
+- else
++ spin_unlock_irqrestore(&port->lock, flags);
++ } else {
+ usb_serial_generic_write_start(port);
++ }
+ }
+
+ if (status)
diff --git a/usb/usb-serial-generalise-generic-read-implementation.patch b/usb/usb-serial-generalise-generic-read-implementation.patch
new file mode 100644
index 00000000000000..f0d66ae222c448
--- /dev/null
+++ b/usb/usb-serial-generalise-generic-read-implementation.patch
@@ -0,0 +1,83 @@
+From jhovold@gmail.com Fri Apr 23 15:30:15 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:05:57 +0100
+Subject: [RFC][PATCH 05/20] USB: serial: generalise generic read implementation
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-6-git-send-email-jhovold@gmail.com>
+
+
+Add process_read_urb to usb_serial_driver so that a driver can rely on
+the generic read (and throttle) mechanism but still do device specific
+processing of incoming data (such as adding tty_flags before pushing to
+line discipline).
+
+The default generic implementation handles sysrq for consoles but
+otherwise simply pushes to tty.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 5 +++--
+ drivers/usb/serial/usb-serial.c | 1 +
+ include/linux/usb/serial.h | 3 +++
+ 3 files changed, 7 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -408,7 +408,7 @@ int usb_serial_generic_submit_read_urb(s
+ }
+ EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urb);
+
+-static void usb_serial_generic_process_read_urb(struct urb *urb)
++void usb_serial_generic_process_read_urb(struct urb *urb)
+ {
+ struct usb_serial_port *port = urb->context;
+ struct tty_struct *tty;
+@@ -433,6 +433,7 @@ static void usb_serial_generic_process_r
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+ }
++EXPORT_SYMBOL_GPL(usb_serial_generic_process_read_urb);
+
+ void usb_serial_generic_read_bulk_callback(struct urb *urb)
+ {
+@@ -451,7 +452,7 @@ void usb_serial_generic_read_bulk_callba
+
+ usb_serial_debug_data(debug, &port->dev, __func__,
+ urb->actual_length, data);
+- usb_serial_generic_process_read_urb(urb);
++ port->serial->type->process_read_urb(urb);
+
+ /* Throttle the device if requested by tty */
+ spin_lock_irqsave(&port->lock, flags);
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -1298,6 +1298,7 @@ static void fixup_generic(struct usb_ser
+ set_to_generic_if_null(device, write_bulk_callback);
+ set_to_generic_if_null(device, disconnect);
+ set_to_generic_if_null(device, release);
++ set_to_generic_if_null(device, process_read_urb);
+ }
+
+ int usb_serial_register(struct usb_serial_driver *driver)
+--- a/include/linux/usb/serial.h
++++ b/include/linux/usb/serial.h
+@@ -272,6 +272,8 @@ struct usb_serial_driver {
+ void (*write_int_callback)(struct urb *urb);
+ void (*read_bulk_callback)(struct urb *urb);
+ void (*write_bulk_callback)(struct urb *urb);
++ /* Called by the generic read bulk callback */
++ void (*process_read_urb)(struct urb *urb);
+ };
+ #define to_usb_serial_driver(d) \
+ container_of(d, struct usb_serial_driver, driver)
+@@ -323,6 +325,7 @@ extern int usb_serial_generic_register(i
+ extern void usb_serial_generic_deregister(void);
+ extern int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
+ gfp_t mem_flags);
++extern void usb_serial_generic_process_read_urb(struct urb *urb);
+ extern int usb_serial_handle_sysrq_char(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ unsigned int ch);
diff --git a/usb/usb-serial-generalise-write-buffer-preparation.patch b/usb/usb-serial-generalise-write-buffer-preparation.patch
new file mode 100644
index 00000000000000..2c1fd7c6a4ae4b
--- /dev/null
+++ b/usb/usb-serial-generalise-write-buffer-preparation.patch
@@ -0,0 +1,153 @@
+From jhovold@gmail.com Fri Apr 23 15:36:57 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:06:08 +0100
+Subject: USB: serial: generalise write buffer preparation
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-17-git-send-email-jhovold@gmail.com>
+
+
+Generalise write buffer preparation.
+
+This allows for drivers to manipulate (e.g. add headers) to bulk out
+data before it is sent.
+
+This adds a new function pointer to usb_serial_driver:
+
+int (*prepare_write_buffer)(struct usb_serial_port *port,
+ void **dest, size_t size, const void *src, size_t count);
+
+The function is generic and can be used with either kfifo-based or
+multi-urb writes:
+
+If *dest is NULL the implementation should allocate dest.
+If src is NULL the implementation should use the port write fifo.
+
+If not set, a generic implementation is used which simply uses memcpy or
+kfifo_out.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 47 ++++++++++++++++++++++++++++------------
+ drivers/usb/serial/usb-serial.c | 1
+ include/linux/usb/serial.h | 5 ++++
+ 3 files changed, 40 insertions(+), 13 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -167,12 +167,35 @@ void usb_serial_generic_close(struct usb
+ }
+ EXPORT_SYMBOL_GPL(usb_serial_generic_close);
+
++int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
++ void **dest, size_t size, const void *src, size_t count)
++{
++ if (!*dest) {
++ size = count;
++ *dest = kmalloc(count, GFP_ATOMIC);
++ if (!*dest) {
++ dev_err(&port->dev, "%s - could not allocate buffer\n",
++ __func__);
++ return -ENOMEM;
++ }
++ }
++ if (src) {
++ count = size;
++ memcpy(*dest, src, size);
++ } else {
++ count = kfifo_out_locked(&port->write_fifo, *dest, size,
++ &port->lock);
++ }
++ return count;
++}
++EXPORT_SYMBOL_GPL(usb_serial_generic_prepare_write_buffer);
++
+ static int usb_serial_multi_urb_write(struct tty_struct *tty,
+ struct usb_serial_port *port, const unsigned char *buf, int count)
+ {
+ unsigned long flags;
+ struct urb *urb;
+- unsigned char *buffer;
++ void *buffer;
+ int status;
+
+ spin_lock_irqsave(&port->lock, flags);
+@@ -191,16 +214,14 @@ static int usb_serial_multi_urb_write(st
+ goto err_urb;
+ }
+
++ buffer = NULL;
+ count = min_t(int, count, PAGE_SIZE);
+- buffer = kmalloc(count, GFP_ATOMIC);
+- if (!buffer) {
+- dev_err(&port->dev, "%s - could not allocate buffer\n",
+- __func__);
+- status = -ENOMEM;
++ count = port->serial->type->prepare_write_buffer(port, &buffer, 0,
++ buf, count);
++ if (count < 0) {
++ status = count;
+ goto err_buf;
+ }
+-
+- memcpy(buffer, buf, count);
+ usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
+ usb_fill_bulk_urb(urb, port->serial->dev,
+ usb_sndbulkpipe(port->serial->dev,
+@@ -242,7 +263,6 @@ err_urb:
+ */
+ static int usb_serial_generic_write_start(struct usb_serial_port *port)
+ {
+- unsigned char *data;
+ int result;
+ int count;
+ unsigned long flags;
+@@ -255,10 +275,11 @@ static int usb_serial_generic_write_star
+ port->write_urb_busy = 1;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+- data = port->write_urb->transfer_buffer;
+- count = kfifo_out_locked(&port->write_fifo, data, port->bulk_out_size, &port->lock);
+- usb_serial_debug_data(debug, &port->dev, __func__, count, data);
+-
++ count = port->serial->type->prepare_write_buffer(port,
++ &port->write_urb->transfer_buffer,
++ port->bulk_out_size, NULL, 0);
++ usb_serial_debug_data(debug, &port->dev, __func__,
++ count, port->write_urb->transfer_buffer);
+ port->write_urb->transfer_buffer_length = count;
+
+ /* send the data out the bulk port */
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -1299,6 +1299,7 @@ static void fixup_generic(struct usb_ser
+ set_to_generic_if_null(device, disconnect);
+ set_to_generic_if_null(device, release);
+ set_to_generic_if_null(device, process_read_urb);
++ set_to_generic_if_null(device, prepare_write_buffer);
+ }
+
+ int usb_serial_register(struct usb_serial_driver *driver)
+--- a/include/linux/usb/serial.h
++++ b/include/linux/usb/serial.h
+@@ -277,6 +277,9 @@ struct usb_serial_driver {
+ void (*write_bulk_callback)(struct urb *urb);
+ /* Called by the generic read bulk callback */
+ void (*process_read_urb)(struct urb *urb);
++ /* Called by the generic write implementation */
++ int (*prepare_write_buffer)(struct usb_serial_port *port,
++ void **dest, size_t size, const void *src, size_t count);
+ };
+ #define to_usb_serial_driver(d) \
+ container_of(d, struct usb_serial_driver, driver)
+@@ -329,6 +332,8 @@ extern void usb_serial_generic_deregiste
+ extern int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
+ gfp_t mem_flags);
+ extern void usb_serial_generic_process_read_urb(struct urb *urb);
++extern int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
++ void **dest, size_t size, const void *src, size_t count);
+ extern int usb_serial_handle_sysrq_char(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ unsigned int ch);
diff --git a/usb/usb-serial-re-implement-multi-urb-writes-in-generic-driver.patch b/usb/usb-serial-re-implement-multi-urb-writes-in-generic-driver.patch
new file mode 100644
index 00000000000000..8965f9969a16de
--- /dev/null
+++ b/usb/usb-serial-re-implement-multi-urb-writes-in-generic-driver.patch
@@ -0,0 +1,274 @@
+From jhovold@gmail.com Fri Apr 23 15:36:18 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:06:07 +0100
+Subject: USB: serial: re-implement multi-urb writes in generic driver
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-16-git-send-email-jhovold@gmail.com>
+
+
+Use dynamic transfer buffer sizes since it is more efficient to let the
+host controller do the partitioning to fit endpoint size. This way we
+also do not use more than one urb per write request.
+
+Replace max_in_flight_urbs with multi_urb_write flag in struct
+usb_serial_driver to enable multi-urb writes.
+
+Use MAX_TX_URBS=40 and a max buffer size of PAGE_SIZE to prevent DoS
+attacks.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 132 ++++++++++++++++++-------------------------
+ include/linux/usb/serial.h | 9 +-
+ 2 files changed, 62 insertions(+), 79 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -25,6 +25,8 @@
+
+ static int debug;
+
++#define MAX_TX_URBS 40
++
+ #ifdef CONFIG_USB_SERIAL_GENERIC
+
+ static int generic_probe(struct usb_interface *interface,
+@@ -172,78 +174,63 @@ static int usb_serial_multi_urb_write(st
+ struct urb *urb;
+ unsigned char *buffer;
+ int status;
+- int towrite;
+- int bwrite = 0;
+-
+- dbg("%s - port %d", __func__, port->number);
+-
+- if (count == 0)
+- dbg("%s - write request of 0 bytes", __func__);
+
+- while (count > 0) {
+- towrite = (count > port->bulk_out_size) ?
+- port->bulk_out_size : count;
+- spin_lock_irqsave(&port->lock, flags);
+- if (port->urbs_in_flight >
+- port->serial->type->max_in_flight_urbs) {
+- spin_unlock_irqrestore(&port->lock, flags);
+- dbg("%s - write limit hit", __func__);
+- return bwrite;
+- }
+- port->tx_bytes_flight += towrite;
+- port->urbs_in_flight++;
++ spin_lock_irqsave(&port->lock, flags);
++ if (port->tx_urbs == MAX_TX_URBS) {
+ spin_unlock_irqrestore(&port->lock, flags);
++ dbg("%s - write limit hit", __func__);
++ return 0;
++ }
++ port->tx_urbs++;
++ spin_unlock_irqrestore(&port->lock, flags);
+
+- buffer = kmalloc(towrite, GFP_ATOMIC);
+- if (!buffer) {
+- dev_err(&port->dev,
+- "%s ran out of kernel memory for urb ...\n", __func__);
+- goto error_no_buffer;
+- }
++ urb = usb_alloc_urb(0, GFP_ATOMIC);
++ if (!urb) {
++ dev_err(&port->dev, "%s - no free urbs available\n", __func__);
++ status = -ENOMEM;
++ goto err_urb;
++ }
+
+- urb = usb_alloc_urb(0, GFP_ATOMIC);
+- if (!urb) {
+- dev_err(&port->dev, "%s - no more free urbs\n",
++ count = min_t(int, count, PAGE_SIZE);
++ buffer = kmalloc(count, GFP_ATOMIC);
++ if (!buffer) {
++ dev_err(&port->dev, "%s - could not allocate buffer\n",
+ __func__);
+- goto error_no_urb;
+- }
++ status = -ENOMEM;
++ goto err_buf;
++ }
+
+- /* Copy data */
+- memcpy(buffer, buf + bwrite, towrite);
+- usb_serial_debug_data(debug, &port->dev, __func__,
+- towrite, buffer);
+- /* fill the buffer and send it */
+- usb_fill_bulk_urb(urb, port->serial->dev,
++ memcpy(buffer, buf, count);
++ usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
++ usb_fill_bulk_urb(urb, port->serial->dev,
+ usb_sndbulkpipe(port->serial->dev,
+ port->bulk_out_endpointAddress),
+- buffer, towrite,
++ buffer, count,
+ port->serial->type->write_bulk_callback, port);
+
+- status = usb_submit_urb(urb, GFP_ATOMIC);
+- if (status) {
+- dev_err(&port->dev, "%s - error submitting urb: %d\n",
++ status = usb_submit_urb(urb, GFP_ATOMIC);
++ if (status) {
++ dev_err(&port->dev, "%s - error submitting urb: %d\n",
+ __func__, status);
+- goto error;
+- }
+-
+- /* This urb is the responsibility of the host driver now */
+- usb_free_urb(urb);
+- dbg("%s write: %d", __func__, towrite);
+- count -= towrite;
+- bwrite += towrite;
++ goto err;
+ }
+- return bwrite;
++ spin_lock_irqsave(&port->lock, flags);
++ port->tx_bytes += urb->transfer_buffer_length;
++ spin_unlock_irqrestore(&port->lock, flags);
+
+-error:
+ usb_free_urb(urb);
+-error_no_urb:
++
++ return count;
++err:
+ kfree(buffer);
+-error_no_buffer:
++err_buf:
++ usb_free_urb(urb);
++err_urb:
+ spin_lock_irqsave(&port->lock, flags);
+- port->urbs_in_flight--;
+- port->tx_bytes_flight -= towrite;
++ port->tx_urbs--;
+ spin_unlock_irqrestore(&port->lock, flags);
+- return bwrite;
++
++ return status;
+ }
+
+ /**
+@@ -286,7 +273,7 @@ static int usb_serial_generic_write_star
+ }
+
+ spin_lock_irqsave(&port->lock, flags);
+- port->tx_bytes_flight += count;
++ port->tx_bytes += count;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return count;
+@@ -318,9 +305,8 @@ int usb_serial_generic_write(struct tty_
+ if (!count)
+ return 0;
+
+- if (serial->type->max_in_flight_urbs)
+- return usb_serial_multi_urb_write(tty, port,
+- buf, count);
++ if (serial->type->multi_urb_write)
++ return usb_serial_multi_urb_write(tty, port, buf, count);
+
+ count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
+ result = usb_serial_generic_write_start(port);
+@@ -337,7 +323,7 @@ int usb_serial_generic_write_room(struct
+ struct usb_serial_port *port = tty->driver_data;
+ struct usb_serial *serial = port->serial;
+ unsigned long flags;
+- int room = 0;
++ int room;
+
+ dbg("%s - port %d", __func__, port->number);
+
+@@ -345,14 +331,10 @@ int usb_serial_generic_write_room(struct
+ return 0;
+
+ spin_lock_irqsave(&port->lock, flags);
+- if (serial->type->max_in_flight_urbs) {
+- if (port->urbs_in_flight < serial->type->max_in_flight_urbs)
+- room = port->bulk_out_size *
+- (serial->type->max_in_flight_urbs -
+- port->urbs_in_flight);
+- } else {
++ if (serial->type->multi_urb_write)
++ room = (MAX_TX_URBS - port->tx_urbs) * PAGE_SIZE;
++ else
+ room = kfifo_avail(&port->write_fifo);
+- }
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ dbg("%s - returns %d", __func__, room);
+@@ -372,10 +354,10 @@ int usb_serial_generic_chars_in_buffer(s
+ return 0;
+
+ spin_lock_irqsave(&port->lock, flags);
+- if (serial->type->max_in_flight_urbs)
+- chars = port->tx_bytes_flight;
++ if (serial->type->multi_urb_write)
++ chars = port->tx_bytes;
+ else
+- chars = kfifo_len(&port->write_fifo) + port->tx_bytes_flight;
++ chars = kfifo_len(&port->write_fifo) + port->tx_bytes;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ dbg("%s - returns %d", __func__, chars);
+@@ -461,18 +443,16 @@ void usb_serial_generic_write_bulk_callb
+
+ dbg("%s - port %d", __func__, port->number);
+
+- if (port->serial->type->max_in_flight_urbs) {
++ if (port->serial->type->multi_urb_write) {
+ kfree(urb->transfer_buffer);
+
+ spin_lock_irqsave(&port->lock, flags);
+- --port->urbs_in_flight;
+- port->tx_bytes_flight -= urb->transfer_buffer_length;
+- if (port->urbs_in_flight < 0)
+- port->urbs_in_flight = 0;
++ port->tx_bytes -= urb->transfer_buffer_length;
++ port->tx_urbs--;
+ spin_unlock_irqrestore(&port->lock, flags);
+ } else {
+ spin_lock_irqsave(&port->lock, flags);
+- port->tx_bytes_flight -= urb->transfer_buffer_length;
++ port->tx_bytes -= urb->transfer_buffer_length;
+ port->write_urb_busy = 0;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+--- a/include/linux/usb/serial.h
++++ b/include/linux/usb/serial.h
+@@ -60,6 +60,8 @@ enum port_dev_state {
+ * @write_urb: pointer to the bulk out struct urb for this port.
+ * @write_fifo: kfifo used to buffer outgoing data
+ * @write_urb_busy: port`s writing status
++ * @tx_bytes: number of bytes currently in host stack queues
++ * @tx_urbs: number of urbs currently in host stack queues
+ * @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this
+ * port.
+ * @write_wait: a wait_queue_head_t used by the port.
+@@ -98,8 +100,8 @@ struct usb_serial_port {
+ int write_urb_busy;
+ __u8 bulk_out_endpointAddress;
+
+- int tx_bytes_flight;
+- int urbs_in_flight;
++ int tx_bytes;
++ int tx_urbs;
+
+ wait_queue_head_t write_wait;
+ struct work_struct work;
+@@ -223,7 +225,8 @@ struct usb_serial_driver {
+ struct device_driver driver;
+ struct usb_driver *usb_driver;
+ struct usb_dynids dynids;
+- int max_in_flight_urbs;
++
++ unsigned char multi_urb_write:1;
+
+ size_t bulk_in_size;
+ size_t bulk_out_size;
diff --git a/usb/usb-serial-refactor-generic-close.patch b/usb/usb-serial-refactor-generic-close.patch
new file mode 100644
index 00000000000000..27d688416847ec
--- /dev/null
+++ b/usb/usb-serial-refactor-generic-close.patch
@@ -0,0 +1,111 @@
+From jhovold@gmail.com Fri Apr 23 15:26:02 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:00:45 +0100
+Subject: USB: serial: refactor generic close
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863245-17230-10-git-send-email-jhovold@gmail.com>
+
+
+Export usb_serial_generic_close so that drivers can easily kill the read
+and write urb and make sure that the write fifo is reset.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ark3116.c | 7 ++-----
+ drivers/usb/serial/belkin_sa.c | 4 +---
+ drivers/usb/serial/ch341.c | 5 +----
+ drivers/usb/serial/cp210x.c | 5 +----
+ drivers/usb/serial/generic.c | 1 +
+ drivers/usb/serial/mct_u232.c | 7 ++-----
+ 6 files changed, 8 insertions(+), 21 deletions(-)
+
+--- a/drivers/usb/serial/ark3116.c
++++ b/drivers/usb/serial/ark3116.c
+@@ -355,14 +355,11 @@ static void ark3116_close(struct usb_ser
+ /* deactivate interrupts */
+ ark3116_write_reg(serial, UART_IER, 0);
+
+- /* shutdown any bulk reads that might be going on */
+- if (serial->num_bulk_out)
+- usb_kill_urb(port->write_urb);
+- if (serial->num_bulk_in)
+- usb_kill_urb(port->read_urb);
++ usb_serial_generic_close(port);
+ if (serial->num_interrupt_in)
+ usb_kill_urb(port->interrupt_in_urb);
+ }
++
+ }
+
+ static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port)
+--- a/drivers/usb/serial/belkin_sa.c
++++ b/drivers/usb/serial/belkin_sa.c
+@@ -246,9 +246,7 @@ static void belkin_sa_close(struct usb_s
+ {
+ dbg("%s port %d", __func__, port->number);
+
+- /* shutdown our bulk reads and writes */
+- usb_kill_urb(port->write_urb);
+- usb_kill_urb(port->read_urb);
++ usb_serial_generic_close(port);
+ usb_kill_urb(port->interrupt_in_urb);
+ } /* belkin_sa_close */
+
+--- a/drivers/usb/serial/ch341.c
++++ b/drivers/usb/serial/ch341.c
+@@ -305,10 +305,7 @@ static void ch341_close(struct usb_seria
+ {
+ dbg("%s - port %d", __func__, port->number);
+
+- /* shutdown our urbs */
+- dbg("%s - shutting down urbs", __func__);
+- usb_kill_urb(port->write_urb);
+- usb_kill_urb(port->read_urb);
++ usb_serial_generic_close(port);
+ usb_kill_urb(port->interrupt_in_urb);
+ }
+
+--- a/drivers/usb/serial/cp210x.c
++++ b/drivers/usb/serial/cp210x.c
+@@ -422,10 +422,7 @@ static void cp210x_close(struct usb_seri
+ {
+ dbg("%s - port %d", __func__, port->number);
+
+- /* shutdown our urbs */
+- dbg("%s - shutting down urbs", __func__);
+- usb_kill_urb(port->write_urb);
+- usb_kill_urb(port->read_urb);
++ usb_serial_generic_close(port);
+
+ mutex_lock(&port->serial->disc_mutex);
+ if (!port->serial->disconnected)
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -179,6 +179,7 @@ void usb_serial_generic_close(struct usb
+ dbg("%s - port %d", __func__, port->number);
+ generic_cleanup(port);
+ }
++EXPORT_SYMBOL_GPL(usb_serial_generic_close);
+
+ static int usb_serial_multi_urb_write(struct tty_struct *tty,
+ struct usb_serial_port *port, const unsigned char *buf, int count)
+--- a/drivers/usb/serial/mct_u232.c
++++ b/drivers/usb/serial/mct_u232.c
+@@ -549,12 +549,9 @@ static void mct_u232_close(struct usb_se
+ {
+ dbg("%s port %d", __func__, port->number);
+
+- if (port->serial->dev) {
+- /* shutdown our urbs */
+- usb_kill_urb(port->write_urb);
+- usb_kill_urb(port->read_urb);
++ usb_serial_generic_close(port);
++ if (port->serial->dev)
+ usb_kill_urb(port->interrupt_in_urb);
+- }
+ } /* mct_u232_close */
+
+
diff --git a/usb/usb-serial-refactor-read-urb-submission-in-generic-driver.patch b/usb/usb-serial-refactor-read-urb-submission-in-generic-driver.patch
new file mode 100644
index 00000000000000..a6e729372b58f5
--- /dev/null
+++ b/usb/usb-serial-refactor-read-urb-submission-in-generic-driver.patch
@@ -0,0 +1,118 @@
+From jhovold@gmail.com Fri Apr 23 15:27:15 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:05:53 +0100
+Subject: USB: serial: refactor read urb submission in generic driver
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-2-git-send-email-jhovold@gmail.com>
+
+
+Use the already exported function for submitting the read urb associated
+with a usb_serial_port.
+
+Make sure it returns the result of usb_submit_urb and rename to the
+more descriptive usb_serial_generic_submit_read_urb.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 32 ++++++++------------------------
+ drivers/usb/serial/usb_debug.c | 2 +-
+ include/linux/usb/serial.h | 2 +-
+ 3 files changed, 10 insertions(+), 26 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -118,7 +118,6 @@ void usb_serial_generic_deregister(void)
+
+ int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port)
+ {
+- struct usb_serial *serial = port->serial;
+ int result = 0;
+ unsigned long flags;
+
+@@ -131,23 +130,8 @@ int usb_serial_generic_open(struct tty_s
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ /* if we have a bulk endpoint, start reading from it */
+- if (port->bulk_in_size) {
+- /* Start reading from the device */
+- usb_fill_bulk_urb(port->read_urb, serial->dev,
+- usb_rcvbulkpipe(serial->dev,
+- port->bulk_in_endpointAddress),
+- port->read_urb->transfer_buffer,
+- port->read_urb->transfer_buffer_length,
+- ((serial->type->read_bulk_callback) ?
+- serial->type->read_bulk_callback :
+- usb_serial_generic_read_bulk_callback),
+- port);
+- result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+- if (result)
+- dev_err(&port->dev,
+- "%s - failed resubmitting read urb, error %d\n",
+- __func__, result);
+- }
++ if (port->bulk_in_size)
++ result = usb_serial_generic_submit_read_urb(port, GFP_KERNEL);
+
+ return result;
+ }
+@@ -418,9 +402,8 @@ int usb_serial_generic_chars_in_buffer(s
+ return chars;
+ }
+
+-
+-void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port,
+- gfp_t mem_flags)
++int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
++ gfp_t mem_flags)
+ {
+ struct urb *urb = port->read_urb;
+ struct usb_serial *serial = port->serial;
+@@ -439,11 +422,12 @@ void usb_serial_generic_resubmit_read_ur
+ result = usb_submit_urb(urb, mem_flags);
+ if (result && result != -EPERM) {
+ dev_err(&port->dev,
+- "%s - failed resubmitting read urb, error %d\n",
++ "%s - failed submitting read urb, error %d\n",
+ __func__, result);
+ }
++ return result;
+ }
+-EXPORT_SYMBOL_GPL(usb_serial_generic_resubmit_read_urb);
++EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urb);
+
+ /* Push data to tty layer and resubmit the bulk read URB */
+ static void flush_and_resubmit_read_urb(struct usb_serial_port *port)
+@@ -471,7 +455,7 @@ static void flush_and_resubmit_read_urb(
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+ done:
+- usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC);
++ usb_serial_generic_submit_read_urb(port, GFP_ATOMIC);
+ }
+
+ void usb_serial_generic_read_bulk_callback(struct urb *urb)
+--- a/drivers/usb/serial/usb_debug.c
++++ b/drivers/usb/serial/usb_debug.c
+@@ -69,7 +69,7 @@ static void usb_debug_read_bulk_callback
+ memcmp(urb->transfer_buffer, USB_DEBUG_BRK,
+ USB_DEBUG_BRK_SIZE) == 0) {
+ usb_serial_handle_break(port);
+- usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC);
++ usb_serial_generic_submit_read_urb(port, GFP_ATOMIC);
+ return;
+ }
+
+--- a/include/linux/usb/serial.h
++++ b/include/linux/usb/serial.h
+@@ -321,7 +321,7 @@ extern void usb_serial_generic_disconnec
+ extern void usb_serial_generic_release(struct usb_serial *serial);
+ extern int usb_serial_generic_register(int debug);
+ extern void usb_serial_generic_deregister(void);
+-extern void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port,
++extern int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
+ gfp_t mem_flags);
+ extern int usb_serial_handle_sysrq_char(struct tty_struct *tty,
+ struct usb_serial_port *port,
diff --git a/usb/usb-serial-remove-unnecessary-re-initialisation-of-generic-urbs.patch b/usb/usb-serial-remove-unnecessary-re-initialisation-of-generic-urbs.patch
new file mode 100644
index 00000000000000..099fc45d9c2bc0
--- /dev/null
+++ b/usb/usb-serial-remove-unnecessary-re-initialisation-of-generic-urbs.patch
@@ -0,0 +1,70 @@
+From jhovold@gmail.com Fri Apr 23 15:28:39 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:05:54 +0100
+Subject: USB: serial: remove unnecessary re-initialisation of generic urbs
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1268863572-17891-3-git-send-email-jhovold@gmail.com>
+
+
+The generic read and write bulk urbs are initialised when allocated in
+usb_serial_probe. The only field that needs to be updated after that is
+the transfer_buffer_length of the write urb.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/generic.c | 25 ++-----------------------
+ 1 file changed, 2 insertions(+), 23 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -256,7 +256,6 @@ error_no_buffer:
+ */
+ static int usb_serial_generic_write_start(struct usb_serial_port *port)
+ {
+- struct usb_serial *serial = port->serial;
+ unsigned char *data;
+ int result;
+ int count;
+@@ -281,15 +280,7 @@ static int usb_serial_generic_write_star
+ count = kfifo_out_locked(&port->write_fifo, data, port->bulk_out_size, &port->lock);
+ usb_serial_debug_data(debug, &port->dev, __func__, count, data);
+
+- /* set up our urb */
+- usb_fill_bulk_urb(port->write_urb, serial->dev,
+- usb_sndbulkpipe(serial->dev,
+- port->bulk_out_endpointAddress),
+- port->write_urb->transfer_buffer, count,
+- ((serial->type->write_bulk_callback) ?
+- serial->type->write_bulk_callback :
+- usb_serial_generic_write_bulk_callback),
+- port);
++ port->write_urb->transfer_buffer_length = count;
+
+ /* send the data out the bulk port */
+ result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+@@ -405,21 +396,9 @@ int usb_serial_generic_chars_in_buffer(s
+ int usb_serial_generic_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;
+
+- /* Continue reading from device */
+- usb_fill_bulk_urb(urb, serial->dev,
+- usb_rcvbulkpipe(serial->dev,
+- port->bulk_in_endpointAddress),
+- urb->transfer_buffer,
+- urb->transfer_buffer_length,
+- ((serial->type->read_bulk_callback) ?
+- serial->type->read_bulk_callback :
+- usb_serial_generic_read_bulk_callback), port);
+-
+- result = usb_submit_urb(urb, mem_flags);
++ result = usb_submit_urb(port->read_urb, mem_flags);
+ if (result && result != -EPERM) {
+ dev_err(&port->dev,
+ "%s - failed submitting read urb, error %d\n",
diff --git a/usb/usb-usb_debug-use-the-generic-kfifo-based-write-implementation.patch b/usb/usb-usb_debug-use-the-generic-kfifo-based-write-implementation.patch
new file mode 100644
index 00000000000000..1882927efe5661
--- /dev/null
+++ b/usb/usb-usb_debug-use-the-generic-kfifo-based-write-implementation.patch
@@ -0,0 +1,38 @@
+From jhovold@gmail.com Fri Apr 23 15:35:03 2010
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 17 Mar 2010 23:06:05 +0100
+Subject: USB: usb_debug: use the generic kfifo-based write implementation
+To: linux-usb@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Johan Hovold <jhovold@gmail.com>, Jason Wessel <jason.wessel@windriver.com>
+Message-ID: <1268863572-17891-14-git-send-email-jhovold@gmail.com>
+
+
+Use the generic kfifo-based write implementation rather than allowing up
+to 4000 8 byte urbs in the host stack queues.
+
+Cc: Jason Wessel <jason.wessel@windriver.com>
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/usb_debug.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/usb/serial/usb_debug.c
++++ b/drivers/usb/serial/usb_debug.c
+@@ -16,7 +16,6 @@
+ #include <linux/usb.h>
+ #include <linux/usb/serial.h>
+
+-#define URB_DEBUG_MAX_IN_FLIGHT_URBS 4000
+ #define USB_DEBUG_MAX_PACKET_SIZE 8
+ #define USB_DEBUG_BRK_SIZE 8
+ static char USB_DEBUG_BRK[USB_DEBUG_BRK_SIZE] = {
+@@ -84,7 +83,6 @@ static struct usb_serial_driver debug_de
+ .id_table = id_table,
+ .num_ports = 1,
+ .open = usb_debug_open,
+- .max_in_flight_urbs = URB_DEBUG_MAX_IN_FLIGHT_URBS,
+ .break_ctl = usb_debug_break_ctl,
+ .read_bulk_callback = usb_debug_read_bulk_callback,
+ };