diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2010-04-23 15:48:46 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-04-23 15:48:46 -0700 |
| commit | e059f9c1f697e4f2b69d0144b8dea8c938a97f14 (patch) | |
| tree | c3ee13bb8e31bec78607a2218d06d50fbb40532d | |
| parent | d2a2fb8ae847442b92ce831d7e69ce809f84f598 (diff) | |
| download | patches-e059f9c1f697e4f2b69d0144b8dea8c938a97f14.tar.gz | |
usb patches
28 files changed, 4198 insertions, 0 deletions
@@ -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, + }; |
