diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2008-05-08 16:26:30 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-05-08 16:26:30 -0700 |
| commit | e8593fcdd1bd4fc1992f065951ae4545cf7e6cd5 (patch) | |
| tree | b4fd81d204d736e8019d5c334a3fe172948a5d0e | |
| parent | d1250c4948996b6ff72db22e92acf4167ae6308f (diff) | |
| download | patches-e8593fcdd1bd4fc1992f065951ae4545cf7e6cd5.tar.gz | |
more patches added
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(); } @@ -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 + |
