diff options
-rw-r--r-- | driver-core/debugfs-add-a-reference-to-the-debugfs-api-documentation.patch | 30 | ||||
-rw-r--r-- | driver-core/driver-core-remove-kobj_name_len-define.patch | 65 | ||||
-rw-r--r-- | driver-core/uio-convert-uio-to-fasync_unlocked.patch | 33 | ||||
-rw-r--r-- | ldp/framebuffer-add-the-via-framebuffer-driver.patch | 2 | ||||
-rw-r--r-- | series | 10 | ||||
-rw-r--r-- | usb.current/usb-serial-ch341-new-vid-pid-for-ch341-usb-serial.patch | 33 | ||||
-rw-r--r-- | usb/usb-gotemp.patch | 70 | ||||
-rw-r--r-- | usb/usb-remove-cvs-keywords.patch | 417 | ||||
-rw-r--r-- | usb/usb-remove-documentation-usb-uhci.txt.patch | 188 | ||||
-rw-r--r-- | usb/usb-rndis-switch-to-seq_files.patch | 122 | ||||
-rw-r--r-- | usb/usb-serial-gadget-modular-tty-glue.patch | 1377 | ||||
-rw-r--r-- | usb/usb-serial-gadget-use-new-tty-glue.patch | 1922 | ||||
-rw-r--r-- | version | 2 |
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. @@ -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); @@ -1 +1 @@ -2.6.26-rc2-git5 +2.6.26-rc3 |