aboutsummaryrefslogtreecommitdiffstats
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2008-05-08 16:26:30 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2008-05-08 16:26:30 -0700
commite8593fcdd1bd4fc1992f065951ae4545cf7e6cd5 (patch)
treeb4fd81d204d736e8019d5c334a3fe172948a5d0e
parentd1250c4948996b6ff72db22e92acf4167ae6308f (diff)
downloadpatches-e8593fcdd1bd4fc1992f065951ae4545cf7e6cd5.tar.gz
more patches added
-rw-r--r--driver-core.next/kobject-fix-kobject_rename-and-config_sysfs.patch124
-rw-r--r--driver-core/kobject-replace-with-in-name.patch42
-rw-r--r--driver-core/read-dev_name-instead-of-bus_id.patch8
-rw-r--r--driver-core/sysfs-add-sys-dev-char-block-to-lookup-sysfs-path-by-major-minor.patch30
-rw-r--r--driver-core/warn-when-statically-allocated-kobjects-are-used.patch2
-rw-r--r--series9
-rw-r--r--usb.next/usb-serial-gadget-cleanup-reorg.patch783
-rw-r--r--usb.next/usb-serial-gadget-descriptor-cleanup.patch66
-rw-r--r--usb.next/usb-serial-gadget-remove-needless-data-structure.patch198
-rw-r--r--usb.next/usb-serial-gadget-simplify-endpoint-handling.patch254
-rw-r--r--usb.next/usb-unusual_devs-add-support-for-gi-0401-sd-card-interface.patch93
-rw-r--r--usb/usb-implement-soft-unbinding.patch83
-rw-r--r--usb/usb-storage-implement-soft-unbinding.patch260
13 files changed, 1932 insertions, 20 deletions
diff --git a/driver-core.next/kobject-fix-kobject_rename-and-config_sysfs.patch b/driver-core.next/kobject-fix-kobject_rename-and-config_sysfs.patch
new file mode 100644
index 00000000000000..9d3f4540d36ec6
--- /dev/null
+++ b/driver-core.next/kobject-fix-kobject_rename-and-config_sysfs.patch
@@ -0,0 +1,124 @@
+From ebiederm@xmission.com Thu May 8 14:50:04 2008
+From: Eric W. Biederman <ebiederm@xmission.com>
+Date: Thu, 08 May 2008 14:41:00 -0700
+Subject: kobject: Fix kobject_rename and !CONFIG_SYSFS
+To: Greg KH <gregkh@suse.de>, Andrew Morton <akpm@linux-foundation.org>
+Cc: Benjamin Thery <benjamin.thery@bull.net>, linux-kernel@vger.kernel.org, Tejun Heo <htejun@gmail.com>, Al Viro <viro@ftp.linux.org.uk>, Daniel Lezcano <dlezcano@fr.ibm.com>, "Serge E. Hallyn" <serue@us.ibm.com>, Pavel Emelyanov <xemul@openvz.org>, netdev@vger.kernel.org
+Message-ID: <m163tocw43.fsf_-_@frodo.ebiederm.org>
+
+
+From: Eric W. Biederman <ebiederm@xmission.com>
+
+When looking at kobject_rename I found two bugs with
+that exist when sysfs support is disabled in the kernel.
+
+kobject_rename does not change the name on the kobject when
+sysfs support is not compiled in.
+
+kobject_rename without locking attempts to check the
+validity of a rename operation, which the kobject layer
+simply does not have the infrastructure to do so.
+
+This patch documents the previously unstated requirement of
+kobject_rename that is the responsibility of the caller to
+provide mutual exclusion and to be certain that the new_name
+for the kobject is valid.
+
+This patch modifies sysfs_rename_dir in !CONFIG_SYSFS case
+to call kobject_set_name to actually change the kobject_name.
+
+This patch removes the bogus and misleading check in kobject_rename
+that attempts to see if a rename is valid. The check is bogus
+because we do not have the proper locking. The check is misleading
+because it looks like we can and do perform useful checking at the
+kobject level that we don't.
+
+The users in net/core/dev.c already have all of the necessary checks
+in place and don't care. The other use in net/core/wireless.c
+as originally implemented is incorrect because it performs no locking
+and a simple patch has been sent that adds the necessary locking
+and sanity checks there. Ensuring this patch will not have an
+effect on users of kobject_rename or device_rename.
+
+Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/kobject.txt | 4 ++++
+ drivers/base/core.c | 5 +++++
+ include/linux/sysfs.h | 2 +-
+ lib/kobject.c | 18 +++++-------------
+ 4 files changed, 15 insertions(+), 14 deletions(-)
+
+--- a/Documentation/kobject.txt
++++ b/Documentation/kobject.txt
+@@ -118,6 +118,10 @@ the name of the kobject, call kobject_re
+
+ int kobject_rename(struct kobject *kobj, const char *new_name);
+
++Note kobject_rename does perform any locking or have a solid notion of
++what names are valid so the provide must provide their own sanity checking
++and serialization.
++
+ There is a function called kobject_set_name() but that is legacy cruft and
+ is being removed. If your code needs to call this function, it is
+ incorrect and needs to be fixed.
+--- a/drivers/base/core.c
++++ b/drivers/base/core.c
+@@ -1171,6 +1171,11 @@ EXPORT_SYMBOL_GPL(device_destroy);
+ * device_rename - renames a device
+ * @dev: the pointer to the struct device to be renamed
+ * @new_name: the new name of the device
++ *
++ * It is the responsibility of the caller to provide mutual
++ * exclusion between two different calls of device_rename
++ * on the same device to ensure that new_name is valid and
++ * won't conflict with other devices.
+ */
+ int device_rename(struct device *dev, char *new_name)
+ {
+--- a/include/linux/sysfs.h
++++ b/include/linux/sysfs.h
+@@ -137,7 +137,7 @@ static inline void sysfs_remove_dir(stru
+
+ static inline int sysfs_rename_dir(struct kobject *kobj, const char *new_name)
+ {
+- return 0;
++ return kobject_set_name(kobj, "%s", new_name);
+ }
+
+ static inline int sysfs_move_dir(struct kobject *kobj,
+--- a/lib/kobject.c
++++ b/lib/kobject.c
+@@ -383,6 +383,11 @@ EXPORT_SYMBOL_GPL(kobject_init_and_add);
+ * kobject_rename - change the name of an object
+ * @kobj: object in question.
+ * @new_name: object's new name
++ *
++ * It is the responsibility of the caller to provide mutual
++ * exclusion between two different calls of kobject_rename
++ * on the same kobject and to ensure that new_name is valid and
++ * won't conflict with other kobjects.
+ */
+ int kobject_rename(struct kobject *kobj, const char *new_name)
+ {
+@@ -397,19 +402,6 @@ int kobject_rename(struct kobject *kobj,
+ if (!kobj->parent)
+ return -EINVAL;
+
+- /* see if this name is already in use */
+- if (kobj->kset) {
+- struct kobject *temp_kobj;
+- temp_kobj = kset_find_obj(kobj->kset, new_name);
+- if (temp_kobj) {
+- printk(KERN_WARNING "kobject '%s' cannot be renamed "
+- "to '%s' as '%s' is already in existence.\n",
+- kobject_name(kobj), new_name, new_name);
+- kobject_put(temp_kobj);
+- return -EINVAL;
+- }
+- }
+-
+ devpath = kobject_get_path(kobj, GFP_KERNEL);
+ if (!devpath) {
+ error = -ENOMEM;
diff --git a/driver-core/kobject-replace-with-in-name.patch b/driver-core/kobject-replace-with-in-name.patch
new file mode 100644
index 00000000000000..780cebd7168055
--- /dev/null
+++ b/driver-core/kobject-replace-with-in-name.patch
@@ -0,0 +1,42 @@
+From kay.sievers@vrfy.org Thu May 8 16:05:54 2008
+From: Kay Sievers <kay.sievers@vrfy.org>
+Date: Tue, 06 May 2008 22:24:04 +0200
+Subject: kobject: replace '/' with '!' in name
+Message-ID: <1210105444.1653.17.camel@linux.site>
+
+
+Some (block) devices have a '/' in the name, and need special
+handling. Let's have that rule to the core, so we can remove it
+from the block class.
+
+Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ lib/kobject.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+--- a/lib/kobject.c
++++ b/lib/kobject.c
+@@ -216,13 +216,19 @@ static int kobject_add_internal(struct k
+ static int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
+ va_list vargs)
+ {
+- /* Free the old name, if necessary. */
+- kfree(kobj->name);
++ const char *old_name = kobj->name;
++ char *s;
+
+ kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
+ if (!kobj->name)
+ return -ENOMEM;
+
++ /* ewww... some of these buggers have '/' in the name ... */
++ s = strchr(kobj->name, '/');
++ if (s)
++ s[0] = '!';
++
++ kfree(old_name);
+ return 0;
+ }
+
diff --git a/driver-core/read-dev_name-instead-of-bus_id.patch b/driver-core/read-dev_name-instead-of-bus_id.patch
index 0d2b00c92bfa69..9d7e234d708bd2 100644
--- a/driver-core/read-dev_name-instead-of-bus_id.patch
+++ b/driver-core/read-dev_name-instead-of-bus_id.patch
@@ -820,7 +820,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
kfree(dev);
}
-@@ -1261,7 +1260,7 @@ int device_rename(struct device *dev, ch
+@@ -1266,7 +1265,7 @@ int device_rename(struct device *dev, ch
if (!dev)
return -EINVAL;
@@ -829,7 +829,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
__func__, new_name);
#ifdef CONFIG_SYSFS_DEPRECATED
-@@ -1274,7 +1273,7 @@ int device_rename(struct device *dev, ch
+@@ -1279,7 +1278,7 @@ int device_rename(struct device *dev, ch
error = -ENOMEM;
goto out;
}
@@ -838,7 +838,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
strlcpy(dev->bus_id, new_name, BUS_ID_SIZE);
error = kobject_rename(&dev->kobj, new_name);
-@@ -1298,7 +1297,7 @@ int device_rename(struct device *dev, ch
+@@ -1303,7 +1302,7 @@ int device_rename(struct device *dev, ch
if (dev->class) {
sysfs_remove_link(&dev->class->subsys.kobj, old_device_name);
error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
@@ -847,7 +847,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
if (error) {
dev_err(dev, "%s: sysfs_create_symlink failed (%d)\n",
__func__, error);
-@@ -1376,8 +1375,8 @@ int device_move(struct device *dev, stru
+@@ -1381,8 +1380,8 @@ int device_move(struct device *dev, stru
new_parent = get_device(new_parent);
new_parent_kobj = get_device_parent(dev, new_parent);
diff --git a/driver-core/sysfs-add-sys-dev-char-block-to-lookup-sysfs-path-by-major-minor.patch b/driver-core/sysfs-add-sys-dev-char-block-to-lookup-sysfs-path-by-major-minor.patch
index 5a0f11415e98e1..5c4b457dc3f377 100644
--- a/driver-core/sysfs-add-sys-dev-char-block-to-lookup-sysfs-path-by-major-minor.patch
+++ b/driver-core/sysfs-add-sys-dev-char-block-to-lookup-sysfs-path-by-major-minor.patch
@@ -50,6 +50,20 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
include/linux/device.h | 3 +
7 files changed, 124 insertions(+), 2 deletions(-)
+--- a/block/genhd.c
++++ b/block/genhd.c
+@@ -368,7 +368,10 @@ static struct kobject *base_probe(dev_t
+
+ static int __init genhd_device_init(void)
+ {
+- int error = class_register(&block_class);
++ int error;
++
++ block_class.dev_kobj = sysfs_dev_block_kobj;
++ error = class_register(&block_class);
+ if (unlikely(error))
+ return error;
+ bdev_map = kobj_map_init(base_probe, &block_class_lock);
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-dev
@@ -0,0 +1,20 @@
@@ -95,20 +109,6 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
More information can driver-model specific features can be found in
Documentation/driver-model/.
---- a/block/genhd.c
-+++ b/block/genhd.c
-@@ -368,7 +368,10 @@ static struct kobject *base_probe(dev_t
-
- static int __init genhd_device_init(void)
- {
-- int error = class_register(&block_class);
-+ int error;
-+
-+ block_class.dev_kobj = sysfs_dev_block_kobj;
-+ error = class_register(&block_class);
- if (unlikely(error))
- return error;
- bdev_map = kobj_map_init(base_probe, &block_class_lock);
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -149,6 +149,10 @@ int class_register(struct class *cls)
@@ -248,7 +248,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
}
EXPORT_SYMBOL_GPL(device_for_each_child);
-@@ -1360,4 +1438,7 @@ void device_shutdown(void)
+@@ -1365,4 +1443,7 @@ void device_shutdown(void)
dev->driver->shutdown(dev);
}
}
diff --git a/driver-core/warn-when-statically-allocated-kobjects-are-used.patch b/driver-core/warn-when-statically-allocated-kobjects-are-used.patch
index eb904808ccf78f..66bd2dde247c0f 100644
--- a/driver-core/warn-when-statically-allocated-kobjects-are-used.patch
+++ b/driver-core/warn-when-statically-allocated-kobjects-are-used.patch
@@ -129,7 +129,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
/*
* populate_dir - populate directory with attributes.
-@@ -278,6 +329,7 @@ void kobject_init(struct kobject *kobj,
+@@ -284,6 +335,7 @@ void kobject_init(struct kobject *kobj,
"object, something is seriously wrong.\n", kobj);
dump_stack();
}
diff --git a/series b/series
index 6eb6cc8e62d3c9..72a9f606ad20da 100644
--- a/series
+++ b/series
@@ -12,6 +12,7 @@ gregkh/detect-atomic-counter-underflows.patch
# Driver core patches for 2.6.26
#################################
driver-core.next/block-do_mounts-accept-root-non-existant-partition.patch
+driver-core.next/kobject-fix-kobject_rename-and-config_sysfs.patch
#################################
# USB patches for 2.6.26
@@ -30,6 +31,11 @@ usb.next/usb-c67x00-build-fix.patch
usb.next/usb-do-not-handle-device-1410-5010-in-option-driver.patch
usb.next/usb-fix-compile-warning-in-isp1760.patch
usb.next/usb-use-get_unaligned_-helpers-for-kl5kusb105-driver.patch
+usb.next/usb-serial-gadget-cleanup-reorg.patch
+usb.next/usb-serial-gadget-remove-needless-data-structure.patch
+usb.next/usb-serial-gadget-simplify-endpoint-handling.patch
+usb.next/usb-serial-gadget-descriptor-cleanup.patch
+usb.next/usb-unusual_devs-add-support-for-gi-0401-sd-card-interface.patch
#####################################################################
@@ -40,6 +46,7 @@ usb.next/usb-use-get_unaligned_-helpers-for-kl5kusb105-driver.patch
# Driver core patches for after 2.6.26 is out
#################################
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/net-convert-the-phy_device-file-to-use-bus_find_device_by_name.patch
@@ -79,6 +86,8 @@ usb/usb-io_ti-first-cut-at-a-big-clean-up.patch
usb/usb-storage-separate-dynamic-flags-from-fixed-flags.patch
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
# wireless usb, still a work in progress
diff --git a/usb.next/usb-serial-gadget-cleanup-reorg.patch b/usb.next/usb-serial-gadget-cleanup-reorg.patch
new file mode 100644
index 00000000000000..641353010dd942
--- /dev/null
+++ b/usb.next/usb-serial-gadget-cleanup-reorg.patch
@@ -0,0 +1,783 @@
+From linux-usb-owner@vger.kernel.org Wed May 7 16:02:19 2008
+From: David Brownell <david-b@pacbell.net>
+Date: Wed, 7 May 2008 16:00:36 -0700
+Subject: USB: serial gadget: cleanup/reorg
+To: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Cc: Greg KH <greg@kroah.com>, linux-usb@vger.kernel.org, Al Borchers <alborchers@steinerpoint.com>
+Message-ID: <200805071600.37154.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+Some cleanup/reorg of g_serial ... simplifying it, and disentangling
+its structure so morphing it into a "function" driver (combinable with
+other interfaces) should be less painful.
+
+ - Remove most forward declarations
+ * put tty and gadget driver structs after their contents
+ * snug module init/exit decls next to their functions
+ * reordered some functions
+
+ - Other cleanup:
+ * convert a funky macro to an inline function
+ * snug up module params next to their declarations
+ * add missing driver.owner
+ * add separator lines between major driver sections
+
+ - Add comments re potential parameter/#define changes:
+ * only supports one port (shrank GS_NUM_PORTS)
+ * changing from 9600-8-N-1 affects multiple sites
+
+ - Remove net2280-specific optimization ... it was being done
+ way too late, can be done by net2280 module options, and in
+ any case doesn't matter at any sane serial data rates.
+
+There are no behavioral changes, but the macro thing saves I-space.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Cc: Al Borchers <alborchers@steinerpoint.com>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/usb/gadget/serial.c | 553 ++++++++++++++++++++------------------------
+ 1 file changed, 263 insertions(+), 290 deletions(-)
+
+--- a/drivers/usb/gadget/serial.c
++++ b/drivers/usb/gadget/serial.c
+@@ -14,7 +14,6 @@
+ * 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>
+@@ -41,7 +40,11 @@
+ #define GS_MAJOR 127
+ #define GS_MINOR_START 0
+
+-#define GS_NUM_PORTS 16
++/* REVISIT only one port is supported for now;
++ * see gs_{send,recv}_packet() ... no multiplexing,
++ * and no support for multiple ACM devices.
++ */
++#define GS_NUM_PORTS 1
+
+ #define GS_NUM_CONFIGS 1
+ #define GS_NO_CONFIG_ID 0
+@@ -65,6 +68,9 @@
+
+ #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
+@@ -107,10 +113,6 @@ static int debug = 1;
+ #define GS_NOTIFY_MAXPACKET 8
+
+
+-/* Structures */
+-
+-struct gs_dev;
+-
+ /* circular buffer */
+ struct gs_buf {
+ unsigned int buf_size;
+@@ -164,26 +166,7 @@ struct gs_dev {
+
+ /* Functions */
+
+-/* module */
+-static int __init gs_module_init(void);
+-static void __exit gs_module_exit(void);
+-
+-/* tty driver */
+-static int gs_open(struct tty_struct *tty, struct file *file);
+-static void gs_close(struct tty_struct *tty, struct file *file);
+-static int gs_write(struct tty_struct *tty,
+- const unsigned char *buf, int count);
+-static int gs_put_char(struct tty_struct *tty, unsigned char ch);
+-static void gs_flush_chars(struct tty_struct *tty);
+-static int gs_write_room(struct tty_struct *tty);
+-static int gs_chars_in_buffer(struct tty_struct *tty);
+-static void gs_throttle(struct tty_struct * tty);
+-static void gs_unthrottle(struct tty_struct * tty);
+-static void gs_break(struct tty_struct *tty, int break_state);
+-static int gs_ioctl(struct tty_struct *tty, struct file *file,
+- unsigned int cmd, unsigned long arg);
+-static void gs_set_termios(struct tty_struct *tty, struct ktermios *old);
+-
++/* 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);
+@@ -192,19 +175,7 @@ static int gs_recv_packet(struct gs_dev
+ 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 */
+-static int gs_bind(struct usb_gadget *gadget);
+-static void gs_unbind(struct usb_gadget *gadget);
+-static int gs_setup(struct usb_gadget *gadget,
+- const struct usb_ctrlrequest *ctrl);
+-static int gs_setup_standard(struct usb_gadget *gadget,
+- const struct usb_ctrlrequest *ctrl);
+-static int gs_setup_class(struct usb_gadget *gadget,
+- const struct usb_ctrlrequest *ctrl);
+-static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req);
+-static void gs_setup_complete_set_line_coding(struct usb_ep *ep,
+- struct usb_request *req);
+-static void gs_disconnect(struct usb_gadget *gadget);
++/* gadget driver internals */
+ static int gs_set_config(struct gs_dev *dev, unsigned config);
+ static void gs_reset_config(struct gs_dev *dev);
+ static int gs_build_config_buf(u8 *buf, struct usb_gadget *g,
+@@ -232,9 +203,6 @@ static unsigned int gs_buf_put(struct gs
+ static unsigned int gs_buf_get(struct gs_buf *gb, char *buf,
+ unsigned int count);
+
+-/* external functions */
+-extern int net2280_set_fifo_mode(struct usb_gadget *gadget, int mode);
+-
+
+ /* Globals */
+
+@@ -246,48 +214,8 @@ static const char *EP_NOTIFY_NAME;
+
+ static struct mutex gs_open_close_lock[GS_NUM_PORTS];
+
+-static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE;
+-static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE;
+-
+-static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE;
+-
+-static unsigned int use_acm = GS_DEFAULT_USE_ACM;
+-
+-
+-/* tty driver struct */
+-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,
+-};
+-static struct tty_driver *gs_tty_driver;
+-
+-/* gadget driver struct */
+-static struct usb_gadget_driver gs_gadget_driver = {
+-#ifdef CONFIG_USB_GADGET_DUALSPEED
+- .speed = USB_SPEED_HIGH,
+-#else
+- .speed = USB_SPEED_FULL,
+-#endif /* CONFIG_USB_GADGET_DUALSPEED */
+- .function = GS_LONG_NAME,
+- .bind = gs_bind,
+- .unbind = gs_unbind,
+- .setup = gs_setup,
+- .disconnect = gs_disconnect,
+- .driver = {
+- .name = GS_SHORT_NAME,
+- },
+-};
+
++/*-------------------------------------------------------------------------*/
+
+ /* USB descriptors */
+
+@@ -521,6 +449,8 @@ static const struct usb_descriptor_heade
+ };
+
+
++/*-------------------------------------------------------------------------*/
++
+ /* Module */
+ MODULE_DESCRIPTION(GS_LONG_NAME);
+ MODULE_AUTHOR("Al Borchers");
+@@ -531,84 +461,23 @@ 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");
+
+-module_init(gs_module_init);
+-module_exit(gs_module_exit);
+-
+-/*
+-* gs_module_init
+-*
+-* Register as a USB gadget driver and a 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;
+- gs_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+- 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;
+-}
+-
+-/*
+-* gs_module_exit
+-*
+-* Unregister as a tty driver and a USB gadget driver.
+-*/
+-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);
+-}
++/*-------------------------------------------------------------------------*/
+
+ /* TTY Driver */
+
+@@ -753,15 +622,15 @@ exit_unlock_dev:
+ * gs_close
+ */
+
+-#define GS_WRITE_FINISHED_EVENT_SAFELY(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); \
+- cond; \
+-})
++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)
+ {
+@@ -807,7 +676,7 @@ static void gs_close(struct tty_struct *
+ 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_write_finished_event_safely(port),
+ GS_CLOSE_TIMEOUT * HZ);
+ spin_lock_irq(&port->port_lock);
+ }
+@@ -1065,6 +934,23 @@ static void gs_set_termios(struct tty_st
+ {
+ }
+
++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
+ *
+@@ -1328,9 +1214,44 @@ requeue:
+ }
+ }
+
++/*-------------------------------------------------------------------------*/
++
+ /* Gadget Driver */
+
+ /*
++ * gs_unbind
++ *
++ * Called on module unload. Frees the control request and device
++ * structure.
++ */
++static void /* __init_or_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) {
++ gs_free_req(gadget->ep0, dev->dev_ctrl_req);
++ dev->dev_ctrl_req = NULL;
++ }
++ gs_free_ports(dev);
++ if (dev->dev_notify_ep)
++ usb_ep_disable(dev->dev_notify_ep);
++ if (dev->dev_in_ep)
++ usb_ep_disable(dev->dev_in_ep);
++ if (dev->dev_out_ep)
++ usb_ep_disable(dev->dev_out_ep);
++ kfree(dev);
++ set_gadget_data(gadget, NULL);
++ }
++
++ pr_info("gs_unbind: %s %s unbound\n", GS_LONG_NAME,
++ GS_VERSION_STR);
++}
++
++/*
+ * gs_bind
+ *
+ * Called on module load. Allocates and initializes the device
+@@ -1441,8 +1362,6 @@ static int __init gs_bind(struct usb_gad
+ gs_unbind(gadget);
+ return -ENOMEM;
+ }
+- dev->dev_ctrl_req->complete = gs_setup_complete;
+-
+ gadget->ep0->driver_data = dev;
+
+ pr_info("gs_bind: %s %s bound\n",
+@@ -1455,95 +1374,6 @@ autoconf_fail:
+ return -ENODEV;
+ }
+
+-/*
+- * gs_unbind
+- *
+- * Called on module unload. Frees the control request and device
+- * structure.
+- */
+-static void /* __init_or_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) {
+- gs_free_req(gadget->ep0, dev->dev_ctrl_req);
+- dev->dev_ctrl_req = NULL;
+- }
+- gs_free_ports(dev);
+- if (dev->dev_notify_ep)
+- usb_ep_disable(dev->dev_notify_ep);
+- if (dev->dev_in_ep)
+- usb_ep_disable(dev->dev_in_ep);
+- if (dev->dev_out_ep)
+- usb_ep_disable(dev->dev_out_ep);
+- kfree(dev);
+- set_gadget_data(gadget, NULL);
+- }
+-
+- pr_info("gs_unbind: %s %s unbound\n", GS_LONG_NAME,
+- GS_VERSION_STR);
+-}
+-
+-/*
+- * gs_setup
+- *
+- * Implements all the control endpoint functionality that's not
+- * handled in hardware or the hardware driver.
+- *
+- * Returns the size of the data sent to the host, or a negative
+- * error number.
+- */
+-static int gs_setup(struct usb_gadget *gadget,
+- const struct usb_ctrlrequest *ctrl)
+-{
+- int ret = -EOPNOTSUPP;
+- struct gs_dev *dev = get_gadget_data(gadget);
+- struct usb_request *req = dev->dev_ctrl_req;
+- u16 wIndex = le16_to_cpu(ctrl->wIndex);
+- u16 wValue = le16_to_cpu(ctrl->wValue);
+- u16 wLength = le16_to_cpu(ctrl->wLength);
+-
+- req->complete = gs_setup_complete;
+-
+- switch (ctrl->bRequestType & USB_TYPE_MASK) {
+- case USB_TYPE_STANDARD:
+- ret = gs_setup_standard(gadget,ctrl);
+- break;
+-
+- case USB_TYPE_CLASS:
+- ret = gs_setup_class(gadget,ctrl);
+- break;
+-
+- default:
+- pr_err("gs_setup: unknown request, type=%02x, request=%02x, "
+- "value=%04x, index=%04x, length=%d\n",
+- ctrl->bRequestType, ctrl->bRequest,
+- wValue, wIndex, wLength);
+- break;
+- }
+-
+- /* respond with data transfer before status phase? */
+- if (ret >= 0) {
+- req->length = ret;
+- req->zero = ret < wLength
+- && (ret % gadget->ep0->maxpacket) == 0;
+- ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+- if (ret < 0) {
+- pr_err("gs_setup: cannot queue response, ret=%d\n",
+- ret);
+- req->status = 0;
+- gs_setup_complete(gadget->ep0, req);
+- }
+- }
+-
+- /* device either stalls (ret < 0) or reports success */
+- return ret;
+-}
+-
+ static int gs_setup_standard(struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl)
+ {
+@@ -1673,6 +1503,42 @@ set_interface_done:
+ return ret;
+ }
+
++static void gs_setup_complete_set_line_coding(struct usb_ep *ep,
++ 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))
++ usb_ep_set_halt(ep);
++ else if (port) {
++ 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);
++ }
++ break;
++
++ case -ESHUTDOWN:
++ /* disconnect */
++ gs_free_req(ep, req);
++ break;
++
++ default:
++ /* unexpected */
++ break;
++ }
++ return;
++}
++
+ static int gs_setup_class(struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl)
+ {
+@@ -1734,52 +1600,72 @@ static int gs_setup_class(struct usb_gad
+ return ret;
+ }
+
+-static void gs_setup_complete_set_line_coding(struct usb_ep *ep,
+- struct usb_request *req)
++/*
++ * gs_setup_complete
++ */
++static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req)
+ {
+- struct gs_dev *dev = ep->driver_data;
+- struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */
++ if (req->status || req->actual != req->length) {
++ pr_err("gs_setup_complete: status error, status=%d, "
++ "actual=%d, length=%d\n",
++ req->status, req->actual, req->length);
++ }
++}
+
+- switch (req->status) {
+- case 0:
+- /* normal completion */
+- if (req->actual != sizeof(port->port_line_coding))
+- usb_ep_set_halt(ep);
+- else if (port) {
+- struct usb_cdc_line_coding *value = req->buf;
++/*
++ * gs_setup
++ *
++ * Implements all the control endpoint functionality that's not
++ * handled in hardware or the hardware driver.
++ *
++ * Returns the size of the data sent to the host, or a negative
++ * error number.
++ */
++static int gs_setup(struct usb_gadget *gadget,
++ const struct usb_ctrlrequest *ctrl)
++{
++ int ret = -EOPNOTSUPP;
++ struct gs_dev *dev = get_gadget_data(gadget);
++ struct usb_request *req = dev->dev_ctrl_req;
++ u16 wIndex = le16_to_cpu(ctrl->wIndex);
++ u16 wValue = le16_to_cpu(ctrl->wValue);
++ u16 wLength = le16_to_cpu(ctrl->wLength);
+
+- /* 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);
+- }
++ req->complete = gs_setup_complete;
++
++ switch (ctrl->bRequestType & USB_TYPE_MASK) {
++ case USB_TYPE_STANDARD:
++ ret = gs_setup_standard(gadget,ctrl);
+ break;
+
+- case -ESHUTDOWN:
+- /* disconnect */
+- gs_free_req(ep, req);
++ case USB_TYPE_CLASS:
++ ret = gs_setup_class(gadget,ctrl);
+ break;
+
+ default:
+- /* unexpected */
++ pr_err("gs_setup: unknown request, type=%02x, request=%02x, "
++ "value=%04x, index=%04x, length=%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ wValue, wIndex, wLength);
+ break;
+ }
+- return;
+-}
+
+-/*
+- * gs_setup_complete
+- */
+-static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req)
+-{
+- if (req->status || req->actual != req->length) {
+- pr_err("gs_setup_complete: status error, status=%d, "
+- "actual=%d, length=%d\n",
+- req->status, req->actual, req->length);
++ /* respond with data transfer before status phase? */
++ if (ret >= 0) {
++ req->length = ret;
++ req->zero = ret < wLength
++ && (ret % gadget->ep0->maxpacket) == 0;
++ ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
++ if (ret < 0) {
++ pr_err("gs_setup: cannot queue response, ret=%d\n",
++ ret);
++ req->status = 0;
++ gs_setup_complete(gadget->ep0, req);
++ }
+ }
++
++ /* device either stalls (ret < 0) or reports success */
++ return ret;
+ }
+
+ /*
+@@ -1811,6 +1697,23 @@ static void gs_disconnect(struct usb_gad
+ pr_info("gs_disconnect: %s disconnected\n", GS_LONG_NAME);
+ }
+
++static struct usb_gadget_driver gs_gadget_driver = {
++#ifdef CONFIG_USB_GADGET_DUALSPEED
++ .speed = USB_SPEED_HIGH,
++#else
++ .speed = USB_SPEED_FULL,
++#endif /* CONFIG_USB_GADGET_DUALSPEED */
++ .function = GS_LONG_NAME,
++ .bind = gs_bind,
++ .unbind = gs_unbind,
++ .setup = gs_setup,
++ .disconnect = gs_disconnect,
++ .driver = {
++ .name = GS_SHORT_NAME,
++ .owner = THIS_MODULE,
++ },
++};
++
+ /*
+ * gs_set_config
+ *
+@@ -1846,16 +1749,10 @@ static int gs_set_config(struct gs_dev *
+ case GS_BULK_CONFIG_ID:
+ if (use_acm)
+ return -EINVAL;
+- /* device specific optimizations */
+- if (gadget_is_net2280(gadget))
+- net2280_set_fifo_mode(gadget, 1);
+ break;
+ case GS_ACM_CONFIG_ID:
+ if (!use_acm)
+ return -EINVAL;
+- /* device specific optimizations */
+- if (gadget_is_net2280(gadget))
+- net2280_set_fifo_mode(gadget, 1);
+ break;
+ default:
+ return -EINVAL;
+@@ -2233,6 +2130,8 @@ static void gs_free_ports(struct gs_dev
+ }
+ }
+
++/*-------------------------------------------------------------------------*/
++
+ /* Circular Buffer */
+
+ /*
+@@ -2393,3 +2292,77 @@ gs_buf_get(struct gs_buf *gb, char *buf,
+
+ return count;
+ }
++
++/*-------------------------------------------------------------------------*/
++
++static struct tty_driver *gs_tty_driver;
++
++/*
++ * gs_module_init
++ *
++ * Register as a USB gadget driver and a 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;
++}
++module_init(gs_module_init);
++
++/*
++ * gs_module_exit
++ *
++ * Unregister as a tty driver and a USB gadget driver.
++ */
++static void __exit gs_module_exit(void)
++{
++ tty_unregister_driver(gs_tty_driver);
++ put_tty_driver(gs_tty_driver);
++ usb_gadget_unregister_driver(&gs_gadget_driver);
++
++ pr_info("gs_module_exit: %s %s unloaded\n",
++ GS_LONG_NAME, GS_VERSION_STR);
++}
++module_exit(gs_module_exit);
diff --git a/usb.next/usb-serial-gadget-descriptor-cleanup.patch b/usb.next/usb-serial-gadget-descriptor-cleanup.patch
new file mode 100644
index 00000000000000..7c050eb1de6771
--- /dev/null
+++ b/usb.next/usb-serial-gadget-descriptor-cleanup.patch
@@ -0,0 +1,66 @@
+From linux-usb-owner@vger.kernel.org Wed May 7 14:45:49 2008
+From: David Brownell <david-b@pacbell.net>
+Date: Wed, 7 May 2008 14:27:37 -0700
+Subject: USB: serial gadget: descriptor cleanup
+To: Greg KH <greg@kroah.com>
+Cc: linux-usb@vger.kernel.org, Al Borchers <alborchers@steinerpoint.com>
+Message-ID: <200805071427.37610.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+Bugfix some serial gadget descriptors:
+
+ - Stop mangling the low bits (controller type ID) of bcdDevice;
+ just use the high bits for a driver revision code.
+
+ - Serial numbers that aren't specific to individual devices
+ are useless; stop reporting "0" for this.
+
+ - Since it's not part of a CDC-conformant function, the "bulk only"
+ configuration shouldn't be using "CDC Data" as its interface class.
+ Switch over to using CLASS_VENDOR_SPEC (different value, 0xff).
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Cc: Al Borchers <alborchers@steinerpoint.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/serial.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/gadget/serial.c
++++ b/drivers/usb/gadget/serial.c
+@@ -32,7 +32,7 @@
+ /* Defines */
+
+ #define GS_VERSION_STR "v2.2"
+-#define GS_VERSION_NUM 0x0202
++#define GS_VERSION_NUM 0x2200
+
+ #define GS_LONG_NAME "Gadget Serial"
+ #define GS_SHORT_NAME "g_serial"
+@@ -218,7 +218,6 @@ static char manufacturer[50];
+ static struct usb_string gs_strings[] = {
+ { GS_MANUFACTURER_STR_ID, manufacturer },
+ { GS_PRODUCT_STR_ID, GS_LONG_NAME },
+- { GS_SERIAL_STR_ID, "0" },
+ { GS_BULK_CONFIG_STR_ID, "Gadget Serial Bulk" },
+ { GS_ACM_CONFIG_STR_ID, "Gadget Serial CDC ACM" },
+ { GS_CONTROL_STR_ID, "Gadget Serial Control" },
+@@ -241,7 +240,6 @@ static struct usb_device_descriptor gs_d
+ .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID),
+ .iManufacturer = GS_MANUFACTURER_STR_ID,
+ .iProduct = GS_PRODUCT_STR_ID,
+- .iSerialNumber = GS_SERIAL_STR_ID,
+ .bNumConfigurations = GS_NUM_CONFIGS,
+ };
+
+@@ -278,7 +276,7 @@ static const struct usb_interface_descri
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = GS_BULK_INTERFACE_ID,
+ .bNumEndpoints = 2,
+- .bInterfaceClass = USB_CLASS_CDC_DATA,
++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .iInterface = GS_DATA_STR_ID,
diff --git a/usb.next/usb-serial-gadget-remove-needless-data-structure.patch b/usb.next/usb-serial-gadget-remove-needless-data-structure.patch
new file mode 100644
index 00000000000000..1dd1e5550fe1ef
--- /dev/null
+++ b/usb.next/usb-serial-gadget-remove-needless-data-structure.patch
@@ -0,0 +1,198 @@
+From linux-usb-owner@vger.kernel.org Wed May 7 14:45:49 2008
+From: David Brownell <david-b@pacbell.net>
+Date: Wed, 7 May 2008 14:24:10 -0700
+Subject: USB: serial gadget: remove needless data structure
+To: Greg KH <greg@kroah.com>
+Cc: linux-usb@vger.kernel.org, Al Borchers <alborchers@steinerpoint.com>
+Message-ID: <200805071424.10879.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+This removes a needless data structure from the serial gadget code;
+it's a small code shrink, and a larger data shrink.
+
+Since "struct usb_request" already has a "struct list_head" reserved
+for use by gadget drivers, the serial gadget code doesn't need to
+allocate wrapper structs to hold that list ... it can (and should!)
+just use the list_head provided for that exact use.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Cc: Al Borchers <alborchers@steinerpoint.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/serial.c | 85 ++++++--------------------------------------
+ 1 file changed, 13 insertions(+), 72 deletions(-)
+
+--- a/drivers/usb/gadget/serial.c
++++ b/drivers/usb/gadget/serial.c
+@@ -121,12 +121,6 @@ struct gs_buf {
+ char *buf_put;
+ };
+
+-/* list of requests */
+-struct gs_req_entry {
+- struct list_head re_entry;
+- struct usb_request *re_req;
+-};
+-
+ /* 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 */
+@@ -185,10 +179,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 struct gs_req_entry *gs_alloc_req_entry(struct usb_ep *ep, unsigned len,
+- gfp_t kmalloc_flags);
+-static void gs_free_req_entry(struct usb_ep *ep, struct gs_req_entry *req);
+-
+ static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags);
+ static void gs_free_ports(struct gs_dev *dev);
+
+@@ -966,7 +956,6 @@ static int gs_send(struct gs_dev *dev)
+ unsigned long flags;
+ struct usb_ep *ep;
+ struct usb_request *req;
+- struct gs_req_entry *req_entry;
+
+ if (dev == NULL) {
+ pr_err("gs_send: NULL device pointer\n");
+@@ -979,10 +968,8 @@ static int gs_send(struct gs_dev *dev)
+
+ while(!list_empty(&dev->dev_req_list)) {
+
+- req_entry = list_entry(dev->dev_req_list.next,
+- struct gs_req_entry, re_entry);
+-
+- req = req_entry->re_req;
++ req = list_entry(dev->dev_req_list.next,
++ struct usb_request, list);
+
+ len = gs_send_packet(dev, req->buf, ep->maxpacket);
+
+@@ -992,7 +979,7 @@ static int gs_send(struct gs_dev *dev)
+ *((unsigned char *)req->buf),
+ *((unsigned char *)req->buf+1),
+ *((unsigned char *)req->buf+2));
+- list_del(&req_entry->re_entry);
++ list_del(&req->list);
+ req->length = len;
+ spin_unlock_irqrestore(&dev->dev_lock, flags);
+ if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
+@@ -1175,7 +1162,6 @@ requeue:
+ static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
+ {
+ struct gs_dev *dev = ep->driver_data;
+- struct gs_req_entry *gs_req = req->context;
+
+ if (dev == NULL) {
+ pr_err("gs_write_complete: NULL device pointer\n");
+@@ -1186,13 +1172,8 @@ static void gs_write_complete(struct usb
+ case 0:
+ /* normal completion */
+ requeue:
+- if (gs_req == NULL) {
+- pr_err("gs_write_complete: NULL request pointer\n");
+- return;
+- }
+-
+ spin_lock(&dev->dev_lock);
+- list_add(&gs_req->re_entry, &dev->dev_req_list);
++ list_add(&req->list, &dev->dev_req_list);
+ spin_unlock(&dev->dev_lock);
+
+ gs_send(dev);
+@@ -1731,7 +1712,6 @@ static int gs_set_config(struct gs_dev *
+ struct usb_ep *ep;
+ struct usb_endpoint_descriptor *ep_desc;
+ struct usb_request *req;
+- struct gs_req_entry *req_entry;
+
+ if (dev == NULL) {
+ pr_err("gs_set_config: NULL device pointer\n");
+@@ -1843,9 +1823,10 @@ static int gs_set_config(struct gs_dev *
+ /* allocate write requests, and put on free list */
+ ep = dev->dev_in_ep;
+ for (i=0; i<write_q_size; i++) {
+- if ((req_entry=gs_alloc_req_entry(ep, ep->maxpacket, GFP_ATOMIC))) {
+- req_entry->re_req->complete = gs_write_complete;
+- list_add(&req_entry->re_entry, &dev->dev_req_list);
++ 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");
+@@ -1883,7 +1864,7 @@ exit_reset_config:
+ */
+ static void gs_reset_config(struct gs_dev *dev)
+ {
+- struct gs_req_entry *req_entry;
++ struct usb_request *req;
+
+ if (dev == NULL) {
+ pr_err("gs_reset_config: NULL device pointer\n");
+@@ -1897,10 +1878,10 @@ static void gs_reset_config(struct gs_de
+
+ /* free write requests on the free list */
+ while(!list_empty(&dev->dev_req_list)) {
+- req_entry = list_entry(dev->dev_req_list.next,
+- struct gs_req_entry, re_entry);
+- list_del(&req_entry->re_entry);
+- gs_free_req_entry(dev->dev_in_ep, req_entry);
++ req = list_entry(dev->dev_req_list.next,
++ struct usb_request, list);
++ list_del(&req->list);
++ gs_free_req(dev->dev_in_ep, req);
+ }
+
+ /* disable endpoints, forcing completion of pending i/o; */
+@@ -2010,46 +1991,6 @@ static void gs_free_req(struct usb_ep *e
+ }
+
+ /*
+- * gs_alloc_req_entry
+- *
+- * Allocates a request and its buffer, using the given
+- * endpoint, buffer len, and kmalloc flags.
+- */
+-static struct gs_req_entry *
+-gs_alloc_req_entry(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+-{
+- struct gs_req_entry *req;
+-
+- req = kmalloc(sizeof(struct gs_req_entry), kmalloc_flags);
+- if (req == NULL)
+- return NULL;
+-
+- req->re_req = gs_alloc_req(ep, len, kmalloc_flags);
+- if (req->re_req == NULL) {
+- kfree(req);
+- return NULL;
+- }
+-
+- req->re_req->context = req;
+-
+- return req;
+-}
+-
+-/*
+- * gs_free_req_entry
+- *
+- * Frees a request and its buffer.
+- */
+-static void gs_free_req_entry(struct usb_ep *ep, struct gs_req_entry *req)
+-{
+- if (ep != NULL && req != NULL) {
+- if (req->re_req != NULL)
+- gs_free_req(ep, req->re_req);
+- kfree(req);
+- }
+-}
+-
+-/*
+ * gs_alloc_ports
+ *
+ * Allocate all ports and set the gs_dev struct to point to them.
diff --git a/usb.next/usb-serial-gadget-simplify-endpoint-handling.patch b/usb.next/usb-serial-gadget-simplify-endpoint-handling.patch
new file mode 100644
index 00000000000000..7b98140c3beea0
--- /dev/null
+++ b/usb.next/usb-serial-gadget-simplify-endpoint-handling.patch
@@ -0,0 +1,254 @@
+From linux-usb-owner@vger.kernel.org Wed May 7 14:45:49 2008
+From: David Brownell <david-b@pacbell.net>
+Date: Wed, 7 May 2008 14:25:24 -0700
+Subject: USB: serial gadget: simplify endpoint handling
+To: Greg KH <greg@kroah.com>
+Cc: linux-usb@vger.kernel.org, Al Borchers <alborchers@steinerpoint.com>
+Message-ID: <200805071425.24929.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+Switch serial gadget away from a *very* old idiom: just remember
+the endpoints we'll be using, instead of looking them up by name
+each time. This is a net code and data (globals) shrink.
+
+Also fix a small memory leak in the rmmod path, by working the
+same as the disconnect code.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Cc: Al Borchers <alborchers@steinerpoint.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/serial.c | 146 +++++++++++++++++---------------------------
+ 1 file changed, 57 insertions(+), 89 deletions(-)
+
+--- a/drivers/usb/gadget/serial.c
++++ b/drivers/usb/gadget/serial.c
+@@ -198,10 +198,6 @@ static unsigned int gs_buf_get(struct gs
+
+ static struct gs_dev *gs_device;
+
+-static const char *EP_IN_NAME;
+-static const char *EP_OUT_NAME;
+-static const char *EP_NOTIFY_NAME;
+-
+ static struct mutex gs_open_close_lock[GS_NUM_PORTS];
+
+
+@@ -1217,13 +1213,8 @@ static void /* __init_or_exit */ gs_unbi
+ gs_free_req(gadget->ep0, dev->dev_ctrl_req);
+ dev->dev_ctrl_req = NULL;
+ }
++ gs_reset_config(dev);
+ gs_free_ports(dev);
+- if (dev->dev_notify_ep)
+- usb_ep_disable(dev->dev_notify_ep);
+- if (dev->dev_in_ep)
+- usb_ep_disable(dev->dev_in_ep);
+- if (dev->dev_out_ep)
+- usb_ep_disable(dev->dev_out_ep);
+ kfree(dev);
+ set_gadget_data(gadget, NULL);
+ }
+@@ -1264,19 +1255,23 @@ static int __init gs_bind(struct usb_gad
+ __constant_cpu_to_le16(GS_VERSION_NUM|0x0099);
+ }
+
++ dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL);
++ if (dev == NULL)
++ return -ENOMEM;
++
+ usb_ep_autoconfig_reset(gadget);
+
+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc);
+ if (!ep)
+ goto autoconf_fail;
+- EP_IN_NAME = ep->name;
+- ep->driver_data = ep; /* claim the endpoint */
++ dev->dev_in_ep = ep;
++ ep->driver_data = dev; /* claim the endpoint */
+
+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc);
+ if (!ep)
+ goto autoconf_fail;
+- EP_OUT_NAME = ep->name;
+- ep->driver_data = ep; /* claim the endpoint */
++ dev->dev_out_ep = ep;
++ ep->driver_data = dev; /* claim the endpoint */
+
+ if (use_acm) {
+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc);
+@@ -1286,8 +1281,8 @@ static int __init gs_bind(struct usb_gad
+ }
+ gs_device_desc.idProduct = __constant_cpu_to_le16(
+ GS_CDC_PRODUCT_ID),
+- EP_NOTIFY_NAME = ep->name;
+- ep->driver_data = ep; /* claim the endpoint */
++ dev->dev_notify_ep = ep;
++ ep->driver_data = dev; /* claim the endpoint */
+ }
+
+ gs_device_desc.bDeviceClass = use_acm
+@@ -1317,9 +1312,7 @@ static int __init gs_bind(struct usb_gad
+ gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+- gs_device = dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL);
+- if (dev == NULL)
+- return -ENOMEM;
++ gs_device = dev;
+
+ snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
+ init_utsname()->sysname, init_utsname()->release,
+@@ -1351,6 +1344,7 @@ static int __init gs_bind(struct usb_gad
+ return 0;
+
+ autoconf_fail:
++ kfree(dev);
+ pr_err("gs_bind: cannot autoconfigure on %s\n", gadget->name);
+ return -ENODEV;
+ }
+@@ -1710,7 +1704,7 @@ static int gs_set_config(struct gs_dev *
+ int ret = 0;
+ struct usb_gadget *gadget = dev->dev_gadget;
+ struct usb_ep *ep;
+- struct usb_endpoint_descriptor *ep_desc;
++ struct usb_endpoint_descriptor *out, *in, *notify;
+ struct usb_request *req;
+
+ if (dev == NULL) {
+@@ -1738,71 +1732,53 @@ static int gs_set_config(struct gs_dev *
+ return -EINVAL;
+ }
+
+- dev->dev_config = config;
+-
+- gadget_for_each_ep(ep, gadget) {
+-
+- if (EP_NOTIFY_NAME
+- && strcmp(ep->name, EP_NOTIFY_NAME) == 0) {
+- ep_desc = choose_ep_desc(gadget,
++ in = choose_ep_desc(gadget,
++ &gs_highspeed_in_desc,
++ &gs_fullspeed_in_desc);
++ out = choose_ep_desc(gadget,
++ &gs_highspeed_out_desc,
++ &gs_fullspeed_out_desc);
++ notify = dev->dev_notify_ep
++ ? choose_ep_desc(gadget,
+ &gs_highspeed_notify_desc,
+- &gs_fullspeed_notify_desc);
+- ret = usb_ep_enable(ep,ep_desc);
+- if (ret == 0) {
+- ep->driver_data = dev;
+- dev->dev_notify_ep = ep;
+- dev->dev_notify_ep_desc = ep_desc;
+- } else {
+- pr_err("gs_set_config: cannot enable NOTIFY "
+- "endpoint %s, ret=%d\n",
+- ep->name, ret);
+- goto exit_reset_config;
+- }
+- }
++ &gs_fullspeed_notify_desc)
++ : NULL;
+
+- else if (strcmp(ep->name, EP_IN_NAME) == 0) {
+- ep_desc = choose_ep_desc(gadget,
+- &gs_highspeed_in_desc,
+- &gs_fullspeed_in_desc);
+- ret = usb_ep_enable(ep,ep_desc);
+- if (ret == 0) {
+- ep->driver_data = dev;
+- dev->dev_in_ep = ep;
+- dev->dev_in_ep_desc = ep_desc;
+- } else {
+- pr_err("gs_set_config: cannot enable IN "
+- "endpoint %s, ret=%d\n",
+- ep->name, ret);
+- goto exit_reset_config;
+- }
+- }
+-
+- else if (strcmp(ep->name, EP_OUT_NAME) == 0) {
+- ep_desc = choose_ep_desc(gadget,
+- &gs_highspeed_out_desc,
+- &gs_fullspeed_out_desc);
+- ret = usb_ep_enable(ep,ep_desc);
+- if (ret == 0) {
+- ep->driver_data = dev;
+- dev->dev_out_ep = ep;
+- dev->dev_out_ep_desc = ep_desc;
+- } else {
+- pr_err("gs_set_config: cannot enable OUT "
+- "endpoint %s, ret=%d\n",
+- ep->name, ret);
+- goto exit_reset_config;
+- }
+- }
++ 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;
+ }
+
+- if (dev->dev_in_ep == NULL || dev->dev_out_ep == NULL
+- || (config != GS_BULK_CONFIG_ID && dev->dev_notify_ep == NULL)) {
+- pr_err("gs_set_config: cannot find endpoints\n");
+- ret = -ENODEV;
+- goto exit_reset_config;
++ 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;
++ }
+ }
+
++ dev->dev_config = config;
++
+ /* allocate and queue read requests */
+ ep = dev->dev_out_ep;
+ for (i=0; i<read_q_size && ret == 0; i++) {
+@@ -1886,18 +1862,10 @@ static void gs_reset_config(struct gs_de
+
+ /* disable endpoints, forcing completion of pending i/o; */
+ /* completion handlers free their requests in this case */
+- if (dev->dev_notify_ep) {
++ if (dev->dev_notify_ep)
+ usb_ep_disable(dev->dev_notify_ep);
+- dev->dev_notify_ep = NULL;
+- }
+- if (dev->dev_in_ep) {
+- usb_ep_disable(dev->dev_in_ep);
+- dev->dev_in_ep = NULL;
+- }
+- if (dev->dev_out_ep) {
+- usb_ep_disable(dev->dev_out_ep);
+- dev->dev_out_ep = NULL;
+- }
++ usb_ep_disable(dev->dev_in_ep);
++ usb_ep_disable(dev->dev_out_ep);
+ }
+
+ /*
diff --git a/usb.next/usb-unusual_devs-add-support-for-gi-0401-sd-card-interface.patch b/usb.next/usb-unusual_devs-add-support-for-gi-0401-sd-card-interface.patch
new file mode 100644
index 00000000000000..64c211badd0cbc
--- /dev/null
+++ b/usb.next/usb-unusual_devs-add-support-for-gi-0401-sd-card-interface.patch
@@ -0,0 +1,93 @@
+From linux-usb-owner@vger.kernel.org Thu May 8 10:57:15 2008
+From: Filip Aben <f.aben@option.com>
+Date: Thu, 08 May 2008 10:48:12 -0700
+Subject: USB: unusual_devs: Add support for GI 0401 SD-Card interface
+To: Greg KH <greg@kroah.com>
+Cc: USB Dev <linux-usb@vger.kernel.org>, USB Storage list <usb-storage@lists.one-eyed-alien.net>, f.aben@option.com
+Message-ID: <48233CDC.9030700@ipom.com>
+
+
+From: Filip Aben <f.aben@option.com>
+
+Enables the SD-Card interface on the GI 0401 HSUPA card from Option.
+
+The unusual_devs.h entry is necessary because the device descriptor is
+vendor-specific. That prevents usb-storage from binding to it as an
+interface driver.
+
+This revised patch adds a small comment explaining why and reduces the
+rev range.
+
+T: Bus=02 Lev=01 Prnt=01 Port=06 Cnt=01 Dev#= 3 Spd=480 MxCh= 0
+D: Ver= 2.00 Cls=ff(vend.) Sub=ff Prot=ff MxPS=64 #Cfgs= 1
+P: Vendor=0af0 ProdID=7401 Rev= 0.00
+S: Manufacturer=Option N.V.
+S: Product=Globetrotter HSUPA Modem
+C:* #Ifs=10 Cfg#= 1 Atr=80 MxPwr=500mA
+I:* If#= 0 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+I: If#= 0 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
+I:* If#= 1 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+I: If#= 1 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
+I:* If#= 2 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+I: If#= 2 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
+I:* If#= 3 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+I: If#= 3 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
+I:* If#= 4 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+I: If#= 4 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
+I:* If#= 5 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+I: If#= 5 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
+I:* If#= 6 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+I: If#= 6 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
+I:* If#= 7 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+I: If#= 7 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+E: Ad=88(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=08(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
+I:* If#= 8 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+E: Ad=89(I) Atr=03(Int.) MxPS= 64 Ivl=2ms
+E: Ad=8a(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=09(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
+I:* If#= 9 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
+E: Ad=0a(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+
+Signed-off-by: Filip Aben <f.aben@option.com>
+Signed-off-by: Phil Dibowitz <phil@ipom.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/storage/unusual_devs.h | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/usb/storage/unusual_devs.h
++++ b/drivers/usb/storage/unusual_devs.h
+@@ -1311,6 +1311,16 @@ UNUSUAL_DEV( 0x0ace, 0x20ff, 0x0101, 0x0
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_DEVICE ),
+
++/* Reported by F. Aben <f.aben@option.com>
++ * This device (wrongly) has a vendor-specific device descriptor.
++ * The entry is needed so usb-storage can bind to it's mass-storage
++ * interface as an interface driver */
++UNUSUAL_DEV( 0x0af0, 0x7401, 0x0000, 0x0000,
++ "Option",
++ "GI 0401 SD-Card",
++ US_SC_DEVICE, US_PR_DEVICE, NULL,
++ 0 ),
++
+ #ifdef CONFIG_USB_STORAGE_ISD200
+ UNUSUAL_DEV( 0x0bf6, 0xa001, 0x0100, 0x0110,
+ "ATI",
diff --git a/usb/usb-implement-soft-unbinding.patch b/usb/usb-implement-soft-unbinding.patch
new file mode 100644
index 00000000000000..5b6d63c605d24d
--- /dev/null
+++ b/usb/usb-implement-soft-unbinding.patch
@@ -0,0 +1,83 @@
+From stern+483d8e12@rowland.harvard.edu Thu May 8 08:54:42 2008
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Thu, 8 May 2008 11:54:37 -0400 (EDT)
+Subject: USB: implement "soft" unbinding
+To: Greg KH <greg@kroah.com>
+Cc: USB list <linux-usb@vger.kernel.org>
+Message-ID: <Pine.LNX.4.44L0.0805081152300.2188-100000@iolanthe.rowland.org>
+
+
+This patch (as1091) changes the way usbcore handles interface
+unbinding. If the interface's driver supports "soft" unbinding (a new
+flag in the driver structure) then in-flight URBs are not cancelled
+and endpoints are not disabled. Instead the driver is allowed to
+continue communicating with the device (although of course it should
+stop before its disconnect routine returns).
+
+The purpose of this change is to allow drivers to do a clean shutdown
+when they get unbound from a device that is still plugged in. Killing
+all the URBs and disabling the endpoints before calling the driver's
+disconnect method doesn't give the driver any control over what
+happens, and it can leave devices in indeterminate states. For
+example, when usb-storage unbinds it doesn't want to stop while in the
+middle of transmitting a SCSI command.
+
+The soft_unbind flag is added because in the past, a number of drivers
+have experienced problems related to ongoing I/O after their disconnect
+routine returned. Hence "soft" unbinding is made available only to
+drivers that claim to support it.
+
+The patch also replaces "interface_to_usbdev(intf)" with "udev" in a
+couple of places, a minor simplification.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/driver.c | 11 ++++++-----
+ include/linux/usb.h | 3 +++
+ 2 files changed, 9 insertions(+), 5 deletions(-)
+
+--- a/drivers/usb/core/driver.c
++++ b/drivers/usb/core/driver.c
+@@ -257,15 +257,16 @@ static int usb_unbind_interface(struct d
+ udev = interface_to_usbdev(intf);
+ error = usb_autoresume_device(udev);
+
+- /* release all urbs for this interface */
+- usb_disable_interface(interface_to_usbdev(intf), intf);
++ /* Terminate all URBs for this interface unless the driver
++ * supports "soft" unbinding.
++ */
++ if (!driver->soft_unbind)
++ usb_disable_interface(udev, intf);
+
+ driver->disconnect(intf);
+
+ /* reset other interface state */
+- usb_set_interface(interface_to_usbdev(intf),
+- intf->altsetting[0].desc.bInterfaceNumber,
+- 0);
++ usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);
+ usb_set_intfdata(intf, NULL);
+
+ intf->condition = USB_INTERFACE_UNBOUND;
+--- a/include/linux/usb.h
++++ b/include/linux/usb.h
+@@ -972,6 +972,8 @@ struct usbdrv_wrap {
+ * added to this driver by preventing the sysfs file from being created.
+ * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend
+ * for interfaces bound to this driver.
++ * @soft_unbind: if set to 1, the USB core will not kill URBs and disable
++ * endpoints before calling the driver's disconnect method.
+ *
+ * USB interface drivers must provide a name, probe() and disconnect()
+ * methods, and an id_table. Other driver fields are optional.
+@@ -1012,6 +1014,7 @@ struct usb_driver {
+ struct usbdrv_wrap drvwrap;
+ unsigned int no_dynamic_id:1;
+ unsigned int supports_autosuspend:1;
++ unsigned int soft_unbind:1;
+ };
+ #define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)
+
diff --git a/usb/usb-storage-implement-soft-unbinding.patch b/usb/usb-storage-implement-soft-unbinding.patch
new file mode 100644
index 00000000000000..563faaeae2417e
--- /dev/null
+++ b/usb/usb-storage-implement-soft-unbinding.patch
@@ -0,0 +1,260 @@
+From stern+483d8e12@rowland.harvard.edu Thu May 8 08:56:01 2008
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Thu, 8 May 2008 11:55:59 -0400 (EDT)
+Subject: usb-storage: implement "soft" unbinding
+To: Greg KH <greg@kroah.com>, Matthew Dharm <mdharm-usb@one-eyed-alien.net>
+Cc: USB list <linux-usb@vger.kernel.org>, USB Storage list <usb-storage@lists.one-eyed-alien.net>
+Message-ID: <Pine.LNX.4.44L0.0805081154370.2188-100000@iolanthe.rowland.org>
+
+
+This patch (as1092) implements "soft" unbinding for usb-storage. When
+the disconnect routine is called, all commands and reset delays are
+allowed to complete normally until after scsi_remove_host() returns.
+This means that the commands needed for an orderly shutdown will be
+sent through to the device.
+
+Unlike before, the driver will now execute every command that it
+accepts. Hence there's no need for special code to catch unexecuted
+commands and fail them.
+
+The new sequence of events when disconnect runs goes as follows:
+
+ If the device is truly unplugged, set the DISCONNECTING
+ flag so we won't try to access it any more.
+
+ If the SCSI-scanning thread hasn't started up yet, prevent
+ it from doing anything by setting the new DONT_SCAN flag.
+ Then wake it up and wait for it to terminate.
+
+ Remove the SCSI host. This unbinds the upper-level drivers,
+ doing an orderly shutdown. Commands sent to quiesce the
+ device will be transmitted normally, unless the device is
+ unplugged.
+
+ Set the DISCONNECTING flag so that we won't accept any new
+ commands that might get submitted (there aren't supposed to be
+ any) and we won't try to access the device for resets.
+
+ Tell the control thread to exit by waking it up with no
+ pending command, and wait for it to terminate.
+
+ Go on to do all the other normal stuff: releasing resources,
+ freeing memory, and so on.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/storage/transport.c | 16 ++++----
+ drivers/usb/storage/usb.c | 77 ++++++++++++++++++----------------------
+ drivers/usb/storage/usb.h | 4 --
+ 3 files changed, 45 insertions(+), 52 deletions(-)
+
+--- a/drivers/usb/storage/transport.c
++++ b/drivers/usb/storage/transport.c
+@@ -127,8 +127,8 @@ static int usb_stor_msg_common(struct us
+ long timeleft;
+ int status;
+
+- /* don't submit URBs during abort/disconnect processing */
+- if (us->dflags & ABORTING_OR_DISCONNECTING)
++ /* don't submit URBs during abort processing */
++ if (test_bit(US_FLIDX_ABORTING, &us->dflags))
+ return -EIO;
+
+ /* set up data structures for the wakeup system */
+@@ -161,8 +161,8 @@ static int usb_stor_msg_common(struct us
+ * to cancel it */
+ set_bit(US_FLIDX_URB_ACTIVE, &us->dflags);
+
+- /* did an abort/disconnect occur during the submission? */
+- if (us->dflags & ABORTING_OR_DISCONNECTING) {
++ /* did an abort occur during the submission? */
++ if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {
+
+ /* cancel the URB, if it hasn't been cancelled already */
+ if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) {
+@@ -419,8 +419,8 @@ static int usb_stor_bulk_transfer_sglist
+ {
+ int result;
+
+- /* don't submit s-g requests during abort/disconnect processing */
+- if (us->dflags & ABORTING_OR_DISCONNECTING)
++ /* don't submit s-g requests during abort processing */
++ if (test_bit(US_FLIDX_ABORTING, &us->dflags))
+ return USB_STOR_XFER_ERROR;
+
+ /* initialize the scatter-gather request block */
+@@ -437,8 +437,8 @@ static int usb_stor_bulk_transfer_sglist
+ * okay to cancel it */
+ set_bit(US_FLIDX_SG_ACTIVE, &us->dflags);
+
+- /* did an abort/disconnect occur during the submission? */
+- if (us->dflags & ABORTING_OR_DISCONNECTING) {
++ /* did an abort occur during the submission? */
++ if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {
+
+ /* cancel the request, if it hasn't been cancelled already */
+ if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) {
+--- a/drivers/usb/storage/usb.c
++++ b/drivers/usb/storage/usb.c
+@@ -320,16 +320,17 @@ static int usb_stor_control_thread(void
+ /* lock the device pointers */
+ mutex_lock(&(us->dev_mutex));
+
+- /* if the device has disconnected, we are free to exit */
+- if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
+- US_DEBUGP("-- exiting\n");
++ /* lock access to the state */
++ scsi_lock(host);
++
++ /* When we are called with no command pending, we're done */
++ if (us->srb == NULL) {
++ scsi_unlock(host);
+ mutex_unlock(&us->dev_mutex);
++ US_DEBUGP("-- exiting\n");
+ break;
+ }
+
+- /* lock access to the state */
+- scsi_lock(host);
+-
+ /* has the command timed out *already* ? */
+ if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
+ us->srb->result = DID_ABORT << 16;
+@@ -384,12 +385,8 @@ static int usb_stor_control_thread(void
+ /* lock access to the state */
+ scsi_lock(host);
+
+- /* did the command already complete because of a disconnect? */
+- if (!us->srb)
+- ; /* nothing to do */
+-
+ /* indicate that the command is done */
+- else if (us->srb->result != DID_ABORT << 16) {
++ if (us->srb->result != DID_ABORT << 16) {
+ US_DEBUGP("scsi cmd done, result=0x%x\n",
+ us->srb->result);
+ us->srb->scsi_done(us->srb);
+@@ -820,11 +817,10 @@ static void usb_stor_release_resources(s
+ US_DEBUGP("-- %s\n", __func__);
+
+ /* Tell the control thread to exit. The SCSI host must
+- * already have been removed so it won't try to queue
+- * any more commands.
++ * already have been removed and the DISCONNECTING flag set
++ * so that we won't accept any more commands.
+ */
+ US_DEBUGP("-- sending exit command to thread\n");
+- set_bit(US_FLIDX_DISCONNECTING, &us->dflags);
+ complete(&us->cmnd_ready);
+ if (us->ctl_thread)
+ kthread_stop(us->ctl_thread);
+@@ -859,39 +855,36 @@ static void dissociate_dev(struct us_dat
+ usb_set_intfdata(us->pusb_intf, NULL);
+ }
+
+-/* First stage of disconnect processing: stop all commands and remove
+- * the host */
++/* First stage of disconnect processing: stop SCSI scanning,
++ * remove the host, and stop accepting new commands
++ */
+ static void quiesce_and_remove_host(struct us_data *us)
+ {
+ struct Scsi_Host *host = us_to_host(us);
+
+- /* Prevent new USB transfers, stop the current command, and
+- * interrupt a SCSI-scan or device-reset delay */
+- scsi_lock(host);
+- set_bit(US_FLIDX_DISCONNECTING, &us->dflags);
+- scsi_unlock(host);
+- usb_stor_stop_transport(us);
+- wake_up(&us->delay_wait);
++ /* If the device is really gone, cut short reset delays */
++ if (us->pusb_dev->state == USB_STATE_NOTATTACHED)
++ set_bit(US_FLIDX_DISCONNECTING, &us->dflags);
+
+- /* queuecommand won't accept any new commands and the control
+- * thread won't execute a previously-queued command. If there
+- * is such a command pending, complete it with an error. */
+- mutex_lock(&us->dev_mutex);
+- if (us->srb) {
+- us->srb->result = DID_NO_CONNECT << 16;
+- scsi_lock(host);
+- us->srb->scsi_done(us->srb);
+- us->srb = NULL;
+- complete(&us->notify); /* in case of an abort */
+- scsi_unlock(host);
+- }
+- mutex_unlock(&us->dev_mutex);
++ /* Prevent SCSI-scanning (if it hasn't started yet)
++ * and wait for the SCSI-scanning thread to stop.
++ */
++ set_bit(US_FLIDX_DONT_SCAN, &us->dflags);
++ wake_up(&us->delay_wait);
++ wait_for_completion(&us->scanning_done);
+
+- /* Now we own no commands so it's safe to remove the SCSI host */
++ /* Removing the host will perform an orderly shutdown: caches
++ * synchronized, disks spun down, etc.
++ */
+ scsi_remove_host(host);
+
+- /* Wait for the SCSI-scanning thread to stop */
+- wait_for_completion(&us->scanning_done);
++ /* Prevent any new commands from being accepted and cut short
++ * reset delays.
++ */
++ scsi_lock(host);
++ set_bit(US_FLIDX_DISCONNECTING, &us->dflags);
++ scsi_unlock(host);
++ wake_up(&us->delay_wait);
+ }
+
+ /* Second stage of disconnect processing: deallocate all resources */
+@@ -919,12 +912,12 @@ static int usb_stor_scan_thread(void * _
+ printk(KERN_DEBUG "usb-storage: waiting for device "
+ "to settle before scanning\n");
+ wait_event_freezable_timeout(us->delay_wait,
+- test_bit(US_FLIDX_DISCONNECTING, &us->dflags),
++ test_bit(US_FLIDX_DONT_SCAN, &us->dflags),
+ delay_use * HZ);
+ }
+
+ /* If the device is still connected, perform the scanning */
+- if (!test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
++ if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) {
+
+ /* For bulk-only devices, determine the max LUN value */
+ if (us->protocol == US_PR_BULK &&
+@@ -1023,6 +1016,7 @@ static int storage_probe(struct usb_inte
+ if (IS_ERR(th)) {
+ printk(KERN_WARNING USB_STORAGE
+ "Unable to start the device-scanning thread\n");
++ complete(&us->scanning_done);
+ quiesce_and_remove_host(us);
+ result = PTR_ERR(th);
+ goto BadDevice;
+@@ -1065,6 +1059,7 @@ static struct usb_driver usb_storage_dri
+ .pre_reset = storage_pre_reset,
+ .post_reset = storage_post_reset,
+ .id_table = storage_usb_ids,
++ .soft_unbind = 1,
+ };
+
+ static int __init usb_stor_init(void)
+--- a/drivers/usb/storage/usb.h
++++ b/drivers/usb/storage/usb.h
+@@ -72,11 +72,9 @@ struct us_unusual_dev {
+ #define US_FLIDX_SG_ACTIVE 1 /* current_sg is in use */
+ #define US_FLIDX_ABORTING 2 /* abort is in progress */
+ #define US_FLIDX_DISCONNECTING 3 /* disconnect in progress */
+-#define ABORTING_OR_DISCONNECTING ((1UL << US_FLIDX_ABORTING) | \
+- (1UL << US_FLIDX_DISCONNECTING))
+ #define US_FLIDX_RESETTING 4 /* device reset in progress */
+ #define US_FLIDX_TIMED_OUT 5 /* SCSI midlayer timed out */
+-
++#define US_FLIDX_DONT_SCAN 6 /* don't scan (disconnect) */
+
+ #define USB_STOR_STRING_LEN 32
+