aboutsummaryrefslogtreecommitdiffstats
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2008-05-19 17:21:54 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2008-05-19 17:21:54 -0700
commitbd7cf11c0decee3435a5066d2ef5eeee71c796a9 (patch)
tree802a0325f98671ff66e6c37c48006de1411b11a9
parent7a8f932c31a55ff5f10be40fd5e27d700b911280 (diff)
downloadpatches-bd7cf11c0decee3435a5066d2ef5eeee71c796a9.tar.gz
2.6.26-rc3 resync and more patches added
-rw-r--r--driver-core/debugfs-add-a-reference-to-the-debugfs-api-documentation.patch30
-rw-r--r--driver-core/driver-core-remove-kobj_name_len-define.patch65
-rw-r--r--driver-core/uio-convert-uio-to-fasync_unlocked.patch33
-rw-r--r--ldp/framebuffer-add-the-via-framebuffer-driver.patch2
-rw-r--r--series10
-rw-r--r--usb.current/usb-serial-ch341-new-vid-pid-for-ch341-usb-serial.patch33
-rw-r--r--usb/usb-gotemp.patch70
-rw-r--r--usb/usb-remove-cvs-keywords.patch417
-rw-r--r--usb/usb-remove-documentation-usb-uhci.txt.patch188
-rw-r--r--usb/usb-rndis-switch-to-seq_files.patch122
-rw-r--r--usb/usb-serial-gadget-modular-tty-glue.patch1377
-rw-r--r--usb/usb-serial-gadget-use-new-tty-glue.patch1922
-rw-r--r--version2
13 files changed, 4187 insertions, 84 deletions
diff --git a/driver-core/debugfs-add-a-reference-to-the-debugfs-api-documentation.patch b/driver-core/debugfs-add-a-reference-to-the-debugfs-api-documentation.patch
new file mode 100644
index 00000000000000..2b64a67e8b888d
--- /dev/null
+++ b/driver-core/debugfs-add-a-reference-to-the-debugfs-api-documentation.patch
@@ -0,0 +1,30 @@
+From jesper.juhl@gmail.com Mon May 19 16:58:21 2008
+From: Robert P. J. Day <rpjday@crashcourse.ca>
+Date: Tue, 20 May 2008 00:06:00 +0200
+Subject: debugfs: Add a reference to the debugfs API documentation.
+To: "Greg KH" <greg@kroah.com>
+Cc: "Greg Kroah-Hartman" <gregkh@suse.de>, trivial@kernel.org, "Robert P. J. Day" <rpjday@crashcourse.ca>
+Message-ID: <9a8748490805191506p752e1c9ay7c6d6d37d9dfb25b@mail.gmail.com>
+Content-Disposition: inline
+
+
+Signed-off-by: Robert P. J. Day <rpjday@crashcourse.ca>
+Cc: Jesper Juhl <jesper.juhl@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ lib/Kconfig.debug | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/lib/Kconfig.debug
++++ b/lib/Kconfig.debug
+@@ -74,6 +74,9 @@ config DEBUG_FS
+ debugging files into. Enable this option to be able to read and
+ write to these files.
+
++ For detailed documentation on the debugfs API, see
++ Documentation/DocBook/filesystems.
++
+ If unsure, say N.
+
+ config HEADERS_CHECK
diff --git a/driver-core/driver-core-remove-kobj_name_len-define.patch b/driver-core/driver-core-remove-kobj_name_len-define.patch
index ff5e40b932a438..da99bd46dd0757 100644
--- a/driver-core/driver-core-remove-kobj_name_len-define.patch
+++ b/driver-core/driver-core-remove-kobj_name_len-define.patch
@@ -12,24 +12,22 @@ Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
- arch/arm/plat-omap/mailbox.c | 2 +-
- arch/powerpc/platforms/pasemi/misc.c | 2 +-
- arch/sparc64/kernel/vio.c | 2 +-
- drivers/message/fusion/mptbase.h | 2 +-
- drivers/message/fusion/mptfc.c | 4 ++--
- drivers/pci/hotplug/acpiphp.h | 4 ++--
- drivers/scsi/hosts.c | 4 ++--
- drivers/scsi/scsi_transport_fc.c | 9 +++++----
- drivers/scsi/scsi_transport_iscsi.c | 4 ++--
- fs/partitions/check.c | 2 +-
- include/linux/device.h | 3 +--
- include/linux/i2c.h | 4 ++--
- include/linux/kobject.h | 1 -
- include/linux/spi/spi.h | 2 +-
- include/scsi/scsi_host.h | 2 +-
- include/scsi/scsi_transport_fc.h | 4 ++--
- include/scsi/scsi_transport_iscsi.h | 2 +-
- 17 files changed, 26 insertions(+), 27 deletions(-)
+ arch/arm/plat-omap/mailbox.c | 2 +-
+ arch/sparc64/kernel/vio.c | 2 +-
+ drivers/message/fusion/mptbase.h | 2 +-
+ drivers/message/fusion/mptfc.c | 4 ++--
+ drivers/pci/hotplug/acpiphp.h | 4 ++--
+ drivers/scsi/hosts.c | 4 ++--
+ drivers/scsi/scsi_transport_fc.c | 9 +++++----
+ drivers/scsi/scsi_transport_iscsi.c | 4 ++--
+ fs/partitions/check.c | 2 +-
+ include/linux/device.h | 3 +--
+ include/linux/kobject.h | 1 -
+ include/linux/spi/spi.h | 2 +-
+ include/scsi/scsi_host.h | 2 +-
+ include/scsi/scsi_transport_fc.h | 4 ++--
+ include/scsi/scsi_transport_iscsi.h | 2 +-
+ 15 files changed, 23 insertions(+), 24 deletions(-)
--- a/arch/arm/plat-omap/mailbox.c
+++ b/arch/arm/plat-omap/mailbox.c
@@ -42,17 +40,6 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
dev_set_drvdata(&mbox->dev, mbox);
ret = device_register(&mbox->dev);
---- a/arch/powerpc/platforms/pasemi/misc.c
-+++ b/arch/powerpc/platforms/pasemi/misc.c
-@@ -41,7 +41,7 @@ static int __init find_i2c_driver(struct
- if (!of_device_is_compatible(node, i2c_devices[i].of_device))
- continue;
- if (strlcpy(info->driver_name, i2c_devices[i].i2c_driver,
-- KOBJ_NAME_LEN) >= KOBJ_NAME_LEN ||
-+ sizeof(info->driver_name)) >= sizeof(info->driver_name) ||
- strlcpy(info->type, i2c_devices[i].i2c_type,
- I2C_NAME_SIZE) >= I2C_NAME_SIZE)
- return -ENOMEM;
--- a/arch/sparc64/kernel/vio.c
+++ b/arch/sparc64/kernel/vio.c
@@ -224,7 +224,7 @@ static struct vio_dev *vio_create_one(st
@@ -180,26 +167,6 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
struct device;
struct device_driver;
---- a/include/linux/i2c.h
-+++ b/include/linux/i2c.h
-@@ -174,7 +174,7 @@ struct i2c_client {
- struct i2c_driver *driver; /* and our access routines */
- struct device dev; /* the device structure */
- int irq; /* irq issued by device (or -1) */
-- char driver_name[KOBJ_NAME_LEN];
-+ char driver_name[20];
- struct list_head list; /* DEPRECATED */
- struct completion released;
- };
-@@ -220,7 +220,7 @@ static inline void i2c_set_clientdata (s
- * with the adapter already known.
- */
- struct i2c_board_info {
-- char driver_name[KOBJ_NAME_LEN];
-+ char driver_name[20];
- char type[I2C_NAME_SIZE];
- unsigned short flags;
- unsigned short addr;
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -26,7 +26,6 @@
diff --git a/driver-core/uio-convert-uio-to-fasync_unlocked.patch b/driver-core/uio-convert-uio-to-fasync_unlocked.patch
new file mode 100644
index 00000000000000..4f56623682ba99
--- /dev/null
+++ b/driver-core/uio-convert-uio-to-fasync_unlocked.patch
@@ -0,0 +1,33 @@
+From hjk@linutronix.de Mon May 19 17:10:22 2008
+From: "Hans J. Koch" <hjk@linutronix.de>
+Date: Mon, 19 May 2008 15:30:24 +0200
+Subject: UIO: Convert uio to fasync_unlocked
+To: Andi Kleen <andi@firstfloor.org>
+Cc: hjk@linutronix.de, corbet@lwn.net, linux-kernel@vger.kernel.org, gregkh@suse.de
+Message-ID: <20080519133023.GB3188@local>
+Content-Disposition: inline
+
+
+Convert uio to fasync_unlocked
+
+Just calls fasync_helper
+
+Signed-off-by: Andi Kleen <ak@linux.intel.com>
+Acked-by: Hans J. Koch <hjk@linutronix.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/uio/uio.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/uio/uio.c
++++ b/drivers/uio/uio.c
+@@ -541,7 +541,7 @@ static const struct file_operations uio_
+ .read = uio_read,
+ .mmap = uio_mmap,
+ .poll = uio_poll,
+- .fasync = uio_fasync,
++ .unlocked_fasync = uio_fasync,
+ };
+
+ static int uio_major_init(void)
diff --git a/ldp/framebuffer-add-the-via-framebuffer-driver.patch b/ldp/framebuffer-add-the-via-framebuffer-driver.patch
index c154d0ec01820c..96c25406aa435a 100644
--- a/ldp/framebuffer-add-the-via-framebuffer-driver.patch
+++ b/ldp/framebuffer-add-the-via-framebuffer-driver.patch
@@ -55,7 +55,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
-@@ -1952,6 +1952,12 @@ config FB_AM200EPD
+@@ -1950,6 +1950,12 @@ config FB_AM200EPD
This enables support for the Metronome display controller used on
the E-Ink AM-200 EPD devkit.
diff --git a/series b/series
index d2b18e069515ef..ed43eab105b9ce 100644
--- a/series
+++ b/series
@@ -36,6 +36,7 @@ usb.current/usb-add-telit-hdspa-modem-to-option-driver.patch
usb.current/usb-option-fix-name-of-onda-msa501hs-hsdpa-modem.patch
usb.current/usb-pxa27x_udc-fix-oops.patch
usb.current/usb-build-fix.patch
+usb.current/usb-serial-ch341-new-vid-pid-for-ch341-usb-serial.patch
usb.current/usb-cdc-wdm-driver.patch
@@ -48,7 +49,8 @@ usb.current/usb-cdc-wdm-driver.patch
#################################
driver-core/sysfs-add-sys-dev-char-block-to-lookup-sysfs-path-by-major-minor.patch
driver-core/kobject-replace-with-in-name.patch
-
+driver-core/debugfs-add-a-reference-to-the-debugfs-api-documentation.patch
+driver-core/uio-convert-uio-to-fasync_unlocked.patch
driver-core/net-convert-the-phy_device-file-to-use-bus_find_device_by_name.patch
@@ -90,6 +92,11 @@ usb/usb-storage-change-remaining-semaphore-to-completion.patch
usb/usb-isp1760-hcd.c-make-2-functions-static.patch
usb/usb-implement-soft-unbinding.patch
usb/usb-storage-implement-soft-unbinding.patch
+usb/usb-remove-cvs-keywords.patch
+usb/usb-remove-documentation-usb-uhci.txt.patch
+usb/usb-serial-gadget-modular-tty-glue.patch
+usb/usb-serial-gadget-use-new-tty-glue.patch
+usb/usb-rndis-switch-to-seq_files.patch
# wireless usb, still a work in progress
usb/bitmap-add-bitmap_copy_le.patch
@@ -172,3 +179,4 @@ ldp/oms-add-oms-maxp-driver.patch
foo.patch
driver-core-remove-device_create.patch
+
diff --git a/usb.current/usb-serial-ch341-new-vid-pid-for-ch341-usb-serial.patch b/usb.current/usb-serial-ch341-new-vid-pid-for-ch341-usb-serial.patch
new file mode 100644
index 00000000000000..c79597da97cba8
--- /dev/null
+++ b/usb.current/usb-serial-ch341-new-vid-pid-for-ch341-usb-serial.patch
@@ -0,0 +1,33 @@
+From mrobbins@MIT.EDU Mon May 19 17:07:10 2008
+From: "Michael F. Robbins" <mrobbins@MIT.EDU>
+Date: Fri, 16 May 2008 23:48:42 -0400
+Subject: USB: serial: ch341: New VID/PID for CH341 USB-serial
+To: gregkh@suse.de
+Cc: hevans@MIT.EDU, frank@kingswood-consulting.co.uk
+Message-ID: <1210996122.3530.13.camel@maximum-entropy.mit.edu>
+
+
+Recent USB-serial devices using the WinChipHead CH340/CH341 chipset are
+being shipped with a new vendor/product ID code pair, but an otherwise
+identical device. (This is confirmed by looking at INF for the included
+Windows driver.)
+
+Patch is tested and working, both with new and old devices.
+
+Signed-off-by: Michael F. Robbins <mrobbins@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ch341.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/serial/ch341.c
++++ b/drivers/usb/serial/ch341.c
+@@ -28,6 +28,7 @@ static int debug;
+
+ static struct usb_device_id id_table [] = {
+ { USB_DEVICE(0x4348, 0x5523) },
++ { USB_DEVICE(0x1a86, 0x7523) },
+ { },
+ };
+ MODULE_DEVICE_TABLE(usb, id_table);
diff --git a/usb/usb-gotemp.patch b/usb/usb-gotemp.patch
index c8576868f0a04b..1408827d285b61 100644
--- a/usb/usb-gotemp.patch
+++ b/usb/usb-gotemp.patch
@@ -15,37 +15,12 @@ This is only a teaching tool.
---
drivers/usb/misc/Kconfig | 9 +
drivers/usb/misc/Makefile | 1
- drivers/usb/misc/gotemp.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 305 insertions(+)
+ drivers/usb/misc/gotemp.c | 301 ++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 311 insertions(+)
---- a/drivers/usb/misc/Kconfig
-+++ b/drivers/usb/misc/Kconfig
-@@ -269,3 +269,12 @@ config USB_TEST
- See <http://www.linux-usb.org/usbtest/> for more information,
- including sample test device firmware and "how to use it".
-
-+config USB_GOTEMP
-+ tristate "GoTemp USB thermometer driver support"
-+ depends on USB
-+ help
-+ Say Y here if you want to connect a GoTemp USB thermometer
-+ device to your computer's USB port.
-+
-+ To compile this driver as a module, choose M here: the
-+ module will be called gotemp.
---- a/drivers/usb/misc/Makefile
-+++ b/drivers/usb/misc/Makefile
-@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_CYTHERM) += cytherm.o
- obj-$(CONFIG_USB_EMI26) += emi26.o
- obj-$(CONFIG_USB_EMI62) += emi62.o
- obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
-+obj-$(CONFIG_USB_GOTEMP) += gotemp.o
- obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
- obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
- obj-$(CONFIG_USB_LCD) += usblcd.o
--- /dev/null
+++ b/drivers/usb/misc/gotemp.c
-@@ -0,0 +1,295 @@
+@@ -0,0 +1,301 @@
+/*
+ * USB GoTemp driver
+ *
@@ -121,7 +96,7 @@ This is only a teaching tool.
+ struct output_packet *pkt;
+ int retval;
+
-+ pkt = kzalloc(sizeof(*pkt), GFP_ATOMIC);
++ pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
+ if (!pkt)
+ return -ENOMEM;
+ pkt->cmd = cmd;
@@ -275,12 +250,17 @@ This is only a teaching tool.
+
+ usb_set_intfdata(interface, gdev);
+
++ init_dev(gdev);
++
++ /*
++ * this must come last - after this call the device is active
++ * if we delayed any initialization until after this, the user
++ * would read garbage
++ */
+ retval = device_create_file(&interface->dev, &dev_attr_temp);
+ if (retval)
+ goto error;
+
-+ init_dev(gdev);
-+
+ dev_info(&interface->dev, "USB GoTemp device now attached\n");
+ return 0;
+
@@ -299,9 +279,10 @@ This is only a teaching tool.
+ struct gotemp *gdev;
+
+ gdev = usb_get_intfdata(interface);
-+ usb_set_intfdata(interface, NULL);
+
+ device_remove_file(&interface->dev, &dev_attr_temp);
++ /* intfdata must remain valid while reads are under way */
++ usb_set_intfdata(interface, NULL);
+
+ usb_put_dev(gdev->udev);
+
@@ -341,3 +322,28 @@ This is only a teaching tool.
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+--- a/drivers/usb/misc/Kconfig
++++ b/drivers/usb/misc/Kconfig
+@@ -269,3 +269,12 @@ config USB_TEST
+ See <http://www.linux-usb.org/usbtest/> for more information,
+ including sample test device firmware and "how to use it".
+
++config USB_GOTEMP
++ tristate "GoTemp USB thermometer driver support"
++ depends on USB
++ help
++ Say Y here if you want to connect a GoTemp USB thermometer
++ device to your computer's USB port.
++
++ To compile this driver as a module, choose M here: the
++ module will be called gotemp.
+--- a/drivers/usb/misc/Makefile
++++ b/drivers/usb/misc/Makefile
+@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_CYTHERM) += cytherm.o
+ obj-$(CONFIG_USB_EMI26) += emi26.o
+ obj-$(CONFIG_USB_EMI62) += emi62.o
+ obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
++obj-$(CONFIG_USB_GOTEMP) += gotemp.o
+ obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
+ obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
+ obj-$(CONFIG_USB_LCD) += usblcd.o
diff --git a/usb/usb-remove-cvs-keywords.patch b/usb/usb-remove-cvs-keywords.patch
new file mode 100644
index 00000000000000..de51e25a7f580a
--- /dev/null
+++ b/usb/usb-remove-cvs-keywords.patch
@@ -0,0 +1,417 @@
+From bunk@kernel.org Mon May 19 17:04:03 2008
+From: Adrian Bunk <bunk@kernel.org>
+Date: Tue, 20 May 2008 01:00:46 +0300
+Subject: USB: remove CVS keywords
+To: greg@kroah.com
+Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
+Message-ID: <20080519220046.GY17716@cs181133002.pp.htv.fi>
+Content-Disposition: inline
+
+
+This patch removes CVS keywords that weren't updated for a long time
+from comments.
+
+Signed-off-by: Adrian Bunk <bunk@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/devices.c | 2 --
+ drivers/usb/core/devio.c | 2 --
+ drivers/usb/gadget/rndis.c | 2 --
+ drivers/usb/gadget/rndis.h | 2 --
+ drivers/usb/misc/emi62.c | 2 --
+ drivers/usb/serial/digi_acceleport.c | 2 --
+ drivers/usb/serial/keyspan_pda.S | 2 +-
+ drivers/usb/serial/xircom_pgs.S | 2 +-
+ drivers/usb/storage/datafab.c | 2 --
+ drivers/usb/storage/debug.c | 2 --
+ drivers/usb/storage/debug.h | 2 --
+ drivers/usb/storage/dpcm.c | 2 --
+ drivers/usb/storage/dpcm.h | 2 --
+ drivers/usb/storage/freecom.c | 2 --
+ drivers/usb/storage/freecom.h | 2 --
+ drivers/usb/storage/initializers.c | 2 --
+ drivers/usb/storage/initializers.h | 2 --
+ drivers/usb/storage/isd200.c | 2 --
+ drivers/usb/storage/jumpshot.c | 2 --
+ drivers/usb/storage/protocol.c | 2 --
+ drivers/usb/storage/protocol.h | 2 --
+ drivers/usb/storage/scsiglue.c | 2 --
+ drivers/usb/storage/scsiglue.h | 2 --
+ drivers/usb/storage/sddr09.c | 1 -
+ drivers/usb/storage/sddr09.h | 2 --
+ drivers/usb/storage/sddr55.c | 2 --
+ drivers/usb/storage/sddr55.h | 2 --
+ drivers/usb/storage/shuttle_usbat.c | 2 --
+ drivers/usb/storage/shuttle_usbat.h | 2 --
+ drivers/usb/storage/transport.c | 2 --
+ drivers/usb/storage/transport.h | 2 --
+ drivers/usb/storage/unusual_devs.h | 2 --
+ drivers/usb/storage/usb.c | 2 --
+ drivers/usb/storage/usb.h | 2 --
+ include/linux/usbdevice_fs.h | 2 --
+ 35 files changed, 2 insertions(+), 67 deletions(-)
+
+--- a/drivers/usb/core/devices.c
++++ b/drivers/usb/core/devices.c
+@@ -46,8 +46,6 @@
+ * 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk>
+ * Converted file reading routine to dump to buffer once
+ * per device, not per bus
+- *
+- * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $
+ */
+
+ #include <linux/fs.h>
+--- a/drivers/usb/core/devio.c
++++ b/drivers/usb/core/devio.c
+@@ -19,8 +19,6 @@
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+- * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $
+- *
+ * This file implements the usbfs/x/y files, where
+ * x is the bus number and y the device number.
+ *
+--- a/drivers/usb/gadget/rndis.c
++++ b/drivers/usb/gadget/rndis.c
+@@ -1,8 +1,6 @@
+ /*
+ * RNDIS MSG parser
+ *
+- * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $
+- *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, Pengutronix
+ *
+--- a/drivers/usb/gadget/rndis.h
++++ b/drivers/usb/gadget/rndis.h
+@@ -1,8 +1,6 @@
+ /*
+ * RNDIS Definitions for Remote NDIS
+ *
+- * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $
+- *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, Pengutronix
+ *
+--- a/drivers/usb/misc/emi62.c
++++ b/drivers/usb/misc/emi62.c
+@@ -6,8 +6,6 @@
+ * 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, version 2.
+- *
+- * $Id: emi62.c,v 1.15 2002/04/23 06:13:59 tapio Exp $
+ */
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+--- a/drivers/usb/serial/digi_acceleport.c
++++ b/drivers/usb/serial/digi_acceleport.c
+@@ -229,8 +229,6 @@
+ * in case a wake up is lost.
+ * - Following Documentation/DocBook/kernel-locking.pdf no spin locks
+ * are held when calling copy_to/from_user or printk.
+-*
+-* $Id: digi_acceleport.c,v 1.80.1.2 2000/11/02 05:45:08 root Exp $
+ */
+
+ #include <linux/kernel.h>
+--- a/drivers/usb/serial/keyspan_pda.S
++++ b/drivers/usb/serial/keyspan_pda.S
+@@ -1,4 +1,4 @@
+-/* $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
++/*
+ *
+ * Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
+ * the EzUSB microcontroller.
+--- a/drivers/usb/serial/xircom_pgs.S
++++ b/drivers/usb/serial/xircom_pgs.S
+@@ -1,4 +1,4 @@
+-/* $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
++/*
+ *
+ * Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
+ * the EzUSB microcontroller.
+--- a/drivers/usb/storage/datafab.c
++++ b/drivers/usb/storage/datafab.c
+@@ -1,7 +1,5 @@
+ /* Driver for Datafab USB Compact Flash reader
+ *
+- * $Id: datafab.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $
+- *
+ * datafab driver v0.1:
+ *
+ * First release
+--- a/drivers/usb/storage/debug.c
++++ b/drivers/usb/storage/debug.c
+@@ -1,8 +1,6 @@
+ /* Driver for USB Mass Storage compliant devices
+ * Debugging Functions Source Code File
+ *
+- * $Id: debug.c,v 1.9 2002/04/22 03:39:43 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/debug.h
++++ b/drivers/usb/storage/debug.h
+@@ -1,8 +1,6 @@
+ /* Driver for USB Mass Storage compliant devices
+ * Debugging Functions Header File
+ *
+- * $Id: debug.h,v 1.6 2001/01/12 23:51:04 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/dpcm.c
++++ b/drivers/usb/storage/dpcm.c
+@@ -1,7 +1,5 @@
+ /* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
+ *
+- * $Id: dpcm.c,v 1.4 2001/06/11 02:54:25 mdharm Exp $
+- *
+ * DPCM driver v0.1:
+ *
+ * First release
+--- a/drivers/usb/storage/dpcm.h
++++ b/drivers/usb/storage/dpcm.h
+@@ -1,7 +1,5 @@
+ /* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
+ *
+- * $Id: dpcm.h,v 1.2 2000/08/25 00:13:51 mdharm Exp $
+- *
+ * DPCM driver v0.1:
+ *
+ * First release
+--- a/drivers/usb/storage/freecom.c
++++ b/drivers/usb/storage/freecom.c
+@@ -1,7 +1,5 @@
+ /* Driver for Freecom USB/IDE adaptor
+ *
+- * $Id: freecom.c,v 1.22 2002/04/22 03:39:43 mdharm Exp $
+- *
+ * Freecom v0.1:
+ *
+ * First release
+--- a/drivers/usb/storage/freecom.h
++++ b/drivers/usb/storage/freecom.h
+@@ -1,7 +1,5 @@
+ /* Driver for Freecom USB/IDE adaptor
+ *
+- * $Id: freecom.h,v 1.4 2000/08/29 14:49:15 dlbrown Exp $
+- *
+ * Freecom v0.1:
+ *
+ * First release
+--- a/drivers/usb/storage/initializers.c
++++ b/drivers/usb/storage/initializers.c
+@@ -1,7 +1,5 @@
+ /* Special Initializers for certain USB Mass Storage devices
+ *
+- * $Id: initializers.c,v 1.2 2000/09/06 22:35:57 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/initializers.h
++++ b/drivers/usb/storage/initializers.h
+@@ -1,7 +1,5 @@
+ /* Header file for Special Initializers for certain USB Mass Storage devices
+ *
+- * $Id: initializers.h,v 1.1 2000/08/29 23:07:02 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/isd200.c
++++ b/drivers/usb/storage/isd200.c
+@@ -1,7 +1,5 @@
+ /* Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC
+ *
+- * $Id: isd200.c,v 1.16 2002/04/22 03:39:43 mdharm Exp $
+- *
+ * Current development and maintenance:
+ * (C) 2001-2002 Björn Stenberg (bjorn@haxx.se)
+ *
+--- a/drivers/usb/storage/jumpshot.c
++++ b/drivers/usb/storage/jumpshot.c
+@@ -1,7 +1,5 @@
+ /* Driver for Lexar "Jumpshot" Compact Flash reader
+ *
+- * $Id: jumpshot.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $
+- *
+ * jumpshot driver v0.1:
+ *
+ * First release
+--- a/drivers/usb/storage/protocol.c
++++ b/drivers/usb/storage/protocol.c
+@@ -1,7 +1,5 @@
+ /* Driver for USB Mass Storage compliant devices
+ *
+- * $Id: protocol.c,v 1.14 2002/04/22 03:39:43 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/protocol.h
++++ b/drivers/usb/storage/protocol.h
+@@ -1,8 +1,6 @@
+ /* Driver for USB Mass Storage compliant devices
+ * Protocol Functions Header File
+ *
+- * $Id: protocol.h,v 1.4 2001/02/13 07:10:03 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/scsiglue.c
++++ b/drivers/usb/storage/scsiglue.c
+@@ -1,8 +1,6 @@
+ /* Driver for USB Mass Storage compliant devices
+ * SCSI layer glue code
+ *
+- * $Id: scsiglue.c,v 1.26 2002/04/22 03:39:43 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/scsiglue.h
++++ b/drivers/usb/storage/scsiglue.h
+@@ -1,8 +1,6 @@
+ /* Driver for USB Mass Storage compliant devices
+ * SCSI Connecting Glue Header File
+ *
+- * $Id: scsiglue.h,v 1.4 2000/08/25 00:13:51 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/sddr09.c
++++ b/drivers/usb/storage/sddr09.c
+@@ -1,6 +1,5 @@
+ /* Driver for SanDisk SDDR-09 SmartMedia reader
+ *
+- * $Id: sddr09.c,v 1.24 2002/04/22 03:39:43 mdharm Exp $
+ * (c) 2000, 2001 Robert Baruch (autophile@starband.net)
+ * (c) 2002 Andries Brouwer (aeb@cwi.nl)
+ * Developed with the assistance of:
+--- a/drivers/usb/storage/sddr09.h
++++ b/drivers/usb/storage/sddr09.h
+@@ -1,8 +1,6 @@
+ /* Driver for SanDisk SDDR-09 SmartMedia reader
+ * Header File
+ *
+- * $Id: sddr09.h,v 1.5 2000/08/25 00:13:51 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 2000 Robert Baruch (autophile@dol.net)
+ * (c) 2002 Andries Brouwer (aeb@cwi.nl)
+--- a/drivers/usb/storage/sddr55.c
++++ b/drivers/usb/storage/sddr55.c
+@@ -1,7 +1,5 @@
+ /* Driver for SanDisk SDDR-55 SmartMedia reader
+ *
+- * $Id:$
+- *
+ * SDDR55 driver v0.1:
+ *
+ * First release
+--- a/drivers/usb/storage/sddr55.h
++++ b/drivers/usb/storage/sddr55.h
+@@ -1,8 +1,6 @@
+ /* Driver for SanDisk SDDR-55 SmartMedia reader
+ * Header File
+ *
+- * $Id:$
+- *
+ * Current development and maintenance by:
+ * (c) 2002 Simon Munton
+ *
+--- a/drivers/usb/storage/shuttle_usbat.c
++++ b/drivers/usb/storage/shuttle_usbat.c
+@@ -1,7 +1,5 @@
+ /* Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable
+ *
+- * $Id: shuttle_usbat.c,v 1.17 2002/04/22 03:39:43 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 2000, 2001 Robert Baruch (autophile@starband.net)
+ * (c) 2004, 2005 Daniel Drake <dsd@gentoo.org>
+--- a/drivers/usb/storage/shuttle_usbat.h
++++ b/drivers/usb/storage/shuttle_usbat.h
+@@ -1,8 +1,6 @@
+ /* Driver for SCM Microsystems USB-ATAPI cable
+ * Header File
+ *
+- * $Id: shuttle_usbat.h,v 1.5 2000/09/17 14:44:52 groovyjava Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 2000 Robert Baruch (autophile@dol.net)
+ * (c) 2004, 2005 Daniel Drake <dsd@gentoo.org>
+--- a/drivers/usb/storage/transport.c
++++ b/drivers/usb/storage/transport.c
+@@ -1,7 +1,5 @@
+ /* Driver for USB Mass Storage compliant devices
+ *
+- * $Id: transport.c,v 1.47 2002/04/22 03:39:43 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/transport.h
++++ b/drivers/usb/storage/transport.h
+@@ -1,8 +1,6 @@
+ /* Driver for USB Mass Storage compliant devices
+ * Transport Functions Header File
+ *
+- * $Id: transport.h,v 1.18 2002/04/21 02:57:59 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/unusual_devs.h
++++ b/drivers/usb/storage/unusual_devs.h
+@@ -1,8 +1,6 @@
+ /* Driver for USB Mass Storage compliant devices
+ * Unusual Devices File
+ *
+- * $Id: unusual_devs.h,v 1.32 2002/02/25 02:41:24 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/usb.c
++++ b/drivers/usb/storage/usb.c
+@@ -1,7 +1,5 @@
+ /* Driver for USB Mass Storage compliant devices
+ *
+- * $Id: usb.c,v 1.75 2002/04/22 03:39:43 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999-2003 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/drivers/usb/storage/usb.h
++++ b/drivers/usb/storage/usb.h
+@@ -1,8 +1,6 @@
+ /* Driver for USB Mass Storage compliant devices
+ * Main Header File
+ *
+- * $Id: usb.h,v 1.21 2002/04/21 02:57:59 mdharm Exp $
+- *
+ * Current development and maintenance by:
+ * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+--- a/include/linux/usbdevice_fs.h
++++ b/include/linux/usbdevice_fs.h
+@@ -22,8 +22,6 @@
+ *
+ * History:
+ * 0.1 04.01.2000 Created
+- *
+- * $Id: usbdevice_fs.h,v 1.1 2000/01/06 18:40:41 tom Exp $
+ */
+
+ /*****************************************************************************/
diff --git a/usb/usb-remove-documentation-usb-uhci.txt.patch b/usb/usb-remove-documentation-usb-uhci.txt.patch
new file mode 100644
index 00000000000000..9cf0d9a79fb7f7
--- /dev/null
+++ b/usb/usb-remove-documentation-usb-uhci.txt.patch
@@ -0,0 +1,188 @@
+From bunk@kernel.org Mon May 19 17:06:50 2008
+From: Adrian Bunk <bunk@kernel.org>
+Date: Tue, 20 May 2008 01:00:24 +0300
+Subject: USB: remove Documentation/usb/uhci.txt
+To: greg@kroah.com
+Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
+Message-ID: <20080519220024.GX17716@cs181133002.pp.htv.fi>
+Content-Disposition: inline
+
+
+The driver was removed before kernel 2.6.0
+
+Signed-off-by: Adrian Bunk <bunk@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ Documentation/usb/uhci.txt | 165 ---------------------------------------------
+ 1 file changed, 165 deletions(-)
+
+--- a/Documentation/usb/uhci.txt
++++ /dev/null
+@@ -1,165 +0,0 @@
+-Specification and Internals for the New UHCI Driver (Whitepaper...)
+-
+- brought to you by
+-
+- Georg Acher, acher@in.tum.de (executive slave) (base guitar)
+- Deti Fliegl, deti@fliegl.de (executive slave) (lead voice)
+- Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader)
+-
+- $Id: README.uhci,v 1.1 1999/12/14 14:03:02 fliegl Exp $
+-
+-This document and the new uhci sources can be found on
+- http://hotswap.in.tum.de/usb
+-
+-1. General issues
+-
+-1.1 Why a new UHCI driver, we already have one?!?
+-
+-Correct, but its internal structure got more and more mixed up by the (still
+-ongoing) efforts to get isochronous transfers (ISO) to work.
+-Since there is an increasing need for reliable ISO-transfers (especially
+-for USB-audio needed by TS and for a DAB-USB-Receiver build by GA and DF),
+-this state was a bit unsatisfying in our opinion, so we've decided (based
+-on knowledge and experiences with the old UHCI driver) to start
+-from scratch with a new approach, much simpler but at the same time more
+-powerful.
+-It is inspired by the way Win98/Win2000 handles USB requests via URBs,
+-but it's definitely 100% free of MS-code and doesn't crash while
+-unplugging an used ISO-device like Win98 ;-)
+-Some code for HW setup and root hub management was taken from the
+-original UHCI driver, but heavily modified to fit into the new code.
+-The invention of the basic concept, and major coding were completed in two
+-days (and nights) on the 16th and 17th of October 1999, now known as the
+-great USB-October-Revolution started by GA, DF, and TS ;-)
+-
+-Since the concept is in no way UHCI dependent, we hope that it will also be
+-transferred to the OHCI-driver, so both drivers share a common API.
+-
+-1.2. Advantages and disadvantages
+-
+-+ All USB transfer types work now!
+-+ Asynchronous operation
+-+ Simple, but powerful interface (only two calls for start and cancel)
+-+ Easy migration to the new API, simplified by a compatibility API
+-+ Simple usage of ISO transfers
+-+ Automatic linking of requests
+-+ ISO transfers allow variable length for each frame and striping
+-+ No CPU dependent and non-portable atomic memory access, no asm()-inlines
+-+ Tested on x86 and Alpha
+-
+-- Rewriting for ISO transfers needed
+-
+-1.3. Is there some compatibility to the old API?
+-
+-Yes, but only for control, bulk and interrupt transfers. We've implemented
+-some wrapper calls for these transfer types. The usbcore works fine with
+-these wrappers. For ISO there's no compatibility, because the old ISO-API
+-and its semantics were unnecessary complicated in our opinion.
+-
+-1.4. What's really working?
+-
+-As said above, CTRL and BULK already work fine even with the wrappers,
+-so legacy code wouldn't notice the change.
+-Regarding to Thomas, ISO transfers now run stable with USB audio.
+-INT transfers (e.g. mouse driver) work fine, too.
+-
+-1.5. Are there any bugs?
+-
+-No ;-)
+-Hm...
+-Well, of course this implementation needs extensive testing on all available
+-hardware, but we believe that any fixes shouldn't harm the overall concept.
+-
+-1.6. What should be done next?
+-
+-A large part of the request handling seems to be identical for UHCI and
+-OHCI, so it would be a good idea to extract the common parts and have only
+-the HW specific stuff in uhci.c. Furthermore, all other USB device drivers
+-should need URBification, if they use isochronous or interrupt transfers.
+-One thing missing in the current implementation (and the old UHCI driver)
+-is fair queueing for BULK transfers. Since this would need (in principle)
+-the alteration of already constructed TD chains (to switch from depth to
+-breadth execution), another way has to be found. Maybe some simple
+-heuristics work with the same effect.
+-
+----------------------------------------------------------------------------
+-
+-2. Internal structure and mechanisms
+-
+-To get quickly familiar with the internal structures, here's a short
+-description how the new UHCI driver works. However, the ultimate source of
+-truth is only uhci.c!
+-
+-2.1. Descriptor structure (QHs and TDs)
+-
+-During initialization, the following skeleton is allocated in init_skel:
+-
+- framespecific | common chain
+-
+-framelist[]
+-[ 0 ]-----> TD --> TD -------\
+-[ 1 ]-----> TD --> TD --------> TD ----> QH -------> QH -------> QH ---> NULL
+- ... TD --> TD -------/
+-[1023]-----> TD --> TD ------/
+-
+- ^^ ^^ ^^ ^^ ^^ ^^
+- 1024 TDs for 7 TDs for 1 TD for Start of Start of End Chain
+- ISO INT (2-128ms) 1ms-INT CTRL Chain BULK Chain
+-
+-For each CTRL or BULK transfer a new QH is allocated and the containing data
+-transfers are appended as (vertical) TDs. After building the whole QH with its
+-dangling TDs, the QH is inserted before the BULK Chain QH (for CTRL) or
+-before the End Chain QH (for BULK). Since only the QH->next pointers are
+-affected, no atomic memory operation is required. The three QHs in the
+-common chain are never equipped with TDs!
+-
+-For ISO or INT, the TD for each frame is simply inserted into the appropriate
+-ISO/INT-TD-chain for the desired frame. The 7 skeleton INT-TDs are scattered
+-among the 1024 frames similar to the old UHCI driver.
+-
+-For CTRL/BULK/ISO, the last TD in the transfer has the IOC-bit set. For INT,
+-every TD (there is only one...) has the IOC-bit set.
+-
+-Besides the data for the UHCI controller (2 or 4 32bit words), the descriptors
+-are double-linked through the .vertical and .horizontal elements in the
+-SW data of the descriptor (using the double-linked list structures and
+-operations), but SW-linking occurs only in closed domains, i.e. for each of
+-the 1024 ISO-chains and the 8 INT-chains there is a closed cycle. This
+-simplifies all insertions and unlinking operations and avoids costly
+-bus_to_virt()-calls.
+-
+-2.2. URB structure and linking to QH/TDs
+-
+-During assembly of the QH and TDs of the requested action, these descriptors
+-are stored in urb->urb_list, so the allocated QH/TD descriptors are bound to
+-this URB.
+-If the assembly was successful and the descriptors were added to the HW chain,
+-the corresponding URB is inserted into a global URB list for this controller.
+-This list stores all pending URBs.
+-
+-2.3. Interrupt processing
+-
+-Since UHCI provides no means to directly detect completed transactions, the
+-following is done in each UHCI interrupt (uhci_interrupt()):
+-
+-For each URB in the pending queue (process_urb()), the ACTIVE-flag of the
+-associated TDs are processed (depending on the transfer type
+-process_{transfer|interrupt|iso}()). If the TDs are not active anymore,
+-they indicate the completion of the transaction and the status is calculated.
+-Inactive QH/TDs are removed from the HW chain (since the host controller
+-already removed the TDs from the QH, no atomic access is needed) and
+-eventually the URB is marked as completed (OK or errors) and removed from the
+-pending queue. Then the next linked URB is submitted. After (or immediately
+-before) that, the completion handler is called.
+-
+-2.4. Unlinking URBs
+-
+-First, all QH/TDs stored in the URB are unlinked from the HW chain.
+-To ensure that the host controller really left a vertical TD chain, we
+-wait for one frame. After that, the TDs are physically destroyed.
+-
+-2.5. URB linking and the consequences
+-
+-Since URBs can be linked and the corresponding submit_urb is called in
+-the UHCI-interrupt, all work associated with URB/QH/TD assembly has to be
+-interrupt save. This forces kmalloc to use GFP_ATOMIC in the interrupt.
diff --git a/usb/usb-rndis-switch-to-seq_files.patch b/usb/usb-rndis-switch-to-seq_files.patch
new file mode 100644
index 00000000000000..fe1167ffb799e5
--- /dev/null
+++ b/usb/usb-rndis-switch-to-seq_files.patch
@@ -0,0 +1,122 @@
+From akpm@linux-foundation.org Mon May 19 17:11:23 2008
+From: Alexey Dobriyan <adobriyan@gmail.com>
+Date: Wed, 14 May 2008 16:25:13 -0700
+Subject: USB: rndis: switch to seq_files
+To: greg@kroah.com
+Cc: linux-usb@vger.kernel.org, akpm@linux-foundation.org, adobriyan@gmail.com, david-b@pacbell.net
+Message-ID: <200805142325.m4ENPEUJ026946@imap1.linux-foundation.org>
+
+
+From: Alexey Dobriyan <adobriyan@gmail.com>
+
+Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
+Cc: David Brownell <david-b@pacbell.net>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/rndis.c | 53 +++++++++++++++++++++------------------------
+ 1 file changed, 25 insertions(+), 28 deletions(-)
+
+--- a/drivers/usb/gadget/rndis.c
++++ b/drivers/usb/gadget/rndis.c
+@@ -28,6 +28,7 @@
+ #include <linux/init.h>
+ #include <linux/list.h>
+ #include <linux/proc_fs.h>
++#include <linux/seq_file.h>
+ #include <linux/netdevice.h>
+
+ #include <asm/io.h>
+@@ -1294,14 +1295,11 @@ int rndis_rm_hdr(struct sk_buff *skb)
+
+ #ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+-static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof,
+- void *data)
++static int rndis_proc_show(struct seq_file *m, void *v)
+ {
+- char *out = page;
+- int len;
+- rndis_params *param = (rndis_params *) data;
++ rndis_params *param = m->private;
+
+- out += snprintf (out, count,
++ seq_printf(m,
+ "Config Nr. %d\n"
+ "used : %s\n"
+ "state : %s\n"
+@@ -1324,25 +1322,13 @@ static int rndis_proc_read (char *page,
+ (param->media_state) ? 0 : param->speed*100,
+ (param->media_state) ? "disconnected" : "connected",
+ param->vendorID, param->vendorDescr);
+-
+- len = out - page;
+- len -= off;
+-
+- if (len < count) {
+- *eof = 1;
+- if (len <= 0)
+- return 0;
+- } else
+- len = count;
+-
+- *start = page + off;
+- return len;
++ return 0;
+ }
+
+-static int rndis_proc_write (struct file *file, const char __user *buffer,
+- unsigned long count, void *data)
++static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
++ size_t count, loff_t *ppos)
+ {
+- rndis_params *p = data;
++ rndis_params *p = PDE(file->f_path.dentry->d_inode)->data;
+ u32 speed = 0;
+ int i, fl_speed = 0;
+
+@@ -1384,6 +1370,20 @@ static int rndis_proc_write (struct file
+ return count;
+ }
+
++static int rndis_proc_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, rndis_proc_show, PDE(inode)->data);
++}
++
++static const struct file_operations rndis_proc_fops = {
++ .owner = THIS_MODULE,
++ .open = rndis_proc_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++ .write = rndis_proc_write,
++};
++
+ #define NAME_TEMPLATE "driver/rndis-%03d"
+
+ static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
+@@ -1401,7 +1401,9 @@ int __init rndis_init (void)
+
+ sprintf (name, NAME_TEMPLATE, i);
+ if (!(rndis_connect_state [i]
+- = create_proc_entry (name, 0660, NULL)))
++ = proc_create_data(name, 0660, NULL,
++ &rndis_proc_fops,
++ (void *)(rndis_per_dev_params + i))))
+ {
+ DBG("%s :remove entries", __func__);
+ while (i) {
+@@ -1411,11 +1413,6 @@ int __init rndis_init (void)
+ DBG("\n");
+ return -EIO;
+ }
+-
+- rndis_connect_state [i]->write_proc = rndis_proc_write;
+- rndis_connect_state [i]->read_proc = rndis_proc_read;
+- rndis_connect_state [i]->data = (void *)
+- (rndis_per_dev_params + i);
+ #endif
+ rndis_per_dev_params [i].confignr = i;
+ rndis_per_dev_params [i].used = 0;
diff --git a/usb/usb-serial-gadget-modular-tty-glue.patch b/usb/usb-serial-gadget-modular-tty-glue.patch
new file mode 100644
index 00000000000000..0faeef71d49eb1
--- /dev/null
+++ b/usb/usb-serial-gadget-modular-tty-glue.patch
@@ -0,0 +1,1377 @@
+From david-b@pacbell.net Mon May 19 17:09:43 2008
+From: David Brownell <david-b@pacbell.net>
+Date: Fri, 16 May 2008 10:42:09 -0700
+Subject: usb serial gadget: modular tty glue
+To: linux-usb@vger.kernel.org
+Cc: Greg KH <greg@kroah.com>, Alan Cox <alan@lxorguk.ukuu.org.uk>
+Message-ID: <200805161042.09655.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+This abstracts the "gadget serial" driver TTY glue into a separate
+module, cleaning it up and disentangling it from connection state.
+
+It also changed some behaviors for the better:
+
+ - Stops using "experimental" major #127, and switches over to
+ having the TTY layer allocate the dev_t numbers.
+
+ - Provides /sys/class/tty/ttyGS* nodes, thus mdev/udev support.
+ (Note "mdev" hotplug bug in Busybox v1.7.2: /dev/ttyGS0 will
+ be a *block* device without CONFIG_SYSFS_DEPRECATED_V2.)
+
+ - The tty nodes no longer reject opens when there's no host.
+ Now they can support normal getty configs in /etc/inttab...
+
+ - Now implements RX throttling. When the line discipline says
+ it doesn't want any more data, only packets in flight will be
+ delivered (currently, max 1K/8K at full/high speeds) until it
+ unthrottles the data.
+
+ - Supports low_latency. This is a good policy for all USB serial
+ adapters, since it eliminates scheduler overhead on RX paths.
+
+This also includes much cleanup including better comments, fixing
+memory leaks and other bugs (including some locking fixes), messaging
+cleanup, and an interface audit and tightening. This added up to a
+significant object code shrinkage, on the order of 20% (!) depending
+on CPU and compiler.
+
+A separate patch actually kicks in this new code, using the functions
+declared in this new header, and removes the previous glue.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/u_serial.c | 1270 ++++++++++++++++++++++++++++++++++++++++++
+ drivers/usb/gadget/u_serial.h | 51 +
+ 2 files changed, 1321 insertions(+)
+
+--- /dev/null
++++ b/drivers/usb/gadget/u_serial.c
+@@ -0,0 +1,1270 @@
++/*
++ * u_serial.c - utilities for USB gadget "serial port"/TTY support
++ *
++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
++ * Copyright (C) 2008 David Brownell
++ *
++ * This code also borrows from usbserial.c, which is
++ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
++ * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
++ * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
++ *
++ * This software is distributed under the terms of the GNU General
++ * Public License ("GPL") as published by the Free Software Foundation,
++ * either version 2 of that License or (at your option) any later version.
++ */
++
++#include <linux/kernel.h>
++#include <linux/interrupt.h>
++#include <linux/device.h>
++#include <linux/delay.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++
++#include "u_serial.h"
++
++
++/*
++ * This module encapsulates the TTY layer glue needed to provide basic
++ * "serial port" functionality through the USB gadget stack. Each such
++ * port is exposed through a /dev/ttyGS* node.
++ *
++ * After initialization (gserial_setup), these TTY port devices stay
++ * available until they are removed (gserial_cleanup). Each one may be
++ * connected to a USB function (gserial_connect), or disconnected (with
++ * gserial_disconnect) when the USB host issues a config change event.
++ * Data can only flow when the port is connected to the host.
++ *
++ * A given TTY port can be made available in multiple configurations.
++ * For example, each one might expose a ttyGS0 node which provides a
++ * login application. In one case that might use CDC ACM interface 0,
++ * while another configuration might use interface 3 for that. The
++ * work to handle that (including descriptor management) is not part
++ * of this module.
++ *
++ * Configurations may expose more than one TTY port. For example, if
++ * ttyGS0 provides login service, then ttyGS1 might provide dialer access
++ * for a telephone or fax link. And ttyGS2 might be something that just
++ * needs a simple byte stream interface for some messaging protocol that
++ * is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
++ */
++
++/*
++ * gserial is the lifecycle interface, used by USB functions
++ * gs_port is the I/O nexus, used by the tty driver
++ * tty_struct links to the tty/filesystem framework
++ *
++ * gserial <---> gs_port ... links will be null when the USB link is
++ * inactive; managed by gserial_{connect,disconnect}().
++ * gserial->ioport == usb_ep->driver_data ... gs_port
++ * gs_port->port_usb ... gserial
++ *
++ * gs_port <---> tty_struct ... links will be null when the TTY file
++ * isn't opened; managed by gs_open()/gs_close()
++ * gserial->port_tty ... tty_struct
++ * tty_struct->driver_data ... gserial
++ */
++
++/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the
++ * next layer of buffering. For TX that's a circular buffer; for RX
++ * consider it a NOP. A third layer is provided by the TTY code.
++ */
++#define QUEUE_SIZE 16
++#define WRITE_BUF_SIZE 8192 /* TX only */
++
++/* circular buffer */
++struct gs_buf {
++ unsigned buf_size;
++ char *buf_buf;
++ char *buf_get;
++ char *buf_put;
++};
++
++/*
++ * The port structure holds info for each port, one for each minor number
++ * (and thus for each /dev/ node).
++ */
++struct gs_port {
++ spinlock_t port_lock;
++
++ struct gserial *port_usb;
++ struct tty_struct *port_tty;
++
++ unsigned open_count;
++ bool openclose; /* open/close in progress */
++ u8 port_num;
++
++ wait_queue_head_t close_wait; /* wait for last close */
++
++ struct list_head read_pool;
++ struct tasklet_struct push;
++
++ struct list_head write_pool;
++ struct gs_buf port_write_buf;
++ wait_queue_head_t drain_wait; /* wait while writes drain */
++
++ /* REVISIT this state ... */
++ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
++};
++
++/* increase N_PORTS if you need more */
++#define N_PORTS 4
++static struct portmaster {
++ struct mutex lock; /* protect open/close */
++ struct gs_port *port;
++} ports[N_PORTS];
++static unsigned n_ports;
++
++#define GS_CLOSE_TIMEOUT 15 /* seconds */
++
++
++
++#ifdef VERBOSE_DEBUG
++#define pr_vdebug(fmt, arg...) \
++ pr_debug(fmt, ##arg)
++#else
++#define pr_vdebug(fmt, arg...) \
++ ({ if (0) pr_debug(fmt, ##arg); })
++#endif
++
++/*-------------------------------------------------------------------------*/
++
++/* Circular Buffer */
++
++/*
++ * gs_buf_alloc
++ *
++ * Allocate a circular buffer and all associated memory.
++ */
++static int gs_buf_alloc(struct gs_buf *gb, unsigned size)
++{
++ gb->buf_buf = kmalloc(size, GFP_KERNEL);
++ if (gb->buf_buf == NULL)
++ return -ENOMEM;
++
++ gb->buf_size = size;
++ gb->buf_get = gb->buf_put = gb->buf_buf;
++
++ return 0;
++}
++
++/*
++ * gs_buf_free
++ *
++ * Free the buffer and all associated memory.
++ */
++static void gs_buf_free(struct gs_buf *gb)
++{
++ kfree(gb->buf_buf);
++ gb->buf_buf = NULL;
++}
++
++/*
++ * gs_buf_clear
++ *
++ * Clear out all data in the circular buffer.
++ */
++static void gs_buf_clear(struct gs_buf *gb)
++{
++ gb->buf_get = gb->buf_put;
++ /* equivalent to a get of all data available */
++}
++
++/*
++ * gs_buf_data_avail
++ *
++ * Return the number of bytes of data available in the circular
++ * buffer.
++ */
++static unsigned gs_buf_data_avail(struct gs_buf *gb)
++{
++ return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
++}
++
++/*
++ * gs_buf_space_avail
++ *
++ * Return the number of bytes of space available in the circular
++ * buffer.
++ */
++static unsigned gs_buf_space_avail(struct gs_buf *gb)
++{
++ return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
++}
++
++/*
++ * gs_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
++gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
++{
++ unsigned len;
++
++ len = gs_buf_space_avail(gb);
++ if (count > len)
++ count = len;
++
++ if (count == 0)
++ return 0;
++
++ len = gb->buf_buf + gb->buf_size - gb->buf_put;
++ if (count > len) {
++ memcpy(gb->buf_put, buf, len);
++ memcpy(gb->buf_buf, buf+len, count - len);
++ gb->buf_put = gb->buf_buf + count - len;
++ } else {
++ memcpy(gb->buf_put, buf, count);
++ if (count < len)
++ gb->buf_put += count;
++ else /* count == len */
++ gb->buf_put = gb->buf_buf;
++ }
++
++ return count;
++}
++
++/*
++ * gs_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
++gs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
++{
++ unsigned len;
++
++ len = gs_buf_data_avail(gb);
++ if (count > len)
++ count = len;
++
++ if (count == 0)
++ return 0;
++
++ len = gb->buf_buf + gb->buf_size - gb->buf_get;
++ if (count > len) {
++ memcpy(buf, gb->buf_get, len);
++ memcpy(buf+len, gb->buf_buf, count - len);
++ gb->buf_get = gb->buf_buf + count - len;
++ } else {
++ memcpy(buf, gb->buf_get, count);
++ if (count < len)
++ gb->buf_get += count;
++ else /* count == len */
++ gb->buf_get = gb->buf_buf;
++ }
++
++ return count;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* I/O glue between TTY (upper) and USB function (lower) driver layers */
++
++/*
++ * gs_alloc_req
++ *
++ * Allocate a usb_request and its buffer. Returns a pointer to the
++ * usb_request or NULL if there is an error.
++ */
++static struct usb_request *
++gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
++{
++ struct usb_request *req;
++
++ req = usb_ep_alloc_request(ep, kmalloc_flags);
++
++ if (req != NULL) {
++ req->length = len;
++ req->buf = kmalloc(len, kmalloc_flags);
++ if (req->buf == NULL) {
++ usb_ep_free_request(ep, req);
++ return NULL;
++ }
++ }
++
++ return req;
++}
++
++/*
++ * gs_free_req
++ *
++ * Free a usb_request and its buffer.
++ */
++static void gs_free_req(struct usb_ep *ep, struct usb_request *req)
++{
++ kfree(req->buf);
++ usb_ep_free_request(ep, req);
++}
++
++/*
++ * gs_send_packet
++ *
++ * If there is data to send, a packet is built in the given
++ * buffer and the size is returned. If there is no data to
++ * send, 0 is returned.
++ *
++ * Called with port_lock held.
++ */
++static unsigned
++gs_send_packet(struct gs_port *port, char *packet, unsigned size)
++{
++ unsigned len;
++
++ len = gs_buf_data_avail(&port->port_write_buf);
++ if (len < size)
++ size = len;
++ if (size != 0)
++ size = gs_buf_get(&port->port_write_buf, packet, size);
++ return size;
++}
++
++/*
++ * gs_start_tx
++ *
++ * This function finds available write requests, calls
++ * gs_send_packet to fill these packets with data, and
++ * continues until either there are no more write requests
++ * available or no more data to send. This function is
++ * run whenever data arrives or write requests are available.
++ *
++ * Context: caller owns port_lock; port_usb is non-null.
++ */
++static int gs_start_tx(struct gs_port *port)
++/*
++__releases(&port->port_lock)
++__acquires(&port->port_lock)
++*/
++{
++ struct list_head *pool = &port->write_pool;
++ struct usb_ep *in = port->port_usb->in;
++ int status = 0;
++ bool do_tty_wake = false;
++
++ while (!list_empty(pool)) {
++ struct usb_request *req;
++ int len;
++
++ req = list_entry(pool->next, struct usb_request, list);
++ len = gs_send_packet(port, req->buf, in->maxpacket);
++ if (len == 0) {
++ wake_up_interruptible(&port->drain_wait);
++ break;
++ }
++ do_tty_wake = true;
++
++ req->length = len;
++ list_del(&req->list);
++
++#ifdef VERBOSE_DEBUG
++ pr_debug("%s: %s, len=%d, 0x%02x 0x%02x 0x%02x ...\n",
++ __func__, in->name, len, *((u8 *)req->buf),
++ *((u8 *)req->buf+1), *((u8 *)req->buf+2));
++#endif
++
++ /* Drop lock while we call out of driver; completions
++ * could be issued while we do so. Disconnection may
++ * happen too; maybe immediately before we queue this!
++ *
++ * NOTE that we may keep sending data for a while after
++ * the TTY closed (dev->ioport->port_tty is NULL).
++ */
++ spin_unlock(&port->port_lock);
++ status = usb_ep_queue(in, req, GFP_ATOMIC);
++ spin_lock(&port->port_lock);
++
++ if (status) {
++ pr_debug("%s: %s %s err %d\n",
++ __func__, "queue", in->name, status);
++ list_add(&req->list, pool);
++ break;
++ }
++
++ /* abort immediately after disconnect */
++ if (!port->port_usb)
++ break;
++ }
++
++ if (do_tty_wake && port->port_tty)
++ tty_wakeup(port->port_tty);
++ return status;
++}
++
++static void gs_rx_push(unsigned long _port)
++{
++ struct gs_port *port = (void *)_port;
++ struct tty_struct *tty = port->port_tty;
++
++ /* With low_latency, tty_flip_buffer_push() doesn't put its
++ * real work through a workqueue, so the ldisc has a better
++ * chance to keep up with peak USB data rates.
++ */
++ if (tty) {
++ tty_flip_buffer_push(tty);
++ wake_up_interruptible(&tty->read_wait);
++ }
++}
++
++/*
++ * gs_recv_packet
++ *
++ * Called for each USB packet received. Reads the packet
++ * header and stuffs the data in the appropriate tty buffer.
++ * Returns 0 if successful, or a negative error number.
++ *
++ * Called during USB completion routine, on interrupt time.
++ * With port_lock.
++ */
++static int gs_recv_packet(struct gs_port *port, char *packet, unsigned size)
++{
++ unsigned len;
++ struct tty_struct *tty;
++
++ /* I/O completions can continue for a while after close(), until the
++ * request queue empties. Just discard any data we receive, until
++ * something reopens this TTY ... as if there were no HW flow control.
++ */
++ tty = port->port_tty;
++ if (tty == NULL) {
++ pr_vdebug("%s: ttyGS%d, after close\n",
++ __func__, port->port_num);
++ return -EIO;
++ }
++
++ len = tty_insert_flip_string(tty, packet, size);
++ if (len > 0)
++ tasklet_schedule(&port->push);
++ if (len < size)
++ pr_debug("%s: ttyGS%d, drop %d bytes\n",
++ __func__, port->port_num, size - len);
++ return 0;
++}
++
++/*
++ * Context: caller owns port_lock, and port_usb is set
++ */
++static unsigned gs_start_rx(struct gs_port *port)
++/*
++__releases(&port->port_lock)
++__acquires(&port->port_lock)
++*/
++{
++ struct list_head *pool = &port->read_pool;
++ struct usb_ep *out = port->port_usb->out;
++ unsigned started = 0;
++
++ while (!list_empty(pool)) {
++ struct usb_request *req;
++ int status;
++ struct tty_struct *tty;
++
++ /* no more rx if closed or throttled */
++ tty = port->port_tty;
++ if (!tty || test_bit(TTY_THROTTLED, &tty->flags))
++ break;
++
++ req = list_entry(pool->next, struct usb_request, list);
++ list_del(&req->list);
++ req->length = out->maxpacket;
++
++ /* drop lock while we call out; the controller driver
++ * may need to call us back (e.g. for disconnect)
++ */
++ spin_unlock(&port->port_lock);
++ status = usb_ep_queue(out, req, GFP_ATOMIC);
++ spin_lock(&port->port_lock);
++
++ if (status) {
++ pr_debug("%s: %s %s err %d\n",
++ __func__, "queue", out->name, status);
++ list_add(&req->list, pool);
++ break;
++ }
++ started++;
++
++ /* abort immediately after disconnect */
++ if (!port->port_usb)
++ break;
++ }
++ return started;
++}
++
++static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ int status;
++ struct gs_port *port = ep->driver_data;
++
++ spin_lock(&port->port_lock);
++ list_add(&req->list, &port->read_pool);
++
++ switch (req->status) {
++ case 0:
++ /* normal completion */
++ status = gs_recv_packet(port, req->buf, req->actual);
++ if (status && status != -EIO)
++ pr_debug("%s: %s %s err %d\n",
++ __func__, "recv", ep->name, status);
++ gs_start_rx(port);
++ break;
++
++ case -ESHUTDOWN:
++ /* disconnect */
++ pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
++ break;
++
++ default:
++ /* presumably a transient fault */
++ pr_warning("%s: unexpected %s status %d\n",
++ __func__, ep->name, req->status);
++ gs_start_rx(port);
++ break;
++ }
++ spin_unlock(&port->port_lock);
++}
++
++static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct gs_port *port = ep->driver_data;
++
++ spin_lock(&port->port_lock);
++ list_add(&req->list, &port->write_pool);
++
++ switch (req->status) {
++ default:
++ /* presumably a transient fault */
++ pr_warning("%s: unexpected %s status %d\n",
++ __func__, ep->name, req->status);
++ /* FALL THROUGH */
++ case 0:
++ /* normal completion */
++ gs_start_tx(port);
++ break;
++
++ case -ESHUTDOWN:
++ /* disconnect */
++ pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
++ break;
++ }
++
++ spin_unlock(&port->port_lock);
++}
++
++static void gs_free_requests(struct usb_ep *ep, struct list_head *head)
++{
++ struct usb_request *req;
++
++ while (!list_empty(head)) {
++ req = list_entry(head->next, struct usb_request, list);
++ list_del(&req->list);
++ gs_free_req(ep, req);
++ }
++}
++
++static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
++ void (*fn)(struct usb_ep *, struct usb_request *))
++{
++ int i;
++ struct usb_request *req;
++
++ /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
++ * do quite that many this time, don't fail ... we just won't
++ * be as speedy as we might otherwise be.
++ */
++ for (i = 0; i < QUEUE_SIZE; i++) {
++ req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
++ if (!req)
++ return list_empty(head) ? -ENOMEM : 0;
++ req->complete = fn;
++ list_add_tail(&req->list, head);
++ }
++ return 0;
++}
++
++/**
++ * gs_start_io - start USB I/O streams
++ * @dev: encapsulates endpoints to use
++ * Context: holding port_lock; port_tty and port_usb are non-null
++ *
++ * We only start I/O when something is connected to both sides of
++ * this port. If nothing is listening on the host side, we may
++ * be pointlessly filling up our TX buffers and FIFO.
++ */
++static int gs_start_io(struct gs_port *port)
++{
++ struct list_head *head = &port->read_pool;
++ struct usb_ep *ep = port->port_usb->out;
++ int status;
++ unsigned started;
++
++ /* Allocate RX and TX I/O buffers. We can't easily do this much
++ * earlier (with GFP_KERNEL) because the requests are coupled to
++ * endpoints, as are the packet sizes we'll be using. Different
++ * configurations may use different endpoints with a given port;
++ * and high speed vs full speed changes packet sizes too.
++ */
++ status = gs_alloc_requests(ep, head, gs_read_complete);
++ if (status)
++ return status;
++
++ status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
++ gs_write_complete);
++ if (status) {
++ gs_free_requests(ep, head);
++ return status;
++ }
++
++ /* queue read requests */
++ started = gs_start_rx(port);
++
++ /* unblock any pending writes into our circular buffer */
++ if (started) {
++ tty_wakeup(port->port_tty);
++ } else {
++ gs_free_requests(ep, head);
++ gs_free_requests(port->port_usb->in, &port->write_pool);
++ }
++
++ return started ? 0 : status;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* TTY Driver */
++
++/*
++ * gs_open sets up the link between a gs_port and its associated TTY.
++ * That link is broken *only* by TTY close(), and all driver methods
++ * know that.
++ */
++static int gs_open(struct tty_struct *tty, struct file *file)
++{
++ int port_num = tty->index;
++ struct gs_port *port;
++ int status;
++
++ if (port_num < 0 || port_num >= n_ports)
++ return -ENXIO;
++
++ do {
++ mutex_lock(&ports[port_num].lock);
++ port = ports[port_num].port;
++ if (!port)
++ status = -ENODEV;
++ else {
++ spin_lock_irq(&port->port_lock);
++
++ /* already open? Great. */
++ if (port->open_count) {
++ status = 0;
++ port->open_count++;
++
++ /* currently opening/closing? wait ... */
++ } else if (port->openclose) {
++ status = -EBUSY;
++
++ /* ... else we do the work */
++ } else {
++ status = -EAGAIN;
++ port->openclose = true;
++ }
++ spin_unlock_irq(&port->port_lock);
++ }
++ mutex_unlock(&ports[port_num].lock);
++
++ switch (status) {
++ default:
++ /* fully handled */
++ return status;
++ case -EAGAIN:
++ /* must do the work */
++ break;
++ case -EBUSY:
++ /* wait for EAGAIN task to finish */
++ msleep(1);
++ /* REVISIT could have a waitchannel here, if
++ * concurrent open performance is important
++ */
++ break;
++ }
++ } while (status != -EAGAIN);
++
++ /* Do the "real open" */
++ spin_lock_irq(&port->port_lock);
++
++ /* allocate circular buffer on first open */
++ if (port->port_write_buf.buf_buf == NULL) {
++
++ spin_unlock_irq(&port->port_lock);
++ status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
++ spin_lock_irq(&port->port_lock);
++
++ if (status) {
++ pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n",
++ port->port_num, tty, file);
++ port->openclose = false;
++ goto exit_unlock_port;
++ }
++ }
++
++ /* REVISIT if REMOVED (ports[].port NULL), abort the open
++ * to let rmmod work faster (but this way isn't wrong).
++ */
++
++ /* REVISIT maybe wait for "carrier detect" */
++
++ tty->driver_data = port;
++ port->port_tty = tty;
++
++ port->open_count = 1;
++ port->openclose = false;
++
++ /* low_latency means ldiscs work in tasklet context, without
++ * needing a workqueue schedule ... easier to keep up.
++ */
++ tty->low_latency = 1;
++
++ /* if connected, start the I/O stream */
++ if (port->port_usb) {
++ pr_debug("gs_open: start ttyGS%d\n", port->port_num);
++ gs_start_io(port);
++
++ /* REVISIT for ACM, issue "network connected" event */
++ }
++
++ pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
++
++ status = 0;
++
++exit_unlock_port:
++ spin_unlock_irq(&port->port_lock);
++ return status;
++}
++
++static int gs_writes_finished(struct gs_port *p)
++{
++ int cond;
++
++ /* return true on disconnect or empty buffer */
++ spin_lock_irq(&p->port_lock);
++ cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);
++ spin_unlock_irq(&p->port_lock);
++
++ return cond;
++}
++
++static void gs_close(struct tty_struct *tty, struct file *file)
++{
++ struct gs_port *port = tty->driver_data;
++
++ spin_lock_irq(&port->port_lock);
++
++ if (port->open_count != 1) {
++ if (port->open_count == 0)
++ WARN_ON(1);
++ else
++ --port->open_count;
++ goto exit;
++ }
++
++ pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
++
++ /* mark port as closing but in use; we can drop port lock
++ * and sleep if necessary
++ */
++ port->openclose = true;
++ port->open_count = 0;
++
++ if (port->port_usb)
++ /* REVISIT for ACM, issue "network disconnected" event */;
++
++ /* wait for circular write buffer to drain, disconnect, or at
++ * most GS_CLOSE_TIMEOUT seconds; then discard the rest
++ */
++ if (gs_buf_data_avail(&port->port_write_buf) > 0
++ && port->port_usb) {
++ spin_unlock_irq(&port->port_lock);
++ wait_event_interruptible_timeout(port->drain_wait,
++ gs_writes_finished(port),
++ GS_CLOSE_TIMEOUT * HZ);
++ spin_lock_irq(&port->port_lock);
++ }
++
++ /* Iff we're disconnected, there can be no I/O in flight so it's
++ * ok to free the circular buffer; else just scrub it. And don't
++ * let the push tasklet fire again until we're re-opened.
++ */
++ if (port->port_usb == NULL)
++ gs_buf_free(&port->port_write_buf);
++ else
++ gs_buf_clear(&port->port_write_buf);
++
++ tasklet_kill(&port->push);
++
++ tty->driver_data = NULL;
++ port->port_tty = NULL;
++
++ port->openclose = false;
++
++ pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
++ port->port_num, tty, file);
++
++ wake_up_interruptible(&port->close_wait);
++exit:
++ spin_unlock_irq(&port->port_lock);
++}
++
++static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++ int status;
++
++ pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
++ port->port_num, tty, count);
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ if (count)
++ count = gs_buf_put(&port->port_write_buf, buf, count);
++ /* treat count == 0 as flush_chars() */
++ if (port->port_usb)
++ status = gs_start_tx(port);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ return count;
++}
++
++static int gs_put_char(struct tty_struct *tty, unsigned char ch)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++ int status;
++
++ pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %p\n",
++ port->port_num, tty, ch, __builtin_return_address(0));
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ status = gs_buf_put(&port->port_write_buf, &ch, 1);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ return status;
++}
++
++static void gs_flush_chars(struct tty_struct *tty)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++
++ pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ if (port->port_usb)
++ gs_start_tx(port);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++}
++
++static int gs_write_room(struct tty_struct *tty)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++ int room = 0;
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ if (port->port_usb)
++ room = gs_buf_space_avail(&port->port_write_buf);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
++ port->port_num, tty, room);
++
++ return room;
++}
++
++static int gs_chars_in_buffer(struct tty_struct *tty)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++ int chars = 0;
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ chars = gs_buf_data_avail(&port->port_write_buf);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
++ port->port_num, tty, chars);
++
++ return chars;
++}
++
++/* undo side effects of setting TTY_THROTTLED */
++static void gs_unthrottle(struct tty_struct *tty)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++ unsigned started = 0;
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ if (port->port_usb)
++ started = gs_start_rx(port);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ pr_vdebug("gs_unthrottle: ttyGS%d, %d packets\n",
++ port->port_num, started);
++}
++
++#if 0
++
++static void gs_break(struct tty_struct *tty, int break_state)
++{
++}
++
++static int gs_ioctl(struct tty_struct *tty, struct file *file,
++ unsigned cmd, unsigned long arg)
++{
++ struct gs_port *port = tty->driver_data;
++
++ pr_debug("gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n",
++ port->port_num, tty, file, cmd, arg);
++
++ /* handle ioctls */
++
++ /* could not handle ioctl */
++ return -ENOIOCTLCMD;
++}
++
++static void gs_set_termios(struct tty_struct *tty, struct ktermios *old)
++{
++}
++
++#endif
++
++static const struct tty_operations gs_tty_ops = {
++ .open = gs_open,
++ .close = gs_close,
++ .write = gs_write,
++ .put_char = gs_put_char,
++ .flush_chars = gs_flush_chars,
++ .write_room = gs_write_room,
++ .chars_in_buffer = gs_chars_in_buffer,
++ .unthrottle = gs_unthrottle,
++#if 0
++ .break_ctl = gs_break,
++ .ioctl = gs_ioctl,
++ .set_termios = gs_set_termios,
++#endif
++};
++
++/*-------------------------------------------------------------------------*/
++
++static struct tty_driver *gs_tty_driver;
++
++static int __init
++gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
++{
++ struct gs_port *port;
++
++ port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
++ if (port == NULL)
++ return -ENOMEM;
++
++ spin_lock_init(&port->port_lock);
++ init_waitqueue_head(&port->close_wait);
++ init_waitqueue_head(&port->drain_wait);
++
++ tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
++
++ INIT_LIST_HEAD(&port->read_pool);
++ INIT_LIST_HEAD(&port->write_pool);
++
++ port->port_num = port_num;
++ port->port_line_coding = *coding;
++
++ ports[port_num].port = port;
++
++ return 0;
++}
++
++/**
++ * gserial_setup - initialize TTY driver for one or more ports
++ * @g: gadget to associate with these ports
++ * @count: how many ports to support
++ * Context: may sleep
++ *
++ * The TTY stack needs to know in advance how many devices it should
++ * plan to manage. Use this call to set up the ports you will be
++ * exporting through USB. Later, connect them to functions based
++ * on what configuration is activated by the USB host; and disconnect
++ * them as appropriate.
++ *
++ * An example would be a two-configuration device in which both
++ * configurations expose port 0, but through different functions.
++ * One configuration could even expose port 1 while the other
++ * one doesn't.
++ *
++ * Returns negative errno or zero.
++ */
++int __init gserial_setup(struct usb_gadget *g, unsigned count)
++{
++ unsigned i;
++ struct usb_cdc_line_coding coding;
++ int status;
++
++ if (count == 0 || count > N_PORTS)
++ return -EINVAL;
++
++ gs_tty_driver = alloc_tty_driver(count);
++ if (!gs_tty_driver)
++ return -ENOMEM;
++
++ gs_tty_driver->owner = THIS_MODULE;
++ gs_tty_driver->driver_name = "g_serial";
++ gs_tty_driver->name = "ttyGS";
++ /* uses dynamically assigned dev_t values */
++
++ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
++ gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
++ gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
++ gs_tty_driver->init_termios = tty_std_termios;
++
++ /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
++ * MS-Windows. Otherwise, most of these flags shouldn't affect
++ * anything unless we were to actually hook up to a serial line.
++ */
++ gs_tty_driver->init_termios.c_cflag =
++ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
++ gs_tty_driver->init_termios.c_ispeed = 9600;
++ gs_tty_driver->init_termios.c_ospeed = 9600;
++
++ coding.dwDTERate = __constant_cpu_to_le32(9600);
++ coding.bCharFormat = 8;
++ coding.bParityType = USB_CDC_NO_PARITY;
++ coding.bDataBits = USB_CDC_1_STOP_BITS;
++
++ tty_set_operations(gs_tty_driver, &gs_tty_ops);
++
++ /* make devices be openable */
++ for (i = 0; i < count; i++) {
++ mutex_init(&ports[i].lock);
++ status = gs_port_alloc(i, &coding);
++ if (status) {
++ count = i;
++ goto fail;
++ }
++ }
++ n_ports = count;
++
++ /* export the driver ... */
++ status = tty_register_driver(gs_tty_driver);
++ if (status) {
++ put_tty_driver(gs_tty_driver);
++ pr_err("%s: cannot register, err %d\n",
++ __func__, status);
++ goto fail;
++ }
++
++ /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
++ for (i = 0; i < count; i++) {
++ struct device *tty_dev;
++
++ tty_dev = tty_register_device(gs_tty_driver, i, &g->dev);
++ if (IS_ERR(tty_dev))
++ pr_warning("%s: no classdev for port %d, err %ld\n",
++ __func__, i, PTR_ERR(tty_dev));
++ }
++
++ pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
++ count, (count == 1) ? "" : "s");
++
++ return status;
++fail:
++ while (count--)
++ kfree(ports[count].port);
++ put_tty_driver(gs_tty_driver);
++ gs_tty_driver = NULL;
++ return status;
++}
++
++static int gs_closed(struct gs_port *port)
++{
++ int cond;
++
++ spin_lock_irq(&port->port_lock);
++ cond = (port->open_count == 0) && !port->openclose;
++ spin_unlock_irq(&port->port_lock);
++ return cond;
++}
++
++/**
++ * gserial_cleanup - remove TTY-over-USB driver and devices
++ * Context: may sleep
++ *
++ * This is called to free all resources allocated by @gserial_setup().
++ * Accordingly, it may need to wait until some open /dev/ files have
++ * closed.
++ *
++ * The caller must have issued @gserial_disconnect() for any ports
++ * that had previously been connected, so that there is never any
++ * I/O pending when it's called.
++ */
++void gserial_cleanup(void)
++{
++ unsigned i;
++ struct gs_port *port;
++
++ /* start sysfs and /dev/ttyGS* node removal */
++ for (i = 0; i < n_ports; i++)
++ tty_unregister_device(gs_tty_driver, i);
++
++ for (i = 0; i < n_ports; i++) {
++ /* prevent new opens */
++ mutex_lock(&ports[i].lock);
++ port = ports[i].port;
++ ports[i].port = NULL;
++ mutex_unlock(&ports[i].lock);
++
++ /* wait for old opens to finish */
++ wait_event(port->close_wait, gs_closed(port));
++
++ WARN_ON(port->port_usb != NULL);
++
++ kfree(port);
++ }
++ n_ports = 0;
++
++ tty_unregister_driver(gs_tty_driver);
++ gs_tty_driver = NULL;
++
++ pr_debug("%s: cleaned up ttyGS* support\n", __func__);
++}
++
++/**
++ * gserial_connect - notify TTY I/O glue that USB link is active
++ * @gser: the function, set up with endpoints and descriptors
++ * @port_num: which port is active
++ * Context: any (usually from irq)
++ *
++ * This is called activate endpoints and let the TTY layer know that
++ * the connection is active ... not unlike "carrier detect". It won't
++ * necessarily start I/O queues; unless the TTY is held open by any
++ * task, there would be no point. However, the endpoints will be
++ * activated so the USB host can perform I/O, subject to basic USB
++ * hardware flow control.
++ *
++ * Caller needs to have set up the endpoints and USB function in @dev
++ * before calling this, as well as the appropriate (speed-specific)
++ * endpoint descriptors, and also have set up the TTY driver by calling
++ * @gserial_setup().
++ *
++ * Returns negative errno or zero.
++ * On success, ep->driver_data will be overwritten.
++ */
++int gserial_connect(struct gserial *gser, u8 port_num)
++{
++ struct gs_port *port;
++ unsigned long flags;
++ int status;
++
++ if (!gs_tty_driver || port_num >= n_ports)
++ return -ENXIO;
++
++ /* we "know" gserial_cleanup() hasn't been called */
++ port = ports[port_num].port;
++
++ /* activate the endpoints */
++ status = usb_ep_enable(gser->in, gser->in_desc);
++ if (status < 0)
++ return status;
++ gser->in->driver_data = port;
++
++ status = usb_ep_enable(gser->out, gser->out_desc);
++ if (status < 0)
++ goto fail_out;
++ gser->out->driver_data = port;
++
++ /* then tell the tty glue that I/O can work */
++ spin_lock_irqsave(&port->port_lock, flags);
++ gser->ioport = port;
++ port->port_usb = gser;
++
++ /* REVISIT unclear how best to handle this state...
++ * we don't really couple it with the Linux TTY.
++ */
++ gser->port_line_coding = port->port_line_coding;
++
++ /* REVISIT if waiting on "carrier detect", signal. */
++
++ /* REVISIT for ACM, issue "network connection" status notification:
++ * connected if open_count, else disconnected.
++ */
++
++ /* if it's already open, start I/O */
++ if (port->open_count) {
++ pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
++ gs_start_io(port);
++ }
++
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ return status;
++
++fail_out:
++ usb_ep_disable(gser->in);
++ gser->in->driver_data = NULL;
++ return status;
++}
++
++/**
++ * gserial_disconnect - notify TTY I/O glue that USB link is inactive
++ * @gser: the function, on which gserial_connect() was called
++ * Context: any (usually from irq)
++ *
++ * This is called to deactivate endpoints and let the TTY layer know
++ * that the connection went inactive ... not unlike "hangup".
++ *
++ * On return, the state is as if gserial_connect() had never been called;
++ * there is no active USB I/O on these endpoints.
++ */
++void gserial_disconnect(struct gserial *gser)
++{
++ struct gs_port *port = gser->ioport;
++ unsigned long flags;
++
++ if (!port)
++ return;
++
++ /* tell the TTY glue not to do I/O here any more */
++ spin_lock_irqsave(&port->port_lock, flags);
++
++ /* REVISIT as above: how best to track this? */
++ port->port_line_coding = gser->port_line_coding;
++
++ port->port_usb = NULL;
++ gser->ioport = NULL;
++ if (port->open_count > 0 || port->openclose) {
++ wake_up_interruptible(&port->drain_wait);
++ if (port->port_tty)
++ tty_hangup(port->port_tty);
++ }
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ /* disable endpoints, aborting down any active I/O */
++ usb_ep_disable(gser->out);
++ gser->out->driver_data = NULL;
++
++ usb_ep_disable(gser->in);
++ gser->in->driver_data = NULL;
++
++ /* finally, free any unused/unusable I/O buffers */
++ spin_lock_irqsave(&port->port_lock, flags);
++ if (port->open_count == 0 && !port->openclose)
++ gs_buf_free(&port->port_write_buf);
++ gs_free_requests(gser->out, &port->read_pool);
++ gs_free_requests(gser->in, &port->write_pool);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++}
+--- /dev/null
++++ b/drivers/usb/gadget/u_serial.h
+@@ -0,0 +1,51 @@
++#ifndef __U_SERIAL_H
++#define __U_SERIAL_H
++
++/* #include <linux/usb/composite.h> */
++#include <linux/usb/ch9.h>
++#include <linux/usb/gadget.h>
++
++#include <linux/usb/cdc.h>
++
++/*
++ * One non-multiplexed "serial" I/O port ... there can be several of these
++ * on any given USB peripheral device, if it provides enough endpoints.
++ *
++ * The "u_serial" utility module exists to do one thing: manage TTY style
++ * I/O using the USB peripheral endpoints listed here, including hookups
++ * to sysfs and /dev for each logical "tty" device.
++ *
++ * REVISIT need TTY --> USB event flow too, so ACM can report open/close
++ * as carrier detect events. There's other ACM state too...
++ *
++ * REVISIT someday, allow multiplexing several TTYs over these endpoints.
++ */
++struct gserial {
++ /* struct usb_function func; */
++
++ /* port is managed by gserial_{connect,disconnect} */
++ struct gs_port *ioport;
++
++ struct usb_ep *in;
++ struct usb_ep *out;
++ struct usb_endpoint_descriptor *in_desc;
++ struct usb_endpoint_descriptor *out_desc;
++
++ /* REVISIT avoid this CDC-ACM support harder ... */
++ struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
++
++ /* FIXME remove these when we switch to acm_bind_config... */
++ struct usb_ep *notify;
++ struct usb_endpoint_descriptor *notify_desc;
++ u16 port_handshake_bits;
++};
++
++/* port setup/teardown is handled by gadget driver */
++int gserial_setup(struct usb_gadget *g, unsigned n_ports);
++void gserial_cleanup(void);
++
++/* connect/disconnect is handled by individual functions */
++int gserial_connect(struct gserial *, u8 port_num);
++void gserial_disconnect(struct gserial *);
++
++#endif /* __U_SERIAL_H */
diff --git a/usb/usb-serial-gadget-use-new-tty-glue.patch b/usb/usb-serial-gadget-use-new-tty-glue.patch
new file mode 100644
index 00000000000000..d5f2c60f1c5cf9
--- /dev/null
+++ b/usb/usb-serial-gadget-use-new-tty-glue.patch
@@ -0,0 +1,1922 @@
+From david-b@pacbell.net Mon May 19 17:10:00 2008
+From: David Brownell <david-b@pacbell.net>
+Date: Fri, 16 May 2008 10:44:09 -0700
+Subject: usb serial gadget: use new tty glue
+To: linux-usb@vger.kernel.org
+Cc: Greg KH <greg@kroah.com>, Alan Cox <alan@lxorguk.ukuu.org.uk>
+Message-ID: <200805161044.09956.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+Teach "gadget serial" to use the new abstracted (and bugfixed) TTY glue,
+and remove all the orignal tangled-up code. Update the documentation
+accordingly. This is a net object code shrink and cleanup; it should
+make it a lot easier to see how the TTY glue should accomodate updates
+to the TTY layer, be bugfixed, etc.
+
+Notable behavior changes include: it can now support getty even when
+there's no USB connection; it fits properly into the mdev/udev world;
+and RX handling is better (throttling works, and low latency).
+
+Configurations with scripts setting up the /dev/ttygserial device node
+(with "experimental" major number) may want to change that to be a
+symlink pointing to the /dev/ttyGS0 file, as a migration aid; else,
+just switch entirely over to mdev/udev.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/usb/gadget_serial.txt | 35
+ drivers/usb/gadget/Makefile | 2
+ drivers/usb/gadget/serial.c | 1506 +++---------------------------------
+ 3 files changed, 160 insertions(+), 1383 deletions(-)
+
+--- a/Documentation/usb/gadget_serial.txt
++++ b/Documentation/usb/gadget_serial.txt
+@@ -1,6 +1,7 @@
+
+ Linux Gadget Serial Driver v2.0
+ 11/20/2004
++ (updated 8-May-2008 for v2.3)
+
+
+ License and Disclaimer
+@@ -31,7 +32,7 @@ Prerequisites
+ -------------
+ Versions of the gadget serial driver are available for the
+ 2.4 Linux kernels, but this document assumes you are using
+-version 2.0 or later of the gadget serial driver in a 2.6
++version 2.3 or later of the gadget serial driver in a 2.6
+ Linux kernel.
+
+ This document assumes that you are familiar with Linux and
+@@ -40,6 +41,12 @@ standard utilities, use minicom and Hype
+ USB and serial devices. It also assumes you configure the Linux
+ gadget and usb drivers as modules.
+
++With version 2.3 of the driver, major and minor device nodes are
++no longer statically defined. Your Linux based system should mount
++sysfs in /sys, and use "mdev" (in Busybox) or "udev" to make the
++/dev nodes matching the sysfs /sys/class/tty files.
++
++
+
+ Overview
+ --------
+@@ -104,15 +111,8 @@ driver. All this are listed under "USB
+ configuring the kernel. Then rebuild and install the kernel or
+ modules.
+
+-The gadget serial driver uses major number 127, for now. So you
+-will need to create a device node for it, like this:
+-
+- mknod /dev/ttygserial c 127 0
+-
+-You only need to do this once.
+-
+ Then you must load the gadget serial driver. To load it as an
+-ACM device, do this:
++ACM device (recommended for interoperability), do this:
+
+ modprobe g_serial use_acm=1
+
+@@ -125,6 +125,23 @@ controller driver. This must be done ea
+ side Linux system. You can add this to the start up scripts, if
+ desired.
+
++Your system should use mdev (from busybox) or udev to make the
++device nodes. After this gadget driver has been set up you should
++then see a /dev/ttyGS0 node:
++
++ # ls -l /dev/ttyGS0 | cat
++ crw-rw---- 1 root root 253, 0 May 8 14:10 /dev/ttyGS0
++ #
++
++Note that the major number (253, above) is system-specific. If
++you need to create /dev nodes by hand, the right numbers to use
++will be in the /sys/class/tty/ttyGS0/dev file.
++
++When you link this gadget driver early, perhaps even statically,
++you may want to set up an /etc/inittab entry to run "getty" on it.
++The /dev/ttyGS0 line should work like most any other serial port.
++
++
+ If gadget serial is loaded as an ACM device you will want to use
+ either the Windows or Linux ACM driver on the host side. If gadget
+ serial is loaded as a bulk in/out device, you will want to use the
+--- a/drivers/usb/gadget/Makefile
++++ b/drivers/usb/gadget/Makefile
+@@ -24,7 +24,7 @@ obj-$(CONFIG_USB_M66592) += m66592-udc.o
+ #
+ g_zero-objs := zero.o usbstring.o config.o epautoconf.o
+ g_ether-objs := ether.o usbstring.o config.o epautoconf.o
+-g_serial-objs := serial.o usbstring.o config.o epautoconf.o
++g_serial-objs := serial.o u_serial.o usbstring.o config.o epautoconf.o
+ g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o
+ gadgetfs-objs := inode.o
+ g_file_storage-objs := file_storage.o usbstring.o config.o \
+--- a/drivers/usb/gadget/serial.c
++++ b/drivers/usb/gadget/serial.c
+@@ -1,15 +1,8 @@
+ /*
+- * g_serial.c -- USB gadget serial driver
++ * serial.c -- USB gadget serial driver
+ *
+- * Copyright 2003 (C) Al Borchers (alborchers@steinerpoint.com)
+- *
+- * This code is based in part on the Gadget Zero driver, which
+- * is Copyright (C) 2003 by David Brownell, all rights reserved.
+- *
+- * This code also borrows from usbserial.c, which is
+- * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+- * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
+- * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
++ * Copyright (C) 2008 by David Brownell
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+@@ -22,23 +15,20 @@
+ #include <linux/tty.h>
+ #include <linux/tty_flip.h>
+
+-#include <linux/usb/ch9.h>
+-#include <linux/usb/cdc.h>
+-#include <linux/usb/gadget.h>
+-
++#include "u_serial.h"
+ #include "gadget_chips.h"
+
+
+ /* Defines */
+
+-#define GS_VERSION_STR "v2.2"
+-#define GS_VERSION_NUM 0x2200
++#define GS_VERSION_STR "v2.3"
++#define GS_VERSION_NUM 0x2300
+
+ #define GS_LONG_NAME "Gadget Serial"
+ #define GS_SHORT_NAME "g_serial"
+
+-#define GS_MAJOR 127
+-#define GS_MINOR_START 0
++#define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR
++
+
+ /* REVISIT only one port is supported for now;
+ * see gs_{send,recv}_packet() ... no multiplexing,
+@@ -58,23 +48,8 @@
+
+ #define GS_MAX_DESC_LEN 256
+
+-#define GS_DEFAULT_READ_Q_SIZE 32
+-#define GS_DEFAULT_WRITE_Q_SIZE 32
+-
+-#define GS_DEFAULT_WRITE_BUF_SIZE 8192
+-#define GS_TMP_BUF_SIZE 8192
+-
+-#define GS_CLOSE_TIMEOUT 15
+-
+ #define GS_DEFAULT_USE_ACM 0
+
+-/* 9600-8-N-1 ... matches init_termios.c_cflag and defaults
+- * expected by "usbser.sys" on MS-Windows.
+- */
+-#define GS_DEFAULT_DTE_RATE 9600
+-#define GS_DEFAULT_DATA_BITS 8
+-#define GS_DEFAULT_PARITY USB_CDC_NO_PARITY
+-#define GS_DEFAULT_CHAR_FORMAT USB_CDC_1_STOP_BITS
+
+ /* maxpacket and other transfer characteristics vary by speed. */
+ static inline struct usb_endpoint_descriptor *
+@@ -87,19 +62,6 @@ choose_ep_desc(struct usb_gadget *g, str
+ }
+
+
+-/* debug settings */
+-#ifdef DEBUG
+-static int debug = 1;
+-#else
+-#define debug 0
+-#endif
+-
+-#define gs_debug(format, arg...) \
+- do { if (debug) pr_debug(format, ## arg); } while (0)
+-#define gs_debug_level(level, format, arg...) \
+- do { if (debug >= level) pr_debug(format, ## arg); } while (0)
+-
+-
+ /* Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+@@ -112,63 +74,19 @@ static int debug = 1;
+ #define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
+ #define GS_NOTIFY_MAXPACKET 8
+
+-
+-/* circular buffer */
+-struct gs_buf {
+- unsigned int buf_size;
+- char *buf_buf;
+- char *buf_get;
+- char *buf_put;
+-};
+-
+-/* the port structure holds info for each port, one for each minor number */
+-struct gs_port {
+- struct gs_dev *port_dev; /* pointer to device struct */
+- struct tty_struct *port_tty; /* pointer to tty struct */
+- spinlock_t port_lock;
+- int port_num;
+- int port_open_count;
+- int port_in_use; /* open/close in progress */
+- wait_queue_head_t port_write_wait;/* waiting to write */
+- struct gs_buf *port_write_buf;
+- struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
+- u16 port_handshake_bits;
+-#define RS232_RTS (1 << 1)
+-#define RS232_DTE (1 << 0)
+-};
+-
+ /* the device structure holds info for the USB device */
+ struct gs_dev {
+ struct usb_gadget *dev_gadget; /* gadget device pointer */
+ spinlock_t dev_lock; /* lock for set/reset config */
+ int dev_config; /* configuration number */
+- struct usb_ep *dev_notify_ep; /* address of notify endpoint */
+- struct usb_ep *dev_in_ep; /* address of in endpoint */
+- struct usb_ep *dev_out_ep; /* address of out endpoint */
+- struct usb_endpoint_descriptor /* descriptor of notify ep */
+- *dev_notify_ep_desc;
+- struct usb_endpoint_descriptor /* descriptor of in endpoint */
+- *dev_in_ep_desc;
+- struct usb_endpoint_descriptor /* descriptor of out endpoint */
+- *dev_out_ep_desc;
+ struct usb_request *dev_ctrl_req; /* control request */
+- struct list_head dev_req_list; /* list of write requests */
+- int dev_sched_port; /* round robin port scheduled */
+- struct gs_port *dev_port[GS_NUM_PORTS]; /* the ports */
++
++ struct gserial gser; /* serial/tty port */
+ };
+
+
+ /* Functions */
+
+-/* tty driver internals */
+-static int gs_send(struct gs_dev *dev);
+-static int gs_send_packet(struct gs_dev *dev, char *packet,
+- unsigned int size);
+-static int gs_recv_packet(struct gs_dev *dev, char *packet,
+- unsigned int size);
+-static void gs_read_complete(struct usb_ep *ep, struct usb_request *req);
+-static void gs_write_complete(struct usb_ep *ep, struct usb_request *req);
+-
+ /* gadget driver internals */
+ static int gs_set_config(struct gs_dev *dev, unsigned config);
+ static void gs_reset_config(struct gs_dev *dev);
+@@ -179,28 +97,6 @@ static struct usb_request *gs_alloc_req(
+ gfp_t kmalloc_flags);
+ static void gs_free_req(struct usb_ep *ep, struct usb_request *req);
+
+-static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags);
+-static void gs_free_ports(struct gs_dev *dev);
+-
+-/* circular buffer */
+-static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags);
+-static void gs_buf_free(struct gs_buf *gb);
+-static void gs_buf_clear(struct gs_buf *gb);
+-static unsigned int gs_buf_data_avail(struct gs_buf *gb);
+-static unsigned int gs_buf_space_avail(struct gs_buf *gb);
+-static unsigned int gs_buf_put(struct gs_buf *gb, const char *buf,
+- unsigned int count);
+-static unsigned int gs_buf_get(struct gs_buf *gb, char *buf,
+- unsigned int count);
+-
+-
+-/* Globals */
+-
+-static struct gs_dev *gs_device;
+-
+-static struct mutex gs_open_close_lock[GS_NUM_PORTS];
+-
+-
+ /*-------------------------------------------------------------------------*/
+
+ /* USB descriptors */
+@@ -217,7 +113,7 @@ static struct mutex gs_open_close_lock[G
+ static char manufacturer[50];
+ static struct usb_string gs_strings[] = {
+ { GS_MANUFACTURER_STR_ID, manufacturer },
+- { GS_PRODUCT_STR_ID, GS_LONG_NAME },
++ { GS_PRODUCT_STR_ID, GS_VERSION_NAME },
+ { GS_BULK_CONFIG_STR_ID, "Gadget Serial Bulk" },
+ { GS_ACM_CONFIG_STR_ID, "Gadget Serial CDC ACM" },
+ { GS_CONTROL_STR_ID, "Gadget Serial Control" },
+@@ -376,818 +272,74 @@ static const struct usb_descriptor_heade
+ (struct usb_descriptor_header *) &gs_data_interface_desc,
+ (struct usb_descriptor_header *) &gs_fullspeed_in_desc,
+ (struct usb_descriptor_header *) &gs_fullspeed_out_desc,
+- NULL,
+-};
+-
+-static struct usb_endpoint_descriptor gs_highspeed_notify_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+- .bEndpointAddress = USB_DIR_IN,
+- .bmAttributes = USB_ENDPOINT_XFER_INT,
+- .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
+- .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
+-};
+-
+-static struct usb_endpoint_descriptor gs_highspeed_in_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .wMaxPacketSize = __constant_cpu_to_le16(512),
+-};
+-
+-static struct usb_endpoint_descriptor gs_highspeed_out_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .wMaxPacketSize = __constant_cpu_to_le16(512),
+-};
+-
+-static struct usb_qualifier_descriptor gs_qualifier_desc = {
+- .bLength = sizeof(struct usb_qualifier_descriptor),
+- .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+- .bcdUSB = __constant_cpu_to_le16 (0x0200),
+- /* assumes ep0 uses the same value for both speeds ... */
+- .bNumConfigurations = GS_NUM_CONFIGS,
+-};
+-
+-static const struct usb_descriptor_header *gs_bulk_highspeed_function[] = {
+- (struct usb_descriptor_header *) &gs_otg_descriptor,
+- (struct usb_descriptor_header *) &gs_bulk_interface_desc,
+- (struct usb_descriptor_header *) &gs_highspeed_in_desc,
+- (struct usb_descriptor_header *) &gs_highspeed_out_desc,
+- NULL,
+-};
+-
+-static const struct usb_descriptor_header *gs_acm_highspeed_function[] = {
+- (struct usb_descriptor_header *) &gs_otg_descriptor,
+- (struct usb_descriptor_header *) &gs_control_interface_desc,
+- (struct usb_descriptor_header *) &gs_header_desc,
+- (struct usb_descriptor_header *) &gs_call_mgmt_descriptor,
+- (struct usb_descriptor_header *) &gs_acm_descriptor,
+- (struct usb_descriptor_header *) &gs_union_desc,
+- (struct usb_descriptor_header *) &gs_highspeed_notify_desc,
+- (struct usb_descriptor_header *) &gs_data_interface_desc,
+- (struct usb_descriptor_header *) &gs_highspeed_in_desc,
+- (struct usb_descriptor_header *) &gs_highspeed_out_desc,
+- NULL,
+-};
+-
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* Module */
+-MODULE_DESCRIPTION(GS_LONG_NAME);
+-MODULE_AUTHOR("Al Borchers");
+-MODULE_LICENSE("GPL");
+-
+-#ifdef DEBUG
+-module_param(debug, int, S_IRUGO|S_IWUSR);
+-MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on");
+-#endif
+-
+-static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE;
+-module_param(read_q_size, uint, S_IRUGO);
+-MODULE_PARM_DESC(read_q_size, "Read request queue size, default=32");
+-
+-static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE;
+-module_param(write_q_size, uint, S_IRUGO);
+-MODULE_PARM_DESC(write_q_size, "Write request queue size, default=32");
+-
+-static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE;
+-module_param(write_buf_size, uint, S_IRUGO);
+-MODULE_PARM_DESC(write_buf_size, "Write buffer size, default=8192");
+-
+-static unsigned int use_acm = GS_DEFAULT_USE_ACM;
+-module_param(use_acm, uint, S_IRUGO);
+-MODULE_PARM_DESC(use_acm, "Use CDC ACM, 0=no, 1=yes, default=no");
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* TTY Driver */
+-
+-/*
+- * gs_open
+- */
+-static int gs_open(struct tty_struct *tty, struct file *file)
+-{
+- int port_num;
+- unsigned long flags;
+- struct gs_port *port;
+- struct gs_dev *dev;
+- struct gs_buf *buf;
+- struct mutex *mtx;
+- int ret;
+-
+- port_num = tty->index;
+-
+- gs_debug("gs_open: (%d,%p,%p)\n", port_num, tty, file);
+-
+- if (port_num < 0 || port_num >= GS_NUM_PORTS) {
+- pr_err("gs_open: (%d,%p,%p) invalid port number\n",
+- port_num, tty, file);
+- return -ENODEV;
+- }
+-
+- dev = gs_device;
+-
+- if (dev == NULL) {
+- pr_err("gs_open: (%d,%p,%p) NULL device pointer\n",
+- port_num, tty, file);
+- return -ENODEV;
+- }
+-
+- mtx = &gs_open_close_lock[port_num];
+- if (mutex_lock_interruptible(mtx)) {
+- pr_err("gs_open: (%d,%p,%p) interrupted waiting for mutex\n",
+- port_num, tty, file);
+- return -ERESTARTSYS;
+- }
+-
+- spin_lock_irqsave(&dev->dev_lock, flags);
+-
+- if (dev->dev_config == GS_NO_CONFIG_ID) {
+- pr_err("gs_open: (%d,%p,%p) device is not connected\n",
+- port_num, tty, file);
+- ret = -ENODEV;
+- goto exit_unlock_dev;
+- }
+-
+- port = dev->dev_port[port_num];
+-
+- if (port == NULL) {
+- pr_err("gs_open: (%d,%p,%p) NULL port pointer\n",
+- port_num, tty, file);
+- ret = -ENODEV;
+- goto exit_unlock_dev;
+- }
+-
+- spin_lock(&port->port_lock);
+- spin_unlock(&dev->dev_lock);
+-
+- if (port->port_dev == NULL) {
+- pr_err("gs_open: (%d,%p,%p) port disconnected (1)\n",
+- port_num, tty, file);
+- ret = -EIO;
+- goto exit_unlock_port;
+- }
+-
+- if (port->port_open_count > 0) {
+- ++port->port_open_count;
+- gs_debug("gs_open: (%d,%p,%p) already open\n",
+- port_num, tty, file);
+- ret = 0;
+- goto exit_unlock_port;
+- }
+-
+- tty->driver_data = NULL;
+-
+- /* mark port as in use, we can drop port lock and sleep if necessary */
+- port->port_in_use = 1;
+-
+- /* allocate write buffer on first open */
+- if (port->port_write_buf == NULL) {
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- buf = gs_buf_alloc(write_buf_size, GFP_KERNEL);
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- /* might have been disconnected while asleep, check */
+- if (port->port_dev == NULL) {
+- pr_err("gs_open: (%d,%p,%p) port disconnected (2)\n",
+- port_num, tty, file);
+- port->port_in_use = 0;
+- ret = -EIO;
+- goto exit_unlock_port;
+- }
+-
+- if ((port->port_write_buf=buf) == NULL) {
+- pr_err("gs_open: (%d,%p,%p) cannot allocate "
+- "port write buffer\n",
+- port_num, tty, file);
+- port->port_in_use = 0;
+- ret = -ENOMEM;
+- goto exit_unlock_port;
+- }
+-
+- }
+-
+- /* wait for carrier detect (not implemented) */
+-
+- /* might have been disconnected while asleep, check */
+- if (port->port_dev == NULL) {
+- pr_err("gs_open: (%d,%p,%p) port disconnected (3)\n",
+- port_num, tty, file);
+- port->port_in_use = 0;
+- ret = -EIO;
+- goto exit_unlock_port;
+- }
+-
+- tty->driver_data = port;
+- port->port_tty = tty;
+- port->port_open_count = 1;
+- port->port_in_use = 0;
+-
+- gs_debug("gs_open: (%d,%p,%p) completed\n", port_num, tty, file);
+-
+- ret = 0;
+-
+-exit_unlock_port:
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- mutex_unlock(mtx);
+- return ret;
+-
+-exit_unlock_dev:
+- spin_unlock_irqrestore(&dev->dev_lock, flags);
+- mutex_unlock(mtx);
+- return ret;
+-
+-}
+-
+-/*
+- * gs_close
+- */
+-
+-static int gs_write_finished_event_safely(struct gs_port *p)
+-{
+- int cond;
+-
+- spin_lock_irq(&(p)->port_lock);
+- cond = !(p)->port_dev || !gs_buf_data_avail((p)->port_write_buf);
+- spin_unlock_irq(&(p)->port_lock);
+- return cond;
+-}
+-
+-static void gs_close(struct tty_struct *tty, struct file *file)
+-{
+- struct gs_port *port = tty->driver_data;
+- struct mutex *mtx;
+-
+- if (port == NULL) {
+- pr_err("gs_close: NULL port pointer\n");
+- return;
+- }
+-
+- gs_debug("gs_close: (%d,%p,%p)\n", port->port_num, tty, file);
+-
+- mtx = &gs_open_close_lock[port->port_num];
+- mutex_lock(mtx);
+-
+- spin_lock_irq(&port->port_lock);
+-
+- if (port->port_open_count == 0) {
+- pr_err("gs_close: (%d,%p,%p) port is already closed\n",
+- port->port_num, tty, file);
+- goto exit;
+- }
+-
+- if (port->port_open_count > 1) {
+- --port->port_open_count;
+- goto exit;
+- }
+-
+- /* free disconnected port on final close */
+- if (port->port_dev == NULL) {
+- kfree(port);
+- goto exit;
+- }
+-
+- /* mark port as closed but in use, we can drop port lock */
+- /* and sleep if necessary */
+- port->port_in_use = 1;
+- port->port_open_count = 0;
+-
+- /* wait for write buffer to drain, or */
+- /* at most GS_CLOSE_TIMEOUT seconds */
+- if (gs_buf_data_avail(port->port_write_buf) > 0) {
+- spin_unlock_irq(&port->port_lock);
+- wait_event_interruptible_timeout(port->port_write_wait,
+- gs_write_finished_event_safely(port),
+- GS_CLOSE_TIMEOUT * HZ);
+- spin_lock_irq(&port->port_lock);
+- }
+-
+- /* free disconnected port on final close */
+- /* (might have happened during the above sleep) */
+- if (port->port_dev == NULL) {
+- kfree(port);
+- goto exit;
+- }
+-
+- gs_buf_clear(port->port_write_buf);
+-
+- tty->driver_data = NULL;
+- port->port_tty = NULL;
+- port->port_in_use = 0;
+-
+- gs_debug("gs_close: (%d,%p,%p) completed\n",
+- port->port_num, tty, file);
+-
+-exit:
+- spin_unlock_irq(&port->port_lock);
+- mutex_unlock(mtx);
+-}
+-
+-/*
+- * gs_write
+- */
+-static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
+-{
+- unsigned long flags;
+- struct gs_port *port = tty->driver_data;
+- int ret;
+-
+- if (port == NULL) {
+- pr_err("gs_write: NULL port pointer\n");
+- return -EIO;
+- }
+-
+- gs_debug("gs_write: (%d,%p) writing %d bytes\n", port->port_num, tty,
+- count);
+-
+- if (count == 0)
+- return 0;
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_dev == NULL) {
+- pr_err("gs_write: (%d,%p) port is not connected\n",
+- port->port_num, tty);
+- ret = -EIO;
+- goto exit;
+- }
+-
+- if (port->port_open_count == 0) {
+- pr_err("gs_write: (%d,%p) port is closed\n",
+- port->port_num, tty);
+- ret = -EBADF;
+- goto exit;
+- }
+-
+- count = gs_buf_put(port->port_write_buf, buf, count);
+-
+- spin_unlock_irqrestore(&port->port_lock, flags);
+-
+- gs_send(gs_device);
+-
+- gs_debug("gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty,
+- count);
+-
+- return count;
+-
+-exit:
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- return ret;
+-}
+-
+-/*
+- * gs_put_char
+- */
+-static int gs_put_char(struct tty_struct *tty, unsigned char ch)
+-{
+- unsigned long flags;
+- struct gs_port *port = tty->driver_data;
+- int ret = 0;
+-
+- if (port == NULL) {
+- pr_err("gs_put_char: NULL port pointer\n");
+- return 0;
+- }
+-
+- gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p\n",
+- port->port_num, tty, ch, __builtin_return_address(0));
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_dev == NULL) {
+- pr_err("gs_put_char: (%d,%p) port is not connected\n",
+- port->port_num, tty);
+- goto exit;
+- }
+-
+- if (port->port_open_count == 0) {
+- pr_err("gs_put_char: (%d,%p) port is closed\n",
+- port->port_num, tty);
+- goto exit;
+- }
+-
+- ret = gs_buf_put(port->port_write_buf, &ch, 1);
+-
+-exit:
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- return ret;
+-}
+-
+-/*
+- * gs_flush_chars
+- */
+-static void gs_flush_chars(struct tty_struct *tty)
+-{
+- unsigned long flags;
+- struct gs_port *port = tty->driver_data;
+-
+- if (port == NULL) {
+- pr_err("gs_flush_chars: NULL port pointer\n");
+- return;
+- }
+-
+- gs_debug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_dev == NULL) {
+- pr_err("gs_flush_chars: (%d,%p) port is not connected\n",
+- port->port_num, tty);
+- goto exit;
+- }
+-
+- if (port->port_open_count == 0) {
+- pr_err("gs_flush_chars: (%d,%p) port is closed\n",
+- port->port_num, tty);
+- goto exit;
+- }
+-
+- spin_unlock_irqrestore(&port->port_lock, flags);
+-
+- gs_send(gs_device);
+-
+- return;
+-
+-exit:
+- spin_unlock_irqrestore(&port->port_lock, flags);
+-}
+-
+-/*
+- * gs_write_room
+- */
+-static int gs_write_room(struct tty_struct *tty)
+-{
+-
+- int room = 0;
+- unsigned long flags;
+- struct gs_port *port = tty->driver_data;
+-
+-
+- if (port == NULL)
+- return 0;
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_dev != NULL && port->port_open_count > 0
+- && port->port_write_buf != NULL)
+- room = gs_buf_space_avail(port->port_write_buf);
+-
+- spin_unlock_irqrestore(&port->port_lock, flags);
+-
+- gs_debug("gs_write_room: (%d,%p) room=%d\n",
+- port->port_num, tty, room);
+-
+- return room;
+-}
+-
+-/*
+- * gs_chars_in_buffer
+- */
+-static int gs_chars_in_buffer(struct tty_struct *tty)
+-{
+- int chars = 0;
+- unsigned long flags;
+- struct gs_port *port = tty->driver_data;
+-
+- if (port == NULL)
+- return 0;
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_dev != NULL && port->port_open_count > 0
+- && port->port_write_buf != NULL)
+- chars = gs_buf_data_avail(port->port_write_buf);
+-
+- spin_unlock_irqrestore(&port->port_lock, flags);
+-
+- gs_debug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
+- port->port_num, tty, chars);
+-
+- return chars;
+-}
+-
+-/*
+- * gs_throttle
+- */
+-static void gs_throttle(struct tty_struct *tty)
+-{
+-}
+-
+-/*
+- * gs_unthrottle
+- */
+-static void gs_unthrottle(struct tty_struct *tty)
+-{
+-}
+-
+-/*
+- * gs_break
+- */
+-static void gs_break(struct tty_struct *tty, int break_state)
+-{
+-}
+-
+-/*
+- * gs_ioctl
+- */
+-static int gs_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+-{
+- struct gs_port *port = tty->driver_data;
+-
+- if (port == NULL) {
+- pr_err("gs_ioctl: NULL port pointer\n");
+- return -EIO;
+- }
+-
+- gs_debug("gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n",
+- port->port_num, tty, file, cmd, arg);
+-
+- /* handle ioctls */
+-
+- /* could not handle ioctl */
+- return -ENOIOCTLCMD;
+-}
+-
+-/*
+- * gs_set_termios
+- */
+-static void gs_set_termios(struct tty_struct *tty, struct ktermios *old)
+-{
+-}
+-
+-static const struct tty_operations gs_tty_ops = {
+- .open = gs_open,
+- .close = gs_close,
+- .write = gs_write,
+- .put_char = gs_put_char,
+- .flush_chars = gs_flush_chars,
+- .write_room = gs_write_room,
+- .ioctl = gs_ioctl,
+- .set_termios = gs_set_termios,
+- .throttle = gs_throttle,
+- .unthrottle = gs_unthrottle,
+- .break_ctl = gs_break,
+- .chars_in_buffer = gs_chars_in_buffer,
+-};
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/*
+-* gs_send
+-*
+-* This function finds available write requests, calls
+-* gs_send_packet to fill these packets with data, and
+-* continues until either there are no more write requests
+-* available or no more data to send. This function is
+-* run whenever data arrives or write requests are available.
+-*/
+-static int gs_send(struct gs_dev *dev)
+-{
+- int ret,len;
+- unsigned long flags;
+- struct usb_ep *ep;
+- struct usb_request *req;
+-
+- if (dev == NULL) {
+- pr_err("gs_send: NULL device pointer\n");
+- return -ENODEV;
+- }
+-
+- spin_lock_irqsave(&dev->dev_lock, flags);
+-
+- ep = dev->dev_in_ep;
+-
+- while(!list_empty(&dev->dev_req_list)) {
+-
+- req = list_entry(dev->dev_req_list.next,
+- struct usb_request, list);
+-
+- len = gs_send_packet(dev, req->buf, ep->maxpacket);
+-
+- if (len > 0) {
+- gs_debug_level(3, "gs_send: len=%d, 0x%2.2x "
+- "0x%2.2x 0x%2.2x ...\n", len,
+- *((unsigned char *)req->buf),
+- *((unsigned char *)req->buf+1),
+- *((unsigned char *)req->buf+2));
+- list_del(&req->list);
+- req->length = len;
+- spin_unlock_irqrestore(&dev->dev_lock, flags);
+- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
+- pr_err(
+- "gs_send: cannot queue read request, ret=%d\n",
+- ret);
+- spin_lock_irqsave(&dev->dev_lock, flags);
+- break;
+- }
+- spin_lock_irqsave(&dev->dev_lock, flags);
+- } else {
+- break;
+- }
+-
+- }
+-
+- spin_unlock_irqrestore(&dev->dev_lock, flags);
+-
+- return 0;
+-}
+-
+-/*
+- * gs_send_packet
+- *
+- * If there is data to send, a packet is built in the given
+- * buffer and the size is returned. If there is no data to
+- * send, 0 is returned. If there is any error a negative
+- * error number is returned.
+- *
+- * Called during USB completion routine, on interrupt time.
+- *
+- * We assume that disconnect will not happen until all completion
+- * routines have completed, so we can assume that the dev_port
+- * array does not change during the lifetime of this function.
+- */
+-static int gs_send_packet(struct gs_dev *dev, char *packet, unsigned int size)
+-{
+- unsigned int len;
+- struct gs_port *port;
+-
+- /* TEMPORARY -- only port 0 is supported right now */
+- port = dev->dev_port[0];
+-
+- if (port == NULL) {
+- pr_err("gs_send_packet: port=%d, NULL port pointer\n", 0);
+- return -EIO;
+- }
+-
+- spin_lock(&port->port_lock);
+-
+- len = gs_buf_data_avail(port->port_write_buf);
+- if (len < size)
+- size = len;
+-
+- if (size == 0)
+- goto exit;
+-
+- size = gs_buf_get(port->port_write_buf, packet, size);
+-
+- if (port->port_tty)
+- wake_up_interruptible(&port->port_tty->write_wait);
+-
+-exit:
+- spin_unlock(&port->port_lock);
+- return size;
+-}
+-
+-/*
+- * gs_recv_packet
+- *
+- * Called for each USB packet received. Reads the packet
+- * header and stuffs the data in the appropriate tty buffer.
+- * Returns 0 if successful, or a negative error number.
+- *
+- * Called during USB completion routine, on interrupt time.
+- *
+- * We assume that disconnect will not happen until all completion
+- * routines have completed, so we can assume that the dev_port
+- * array does not change during the lifetime of this function.
+- */
+-static int gs_recv_packet(struct gs_dev *dev, char *packet, unsigned int size)
+-{
+- unsigned int len;
+- struct gs_port *port;
+- int ret;
+- struct tty_struct *tty;
+-
+- /* TEMPORARY -- only port 0 is supported right now */
+- port = dev->dev_port[0];
+-
+- if (port == NULL) {
+- pr_err("gs_recv_packet: port=%d, NULL port pointer\n",
+- port->port_num);
+- return -EIO;
+- }
+-
+- spin_lock(&port->port_lock);
+-
+- if (port->port_open_count == 0) {
+- pr_err("gs_recv_packet: port=%d, port is closed\n",
+- port->port_num);
+- ret = -EIO;
+- goto exit;
+- }
+-
+-
+- tty = port->port_tty;
+-
+- if (tty == NULL) {
+- pr_err("gs_recv_packet: port=%d, NULL tty pointer\n",
+- port->port_num);
+- ret = -EIO;
+- goto exit;
+- }
+-
+- if (port->port_tty->magic != TTY_MAGIC) {
+- pr_err("gs_recv_packet: port=%d, bad tty magic\n",
+- port->port_num);
+- ret = -EIO;
+- goto exit;
+- }
+-
+- len = tty_buffer_request_room(tty, size);
+- if (len > 0) {
+- tty_insert_flip_string(tty, packet, len);
+- tty_flip_buffer_push(port->port_tty);
+- wake_up_interruptible(&port->port_tty->read_wait);
+- }
+- ret = 0;
+-exit:
+- spin_unlock(&port->port_lock);
+- return ret;
+-}
+-
+-/*
+-* gs_read_complete
+-*/
+-static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
+-{
+- int ret;
+- struct gs_dev *dev = ep->driver_data;
+-
+- if (dev == NULL) {
+- pr_err("gs_read_complete: NULL device pointer\n");
+- return;
+- }
+-
+- switch(req->status) {
+- case 0:
+- /* normal completion */
+- gs_recv_packet(dev, req->buf, req->actual);
+-requeue:
+- req->length = ep->maxpacket;
+- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
+- pr_err(
+- "gs_read_complete: cannot queue read request, ret=%d\n",
+- ret);
+- }
+- break;
++ NULL,
++};
+
+- case -ESHUTDOWN:
+- /* disconnect */
+- gs_debug("gs_read_complete: shutdown\n");
+- gs_free_req(ep, req);
+- break;
++static struct usb_endpoint_descriptor gs_highspeed_notify_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
++ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
++};
+
+- default:
+- /* unexpected */
+- pr_err(
+- "gs_read_complete: unexpected status error, status=%d\n",
+- req->status);
+- goto requeue;
+- break;
+- }
+-}
++static struct usb_endpoint_descriptor gs_highspeed_in_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
+
+-/*
+-* gs_write_complete
+-*/
+-static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
+-{
+- struct gs_dev *dev = ep->driver_data;
++static struct usb_endpoint_descriptor gs_highspeed_out_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
+
+- if (dev == NULL) {
+- pr_err("gs_write_complete: NULL device pointer\n");
+- return;
+- }
++static struct usb_qualifier_descriptor gs_qualifier_desc = {
++ .bLength = sizeof(struct usb_qualifier_descriptor),
++ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
++ .bcdUSB = __constant_cpu_to_le16 (0x0200),
++ /* assumes ep0 uses the same value for both speeds ... */
++ .bNumConfigurations = GS_NUM_CONFIGS,
++};
+
+- switch(req->status) {
+- case 0:
+- /* normal completion */
+-requeue:
+- spin_lock(&dev->dev_lock);
+- list_add(&req->list, &dev->dev_req_list);
+- spin_unlock(&dev->dev_lock);
++static const struct usb_descriptor_header *gs_bulk_highspeed_function[] = {
++ (struct usb_descriptor_header *) &gs_otg_descriptor,
++ (struct usb_descriptor_header *) &gs_bulk_interface_desc,
++ (struct usb_descriptor_header *) &gs_highspeed_in_desc,
++ (struct usb_descriptor_header *) &gs_highspeed_out_desc,
++ NULL,
++};
++
++static const struct usb_descriptor_header *gs_acm_highspeed_function[] = {
++ (struct usb_descriptor_header *) &gs_otg_descriptor,
++ (struct usb_descriptor_header *) &gs_control_interface_desc,
++ (struct usb_descriptor_header *) &gs_header_desc,
++ (struct usb_descriptor_header *) &gs_call_mgmt_descriptor,
++ (struct usb_descriptor_header *) &gs_acm_descriptor,
++ (struct usb_descriptor_header *) &gs_union_desc,
++ (struct usb_descriptor_header *) &gs_highspeed_notify_desc,
++ (struct usb_descriptor_header *) &gs_data_interface_desc,
++ (struct usb_descriptor_header *) &gs_highspeed_in_desc,
++ (struct usb_descriptor_header *) &gs_highspeed_out_desc,
++ NULL,
++};
+
+- gs_send(dev);
+
+- break;
++/*-------------------------------------------------------------------------*/
+
+- case -ESHUTDOWN:
+- /* disconnect */
+- gs_debug("gs_write_complete: shutdown\n");
+- gs_free_req(ep, req);
+- break;
++/* Module */
++MODULE_DESCRIPTION(GS_VERSION_NAME);
++MODULE_AUTHOR("Al Borchers");
++MODULE_AUTHOR("David Brownell");
++MODULE_LICENSE("GPL");
+
+- default:
+- pr_err(
+- "gs_write_complete: unexpected status error, status=%d\n",
+- req->status);
+- goto requeue;
+- break;
+- }
+-}
++static unsigned int use_acm = GS_DEFAULT_USE_ACM;
++module_param(use_acm, uint, S_IRUGO);
++MODULE_PARM_DESC(use_acm, "Use CDC ACM, 0=no, 1=yes, default=no");
+
+ /*-------------------------------------------------------------------------*/
+
+@@ -1199,12 +351,10 @@ requeue:
+ * Called on module unload. Frees the control request and device
+ * structure.
+ */
+-static void /* __init_or_exit */ gs_unbind(struct usb_gadget *gadget)
++static void __exit gs_unbind(struct usb_gadget *gadget)
+ {
+ struct gs_dev *dev = get_gadget_data(gadget);
+
+- gs_device = NULL;
+-
+ /* read/write requests already freed, only control request remains */
+ if (dev != NULL) {
+ if (dev->dev_ctrl_req != NULL) {
+@@ -1212,13 +362,13 @@ static void /* __init_or_exit */ gs_unbi
+ dev->dev_ctrl_req = NULL;
+ }
+ gs_reset_config(dev);
+- gs_free_ports(dev);
+ kfree(dev);
+ set_gadget_data(gadget, NULL);
+ }
+
+- pr_info("gs_unbind: %s %s unbound\n", GS_LONG_NAME,
+- GS_VERSION_STR);
++ pr_info("gs_unbind: %s unbound\n", GS_VERSION_NAME);
++
++ gserial_cleanup();
+ }
+
+ /*
+@@ -1234,6 +384,10 @@ static int __init gs_bind(struct usb_gad
+ struct gs_dev *dev;
+ int gcnum;
+
++ ret = gserial_setup(gadget, GS_NUM_PORTS);
++ if (ret < 0)
++ return ret;
++
+ /* Some controllers can't support CDC ACM:
+ * - sh doesn't support multiple interfaces or configs;
+ * - sa1100 doesn't have a third interrupt endpoint
+@@ -1254,21 +408,24 @@ static int __init gs_bind(struct usb_gad
+ }
+
+ dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL);
+- if (dev == NULL)
+- return -ENOMEM;
++ if (dev == NULL) {
++ ret = -ENOMEM;
++ goto autoconf_fail;
++ }
+
+ usb_ep_autoconfig_reset(gadget);
++ ret = -ENXIO;
+
+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc);
+ if (!ep)
+ goto autoconf_fail;
+- dev->dev_in_ep = ep;
++ dev->gser.in = ep;
+ ep->driver_data = dev; /* claim the endpoint */
+
+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc);
+ if (!ep)
+ goto autoconf_fail;
+- dev->dev_out_ep = ep;
++ dev->gser.out = ep;
+ ep->driver_data = dev; /* claim the endpoint */
+
+ if (use_acm) {
+@@ -1279,7 +436,7 @@ static int __init gs_bind(struct usb_gad
+ }
+ gs_device_desc.idProduct = __constant_cpu_to_le16(
+ GS_CDC_PRODUCT_ID),
+- dev->dev_notify_ep = ep;
++ dev->gser.notify = ep;
+ ep->driver_data = dev; /* claim the endpoint */
+ }
+
+@@ -1310,41 +467,32 @@ static int __init gs_bind(struct usb_gad
+ gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+- gs_device = dev;
+-
+ snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
+ init_utsname()->sysname, init_utsname()->release,
+ gadget->name);
+
+ dev->dev_gadget = gadget;
+ spin_lock_init(&dev->dev_lock);
+- INIT_LIST_HEAD(&dev->dev_req_list);
+ set_gadget_data(gadget, dev);
+
+- if ((ret=gs_alloc_ports(dev, GFP_KERNEL)) != 0) {
+- pr_err("gs_bind: cannot allocate ports\n");
+- gs_unbind(gadget);
+- return ret;
+- }
+-
+ /* preallocate control response and buffer */
+ dev->dev_ctrl_req = gs_alloc_req(gadget->ep0, GS_MAX_DESC_LEN,
+ GFP_KERNEL);
+ if (dev->dev_ctrl_req == NULL) {
+- gs_unbind(gadget);
+- return -ENOMEM;
++ ret = -ENOMEM;
++ goto autoconf_fail;
+ }
+ gadget->ep0->driver_data = dev;
+
+- pr_info("gs_bind: %s %s bound\n",
+- GS_LONG_NAME, GS_VERSION_STR);
++ pr_info("gs_bind: %s bound\n", GS_VERSION_NAME);
+
+ return 0;
+
+ autoconf_fail:
+ kfree(dev);
+- pr_err("gs_bind: cannot autoconfigure on %s\n", gadget->name);
+- return -ENODEV;
++ gserial_cleanup();
++ pr_err("gs_bind: to %s, err %d\n", gadget->name, ret);
++ return ret;
+ }
+
+ static int gs_setup_standard(struct usb_gadget *gadget,
+@@ -1435,15 +583,14 @@ static int gs_setup_standard(struct usb_
+ }
+ if (dev->dev_config != GS_BULK_CONFIG_ID
+ && wIndex == GS_CONTROL_INTERFACE_ID) {
+- if (dev->dev_notify_ep) {
+- usb_ep_disable(dev->dev_notify_ep);
+- usb_ep_enable(dev->dev_notify_ep, dev->dev_notify_ep_desc);
++ if (dev->gser.notify) {
++ usb_ep_disable(dev->gser.notify);
++ usb_ep_enable(dev->gser.notify,
++ dev->gser.notify_desc);
+ }
+ } else {
+- usb_ep_disable(dev->dev_in_ep);
+- usb_ep_disable(dev->dev_out_ep);
+- usb_ep_enable(dev->dev_in_ep, dev->dev_in_ep_desc);
+- usb_ep_enable(dev->dev_out_ep, dev->dev_out_ep_desc);
++ gserial_connect(&dev->gser, 0);
++ gserial_disconnect(&dev->gser);
+ }
+ ret = 0;
+ set_interface_done:
+@@ -1480,23 +627,22 @@ static void gs_setup_complete_set_line_c
+ struct usb_request *req)
+ {
+ struct gs_dev *dev = ep->driver_data;
+- struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */
+
+ switch (req->status) {
+ case 0:
+ /* normal completion */
+- if (req->actual != sizeof(port->port_line_coding))
++ if (req->actual != sizeof(dev->gser.port_line_coding))
+ usb_ep_set_halt(ep);
+- else if (port) {
++ else {
+ struct usb_cdc_line_coding *value = req->buf;
+
+ /* REVISIT: we currently just remember this data.
+ * If we change that, (a) validate it first, then
+ * (b) update whatever hardware needs updating.
+ */
+- spin_lock(&port->port_lock);
+- port->port_line_coding = *value;
+- spin_unlock(&port->port_lock);
++ spin_lock(&dev->dev_lock);
++ dev->gser.port_line_coding = *value;
++ spin_unlock(&dev->dev_lock);
+ }
+ break;
+
+@@ -1517,7 +663,6 @@ static int gs_setup_class(struct usb_gad
+ {
+ int ret = -EOPNOTSUPP;
+ struct gs_dev *dev = get_gadget_data(gadget);
+- struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */
+ struct usb_request *req = dev->dev_ctrl_req;
+ u16 wIndex = le16_to_cpu(ctrl->wIndex);
+ u16 wValue = le16_to_cpu(ctrl->wValue);
+@@ -1533,26 +678,22 @@ static int gs_setup_class(struct usb_gad
+
+ case USB_CDC_REQ_GET_LINE_CODING:
+ ret = min_t(int, wLength, sizeof(struct usb_cdc_line_coding));
+- if (port) {
+- spin_lock(&port->port_lock);
+- memcpy(req->buf, &port->port_line_coding, ret);
+- spin_unlock(&port->port_lock);
+- }
++ spin_lock(&dev->dev_lock);
++ memcpy(req->buf, &dev->gser.port_line_coding, ret);
++ spin_unlock(&dev->dev_lock);
+ break;
+
+ case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+ if (wLength != 0)
+ break;
+ ret = 0;
+- if (port) {
+- /* REVISIT: we currently just remember this data.
+- * If we change that, update whatever hardware needs
+- * updating.
+- */
+- spin_lock(&port->port_lock);
+- port->port_handshake_bits = wValue;
+- spin_unlock(&port->port_lock);
+- }
++ /* REVISIT: we currently just remember this data.
++ * If we change that, update whatever hardware needs
++ * updating.
++ */
++ spin_lock(&dev->dev_lock);
++ dev->gser.port_handshake_bits = wValue;
++ spin_unlock(&dev->dev_lock);
+ break;
+
+ default:
+@@ -1654,17 +795,7 @@ static void gs_disconnect(struct usb_gad
+ struct gs_dev *dev = get_gadget_data(gadget);
+
+ spin_lock_irqsave(&dev->dev_lock, flags);
+-
+ gs_reset_config(dev);
+-
+- /* free closed ports and disconnect open ports */
+- /* (open ports will be freed when closed) */
+- gs_free_ports(dev);
+-
+- /* re-allocate ports for the next connection */
+- if (gs_alloc_ports(dev, GFP_ATOMIC) != 0)
+- pr_err("gs_disconnect: cannot re-allocate ports\n");
+-
+ spin_unlock_irqrestore(&dev->dev_lock, flags);
+
+ pr_info("gs_disconnect: %s disconnected\n", GS_LONG_NAME);
+@@ -1698,17 +829,8 @@ static struct usb_gadget_driver gs_gadge
+ */
+ static int gs_set_config(struct gs_dev *dev, unsigned config)
+ {
+- int i;
+ int ret = 0;
+ struct usb_gadget *gadget = dev->dev_gadget;
+- struct usb_ep *ep;
+- struct usb_endpoint_descriptor *out, *in, *notify;
+- struct usb_request *req;
+-
+- if (dev == NULL) {
+- pr_err("gs_set_config: NULL device pointer\n");
+- return 0;
+- }
+
+ if (config == dev->dev_config)
+ return 0;
+@@ -1730,85 +852,37 @@ static int gs_set_config(struct gs_dev *
+ return -EINVAL;
+ }
+
+- in = choose_ep_desc(gadget,
++ dev->gser.in_desc = choose_ep_desc(gadget,
+ &gs_highspeed_in_desc,
+ &gs_fullspeed_in_desc);
+- out = choose_ep_desc(gadget,
++ dev->gser.out_desc = choose_ep_desc(gadget,
+ &gs_highspeed_out_desc,
+ &gs_fullspeed_out_desc);
+- notify = dev->dev_notify_ep
++ dev->gser.notify_desc = dev->gser.notify
+ ? choose_ep_desc(gadget,
+ &gs_highspeed_notify_desc,
+ &gs_fullspeed_notify_desc)
+ : NULL;
+
+- ret = usb_ep_enable(dev->dev_in_ep, in);
+- if (ret == 0) {
+- dev->dev_in_ep_desc = in;
+- } else {
+- pr_debug("%s: cannot enable %s %s, ret=%d\n",
+- __func__, "IN", dev->dev_in_ep->name, ret);
+- return ret;
+- }
+-
+- ret = usb_ep_enable(dev->dev_out_ep, out);
+- if (ret == 0) {
+- dev->dev_out_ep_desc = out;
+- } else {
+- pr_debug("%s: cannot enable %s %s, ret=%d\n",
+- __func__, "OUT", dev->dev_out_ep->name, ret);
+-fail0:
+- usb_ep_disable(dev->dev_in_ep);
+- return ret;
++ /* only support one "serial" port for now */
++ if (dev->gser.notify) {
++ ret = usb_ep_enable(dev->gser.notify, dev->gser.notify_desc);
++ if (ret < 0)
++ return ret;
++ dev->gser.notify->driver_data = dev;
+ }
+
+- if (notify) {
+- ret = usb_ep_enable(dev->dev_notify_ep, notify);
+- if (ret == 0) {
+- dev->dev_notify_ep_desc = notify;
+- } else {
+- pr_debug("%s: cannot enable %s %s, ret=%d\n",
+- __func__, "NOTIFY",
+- dev->dev_notify_ep->name, ret);
+- usb_ep_disable(dev->dev_out_ep);
+- goto fail0;
++ ret = gserial_connect(&dev->gser, 0);
++ if (ret < 0) {
++ if (dev->gser.notify) {
++ usb_ep_disable(dev->gser.notify);
++ dev->gser.notify->driver_data = NULL;
+ }
++ return ret;
+ }
+
+ dev->dev_config = config;
+
+- /* allocate and queue read requests */
+- ep = dev->dev_out_ep;
+- for (i=0; i<read_q_size && ret == 0; i++) {
+- if ((req=gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC))) {
+- req->complete = gs_read_complete;
+- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
+- pr_err("gs_set_config: cannot queue read "
+- "request, ret=%d\n", ret);
+- }
+- } else {
+- pr_err("gs_set_config: cannot allocate "
+- "read requests\n");
+- ret = -ENOMEM;
+- goto exit_reset_config;
+- }
+- }
+-
+- /* allocate write requests, and put on free list */
+- ep = dev->dev_in_ep;
+- for (i=0; i<write_q_size; i++) {
+- req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
+- if (req) {
+- req->complete = gs_write_complete;
+- list_add(&req->list, &dev->dev_req_list);
+- } else {
+- pr_err("gs_set_config: cannot allocate "
+- "write requests\n");
+- ret = -ENOMEM;
+- goto exit_reset_config;
+- }
+- }
+-
+ /* REVISIT the ACM mode should be able to actually *issue* some
+ * notifications, for at least serial state change events if
+ * not also for network connection; say so in bmCapabilities.
+@@ -1820,10 +894,6 @@ fail0:
+ config == GS_BULK_CONFIG_ID ? "BULK" : "CDC-ACM");
+
+ return 0;
+-
+-exit_reset_config:
+- gs_reset_config(dev);
+- return ret;
+ }
+
+ /*
+@@ -1838,32 +908,16 @@ exit_reset_config:
+ */
+ static void gs_reset_config(struct gs_dev *dev)
+ {
+- struct usb_request *req;
+-
+- if (dev == NULL) {
+- pr_err("gs_reset_config: NULL device pointer\n");
+- return;
+- }
+-
+ if (dev->dev_config == GS_NO_CONFIG_ID)
+ return;
+
+ dev->dev_config = GS_NO_CONFIG_ID;
+
+- /* free write requests on the free list */
+- while(!list_empty(&dev->dev_req_list)) {
+- req = list_entry(dev->dev_req_list.next,
+- struct usb_request, list);
+- list_del(&req->list);
+- gs_free_req(dev->dev_in_ep, req);
++ gserial_disconnect(&dev->gser);
++ if (dev->gser.notify) {
++ usb_ep_disable(dev->gser.notify);
++ dev->gser.notify->driver_data = NULL;
+ }
+-
+- /* disable endpoints, forcing completion of pending i/o; */
+- /* completion handlers free their requests in this case */
+- if (dev->dev_notify_ep)
+- usb_ep_disable(dev->dev_notify_ep);
+- usb_ep_disable(dev->dev_in_ep);
+- usb_ep_disable(dev->dev_out_ep);
+ }
+
+ /*
+@@ -1956,254 +1010,8 @@ static void gs_free_req(struct usb_ep *e
+ }
+ }
+
+-/*
+- * gs_alloc_ports
+- *
+- * Allocate all ports and set the gs_dev struct to point to them.
+- * Return 0 if successful, or a negative error number.
+- *
+- * The device lock is normally held when calling this function.
+- */
+-static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags)
+-{
+- int i;
+- struct gs_port *port;
+-
+- if (dev == NULL)
+- return -EIO;
+-
+- for (i=0; i<GS_NUM_PORTS; i++) {
+- if ((port=kzalloc(sizeof(struct gs_port), kmalloc_flags)) == NULL)
+- return -ENOMEM;
+-
+- port->port_dev = dev;
+- port->port_num = i;
+- port->port_line_coding.dwDTERate = cpu_to_le32(GS_DEFAULT_DTE_RATE);
+- port->port_line_coding.bCharFormat = GS_DEFAULT_CHAR_FORMAT;
+- port->port_line_coding.bParityType = GS_DEFAULT_PARITY;
+- port->port_line_coding.bDataBits = GS_DEFAULT_DATA_BITS;
+- spin_lock_init(&port->port_lock);
+- init_waitqueue_head(&port->port_write_wait);
+-
+- dev->dev_port[i] = port;
+- }
+-
+- return 0;
+-}
+-
+-/*
+- * gs_free_ports
+- *
+- * Free all closed ports. Open ports are disconnected by
+- * freeing their write buffers, setting their device pointers
+- * and the pointers to them in the device to NULL. These
+- * ports will be freed when closed.
+- *
+- * The device lock is normally held when calling this function.
+- */
+-static void gs_free_ports(struct gs_dev *dev)
+-{
+- int i;
+- unsigned long flags;
+- struct gs_port *port;
+-
+- if (dev == NULL)
+- return;
+-
+- for (i=0; i<GS_NUM_PORTS; i++) {
+- if ((port=dev->dev_port[i]) != NULL) {
+- dev->dev_port[i] = NULL;
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_write_buf != NULL) {
+- gs_buf_free(port->port_write_buf);
+- port->port_write_buf = NULL;
+- }
+-
+- if (port->port_open_count > 0 || port->port_in_use) {
+- port->port_dev = NULL;
+- wake_up_interruptible(&port->port_write_wait);
+- if (port->port_tty) {
+- tty_hangup(port->port_tty);
+- }
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- } else {
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- kfree(port);
+- }
+-
+- }
+- }
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* Circular Buffer */
+-
+-/*
+- * gs_buf_alloc
+- *
+- * Allocate a circular buffer and all associated memory.
+- */
+-static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags)
+-{
+- struct gs_buf *gb;
+-
+- if (size == 0)
+- return NULL;
+-
+- gb = kmalloc(sizeof(struct gs_buf), kmalloc_flags);
+- if (gb == NULL)
+- return NULL;
+-
+- gb->buf_buf = kmalloc(size, kmalloc_flags);
+- if (gb->buf_buf == NULL) {
+- kfree(gb);
+- return NULL;
+- }
+-
+- gb->buf_size = size;
+- gb->buf_get = gb->buf_put = gb->buf_buf;
+-
+- return gb;
+-}
+-
+-/*
+- * gs_buf_free
+- *
+- * Free the buffer and all associated memory.
+- */
+-static void gs_buf_free(struct gs_buf *gb)
+-{
+- if (gb) {
+- kfree(gb->buf_buf);
+- kfree(gb);
+- }
+-}
+-
+-/*
+- * gs_buf_clear
+- *
+- * Clear out all data in the circular buffer.
+- */
+-static void gs_buf_clear(struct gs_buf *gb)
+-{
+- if (gb != NULL)
+- gb->buf_get = gb->buf_put;
+- /* equivalent to a get of all data available */
+-}
+-
+-/*
+- * gs_buf_data_avail
+- *
+- * Return the number of bytes of data available in the circular
+- * buffer.
+- */
+-static unsigned int gs_buf_data_avail(struct gs_buf *gb)
+-{
+- if (gb != NULL)
+- return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
+- else
+- return 0;
+-}
+-
+-/*
+- * gs_buf_space_avail
+- *
+- * Return the number of bytes of space available in the circular
+- * buffer.
+- */
+-static unsigned int gs_buf_space_avail(struct gs_buf *gb)
+-{
+- if (gb != NULL)
+- return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
+- else
+- return 0;
+-}
+-
+-/*
+- * gs_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
+-gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count)
+-{
+- unsigned int len;
+-
+- if (gb == NULL)
+- return 0;
+-
+- len = gs_buf_space_avail(gb);
+- if (count > len)
+- count = len;
+-
+- if (count == 0)
+- return 0;
+-
+- len = gb->buf_buf + gb->buf_size - gb->buf_put;
+- if (count > len) {
+- memcpy(gb->buf_put, buf, len);
+- memcpy(gb->buf_buf, buf+len, count - len);
+- gb->buf_put = gb->buf_buf + count - len;
+- } else {
+- memcpy(gb->buf_put, buf, count);
+- if (count < len)
+- gb->buf_put += count;
+- else /* count == len */
+- gb->buf_put = gb->buf_buf;
+- }
+-
+- return count;
+-}
+-
+-/*
+- * gs_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
+-gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count)
+-{
+- unsigned int len;
+-
+- if (gb == NULL)
+- return 0;
+-
+- len = gs_buf_data_avail(gb);
+- if (count > len)
+- count = len;
+-
+- if (count == 0)
+- return 0;
+-
+- len = gb->buf_buf + gb->buf_size - gb->buf_get;
+- if (count > len) {
+- memcpy(buf, gb->buf_get, len);
+- memcpy(buf+len, gb->buf_buf, count - len);
+- gb->buf_get = gb->buf_buf + count - len;
+- } else {
+- memcpy(buf, gb->buf_get, count);
+- if (count < len)
+- gb->buf_get += count;
+- else /* count == len */
+- gb->buf_get = gb->buf_buf;
+- }
+-
+- return count;
+-}
+-
+ /*-------------------------------------------------------------------------*/
+
+-static struct tty_driver *gs_tty_driver;
+-
+ /*
+ * gs_module_init
+ *
+@@ -2211,50 +1019,7 @@ static struct tty_driver *gs_tty_driver;
+ */
+ static int __init gs_module_init(void)
+ {
+- int i;
+- int retval;
+-
+- retval = usb_gadget_register_driver(&gs_gadget_driver);
+- if (retval) {
+- pr_err("gs_module_init: cannot register gadget driver, "
+- "ret=%d\n", retval);
+- return retval;
+- }
+-
+- gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS);
+- if (!gs_tty_driver)
+- return -ENOMEM;
+- gs_tty_driver->owner = THIS_MODULE;
+- gs_tty_driver->driver_name = GS_SHORT_NAME;
+- gs_tty_driver->name = "ttygs";
+- gs_tty_driver->major = GS_MAJOR;
+- gs_tty_driver->minor_start = GS_MINOR_START;
+- gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+- gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+- gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+- gs_tty_driver->init_termios = tty_std_termios;
+- /* must match GS_DEFAULT_DTE_RATE and friends */
+- gs_tty_driver->init_termios.c_cflag =
+- B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+- gs_tty_driver->init_termios.c_ispeed = GS_DEFAULT_DTE_RATE;
+- gs_tty_driver->init_termios.c_ospeed = GS_DEFAULT_DTE_RATE;
+- tty_set_operations(gs_tty_driver, &gs_tty_ops);
+-
+- for (i = 0; i < GS_NUM_PORTS; i++)
+- mutex_init(&gs_open_close_lock[i]);
+-
+- retval = tty_register_driver(gs_tty_driver);
+- if (retval) {
+- usb_gadget_unregister_driver(&gs_gadget_driver);
+- put_tty_driver(gs_tty_driver);
+- pr_err("gs_module_init: cannot register tty driver, "
+- "ret=%d\n", retval);
+- return retval;
+- }
+-
+- pr_info("gs_module_init: %s %s loaded\n",
+- GS_LONG_NAME, GS_VERSION_STR);
+- return 0;
++ return usb_gadget_register_driver(&gs_gadget_driver);
+ }
+ module_init(gs_module_init);
+
+@@ -2265,11 +1030,6 @@ module_init(gs_module_init);
+ */
+ static void __exit gs_module_exit(void)
+ {
+- tty_unregister_driver(gs_tty_driver);
+- put_tty_driver(gs_tty_driver);
+ usb_gadget_unregister_driver(&gs_gadget_driver);
+-
+- pr_info("gs_module_exit: %s %s unloaded\n",
+- GS_LONG_NAME, GS_VERSION_STR);
+ }
+ module_exit(gs_module_exit);
diff --git a/version b/version
index 0ed462948c3cc3..4c14bf45c5cb8c 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-2.6.26-rc2-git5
+2.6.26-rc3