diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2010-04-29 13:28:50 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-04-29 13:28:50 -0700 |
| commit | 9c3de47550378c2c5cb83ff5e0f7138b6fa00f83 (patch) | |
| tree | f0e9a76652a4cf3b74c3b5244a96db942715b76b | |
| parent | bccc6afa0ed3ca2fedde5fe95de3a7df61866e7a (diff) | |
| download | patches-9c3de47550378c2c5cb83ff5e0f7138b6fa00f83.tar.gz | |
lots of crap
50 files changed, 8829 insertions, 11 deletions
diff --git a/driver-core.current/firmware_class-fix-memory-leak-free-allocated-pages.patch b/driver-core.current/firmware_class-fix-memory-leak-free-allocated-pages.patch new file mode 100644 index 00000000000000..4e823a45a670b6 --- /dev/null +++ b/driver-core.current/firmware_class-fix-memory-leak-free-allocated-pages.patch @@ -0,0 +1,117 @@ +From tomas.winkler@intel.com Thu Apr 29 13:14:48 2010 +From: David Woodhouse <dwmw2@infradead.org> +Date: Thu, 29 Apr 2010 21:26:17 +0300 +Subject: firmware_class: fix memory leak - free allocated pages +To: linux-kernel@vger.kernel.org, David Woodhouse <dwmw2@infradead.org>, Kay Sievers <kay.sievers@vrfy.org>, Greg Kroah-Hartman <gregkh@suse.de> +Cc: David Woodhouse <dwmw2@infradead.org>, Johannes Berg <johannes@sipsolutions.net>, Greg Kroah-Hartman <gregkh@suse.de>, Ming Lei <tom.leiming@gmail.com>, Catalin Marinas <catalin.marinas@arm.com>, Kay Sievers <kay.sievers@vrfy.org>, Tomas Winkler <tomas.winkler@intel.com> +Message-ID: <1272565577-26587-1-git-send-email-tomas.winkler@intel.com> + + +From: David Woodhouse <dwmw2@infradead.org> + +fix memory leak introduced by the patch 6e03a201bbe: +firmware: speed up request_firmware() + +1. vfree won't release pages there were allocated explicitly and mapped +using vmap. The memory has to be vunmap-ed and the pages needs +to be freed explicitly + +2. page array is moved into the 'struct +firmware' so that we can free it from release_firmware() +and not only in fw_dev_release() + +The fix doesn't break the firmware load speed. + +Cc: Johannes Berg <johannes@sipsolutions.net> +Cc: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Ming Lei <tom.leiming@gmail.com> +Cc: Catalin Marinas <catalin.marinas@arm.com> +Cc: Kay Sievers <kay.sievers@vrfy.org> +Signed-off-by: David Woodhouse <dwmw2@infradead.org> +Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/base/firmware_class.c | 28 ++++++++++++++++++++++------ + include/linux/firmware.h | 1 + + 2 files changed, 23 insertions(+), 6 deletions(-) + +--- a/drivers/base/firmware_class.c ++++ b/drivers/base/firmware_class.c +@@ -130,6 +130,20 @@ static ssize_t firmware_loading_show(str + return sprintf(buf, "%d\n", loading); + } + ++static void firmware_free_data(struct firmware *fw) ++{ ++ int i; ++ vunmap(fw->data); ++ fw->data = NULL; ++ if (fw->pages) { ++ for (i = 0; i < PFN_UP(fw->size); i++) ++ __free_page(fw->pages[i]); ++ kfree(fw->pages); ++ } ++ fw->pages = NULL; ++ fw->size = 0; ++} ++ + /* Some architectures don't have PAGE_KERNEL_RO */ + #ifndef PAGE_KERNEL_RO + #define PAGE_KERNEL_RO PAGE_KERNEL +@@ -162,21 +176,20 @@ static ssize_t firmware_loading_store(st + mutex_unlock(&fw_lock); + break; + } +- vfree(fw_priv->fw->data); +- fw_priv->fw->data = NULL; ++ firmware_free_data(fw_priv->fw); ++ /* If the pages are not owned by 'struct firmware' */ + for (i = 0; i < fw_priv->nr_pages; i++) + __free_page(fw_priv->pages[i]); + kfree(fw_priv->pages); + fw_priv->pages = NULL; + fw_priv->page_array_size = 0; + fw_priv->nr_pages = 0; +- fw_priv->fw->size = 0; + set_bit(FW_STATUS_LOADING, &fw_priv->status); + mutex_unlock(&fw_lock); + break; + case 0: + if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { +- vfree(fw_priv->fw->data); ++ vunmap(fw_priv->fw->data); + fw_priv->fw->data = vmap(fw_priv->pages, + fw_priv->nr_pages, + 0, PAGE_KERNEL_RO); +@@ -184,7 +197,10 @@ static ssize_t firmware_loading_store(st + dev_err(dev, "%s: vmap() failed\n", __func__); + goto err; + } +- /* Pages will be freed by vfree() */ ++ /* Pages are now owned by 'struct firmware' */ ++ fw_priv->fw->pages = fw_priv->pages; ++ fw_priv->pages = NULL; ++ + fw_priv->page_array_size = 0; + fw_priv->nr_pages = 0; + complete(&fw_priv->completion); +@@ -578,7 +594,7 @@ release_firmware(const struct firmware * + if (fw->data == builtin->data) + goto free_fw; + } +- vfree(fw->data); ++ firmware_free_data(fw); + free_fw: + kfree(fw); + } +--- a/include/linux/firmware.h ++++ b/include/linux/firmware.h +@@ -12,6 +12,7 @@ + struct firmware { + size_t size; + const u8 *data; ++ struct page **pages; + }; + + struct device; diff --git a/driver-core/driver-core-implement-ns-directory-support-for-device-classes.patch b/driver-core/driver-core-implement-ns-directory-support-for-device-classes.patch new file mode 100644 index 00000000000000..b23ca1bcaad90f --- /dev/null +++ b/driver-core/driver-core-implement-ns-directory-support-for-device-classes.patch @@ -0,0 +1,87 @@ +From ebiederm@xmission.com Thu Apr 29 12:47:44 2010 +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Tue, 30 Mar 2010 11:31:29 -0700 +Subject: driver core: Implement ns directory support for device classes. +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Kay Sievers <kay.sievers@vrfy.org>, linux-kernel@vger.kernel.org, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, linux-fsdevel@vger.kernel.org, Eric Dumazet <eric.dumazet@gmail.com>, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, <netdev@vger.kernel.org>, "Eric W. Biederman" <ebiederm@xmission.com>, Benjamin Thery <benjamin.thery@bull.net> +Message-ID: <1269973889-25260-6-git-send-email-ebiederm@xmission.com> + + +From: Eric W. Biederman <ebiederm@xmission.com> + +device_del and device_rename were modified to use +sysfs_delete_link and sysfs_rename_link respectively to ensure +when these operations happen on devices whose classes +are in namespace directories they work properly. + +Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> +Signed-off-by: Benjamin Thery <benjamin.thery@bull.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/base/core.c | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -786,7 +786,7 @@ out_device: + out_busid: + if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && + device_is_not_partition(dev)) +- sysfs_remove_link(&dev->class->p->class_subsys.kobj, ++ sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, + dev_name(dev)); + #else + /* link in the class directory pointing to the device */ +@@ -804,7 +804,7 @@ out_busid: + return 0; + + out_busid: +- sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev)); ++ sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev)); + #endif + + out_subsys: +@@ -832,13 +832,13 @@ static void device_remove_class_symlinks + + if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && + device_is_not_partition(dev)) +- sysfs_remove_link(&dev->class->p->class_subsys.kobj, ++ sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, + dev_name(dev)); + #else + if (dev->parent && device_is_not_partition(dev)) + sysfs_remove_link(&dev->kobj, "device"); + +- sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev)); ++ sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev)); + #endif + + sysfs_remove_link(&dev->kobj, "subsystem"); +@@ -1624,6 +1624,14 @@ int device_rename(struct device *dev, ch + goto out; + } + ++#ifndef CONFIG_SYSFS_DEPRECATED ++ if (dev->class) { ++ error = sysfs_rename_link(&dev->class->p->class_subsys.kobj, ++ &dev->kobj, old_device_name, new_name); ++ if (error) ++ goto out; ++ } ++#endif + error = kobject_rename(&dev->kobj, new_name); + if (error) + goto out; +@@ -1638,11 +1646,6 @@ int device_rename(struct device *dev, ch + new_class_name); + } + } +-#else +- if (dev->class) { +- error = sysfs_rename_link(&dev->class->p->class_subsys.kobj, +- &dev->kobj, old_device_name, new_name); +- } + #endif + + out: diff --git a/driver-core/firmware-loader-do-not-allocate-firmare-id-separately.patch b/driver-core/firmware-loader-do-not-allocate-firmare-id-separately.patch index 67ffdf600a2c29..5154435afaa0d9 100644 --- a/driver-core/firmware-loader-do-not-allocate-firmare-id-separately.patch +++ b/driver-core/firmware-loader-do-not-allocate-firmare-id-separately.patch @@ -46,7 +46,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> kfree(fw_priv); kfree(dev); -@@ -423,8 +421,8 @@ static int fw_register_device(struct dev +@@ -439,8 +437,8 @@ static int fw_register_device(struct dev struct device *device) { int retval; @@ -57,7 +57,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> struct device *f_dev = kzalloc(sizeof(*f_dev), GFP_KERNEL); *dev_p = NULL; -@@ -435,16 +433,9 @@ static int fw_register_device(struct dev +@@ -451,16 +449,9 @@ static int fw_register_device(struct dev goto error_kfree; } diff --git a/driver-core/firmware-loader-split-out-builtin-firmware-handling.patch b/driver-core/firmware-loader-split-out-builtin-firmware-handling.patch index 2f8ce7be128a28..b65bb10d0ea8f5 100644 --- a/driver-core/firmware-loader-split-out-builtin-firmware-handling.patch +++ b/driver-core/firmware-loader-split-out-builtin-firmware-handling.patch @@ -14,8 +14,8 @@ Signed-off-by: Dmitry Torokhov <dtor@mail.ru> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- - drivers/base/firmware_class.c | 77 +++++++++++++++++++++++++++--------------- - 1 file changed, 51 insertions(+), 26 deletions(-) + drivers/base/firmware_class.c | 76 +++++++++++++++++++++++++++--------------- + 1 file changed, 50 insertions(+), 26 deletions(-) --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -87,7 +87,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> static void fw_load_abort(struct firmware_priv *fw_priv) { -@@ -483,7 +521,6 @@ _request_firmware(const struct firmware +@@ -499,7 +537,6 @@ _request_firmware(const struct firmware struct device *f_dev; struct firmware_priv *fw_priv; struct firmware *firmware; @@ -95,7 +95,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> int retval; if (!firmware_p) -@@ -497,13 +534,8 @@ _request_firmware(const struct firmware +@@ -513,13 +550,8 @@ _request_firmware(const struct firmware goto out; } @@ -110,7 +110,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> return 0; } -@@ -575,19 +607,12 @@ request_firmware(const struct firmware * +@@ -591,19 +623,11 @@ request_firmware(const struct firmware * * release_firmware: - release the resource associated with a firmware image * @fw: firmware resource to release **/ @@ -126,11 +126,10 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> - if (fw->data == builtin->data) - goto free_fw; - } -- vfree(fw->data); +- firmware_free_data(fw); - free_fw: + if (!fw_is_builtin_firmware(fw)) -+ vfree(fw->data); -+ ++ firmware_free_data(fw); kfree(fw); } } diff --git a/driver-core/kobj-add-basic-infrastructure-for-dealing-with-namespaces.patch b/driver-core/kobj-add-basic-infrastructure-for-dealing-with-namespaces.patch new file mode 100644 index 00000000000000..4ae99590015fa9 --- /dev/null +++ b/driver-core/kobj-add-basic-infrastructure-for-dealing-with-namespaces.patch @@ -0,0 +1,323 @@ +From ebiederm@xmission.com Thu Apr 29 12:44:32 2010 +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Tue, 30 Mar 2010 11:31:25 -0700 +Subject: kobj: Add basic infrastructure for dealing with namespaces. +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Kay Sievers <kay.sievers@vrfy.org>, linux-kernel@vger.kernel.org, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, linux-fsdevel@vger.kernel.org, Eric Dumazet <eric.dumazet@gmail.com>, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, <netdev@vger.kernel.org>, "Eric W. Biederman" <ebiederm@xmission.com> +Message-ID: <1269973889-25260-2-git-send-email-ebiederm@xmission.com> + + +From: Eric W. Biederman <ebiederm@xmission.com> + +Move complete knowledge of namespaces into the kobject layer +so we can use that information when reporting kobjects to +userspace. + +Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/base/class.c | 9 ++++ + drivers/base/core.c | 77 +++++++++++++++++++++++++++++------ + include/linux/device.h | 3 + + include/linux/kobject.h | 26 ++++++++++++ + lib/kobject.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 204 insertions(+), 14 deletions(-) + +--- a/drivers/base/class.c ++++ b/drivers/base/class.c +@@ -63,6 +63,14 @@ static void class_release(struct kobject + kfree(cp); + } + ++static const struct kobj_ns_type_operations *class_child_ns_type(struct kobject *kobj) ++{ ++ struct class_private *cp = to_class(kobj); ++ struct class *class = cp->class; ++ ++ return class->ns_type; ++} ++ + static const struct sysfs_ops class_sysfs_ops = { + .show = class_attr_show, + .store = class_attr_store, +@@ -71,6 +79,7 @@ static const struct sysfs_ops class_sysf + static struct kobj_type class_ktype = { + .sysfs_ops = &class_sysfs_ops, + .release = class_release, ++ .child_ns_type = class_child_ns_type, + }; + + /* Hotplug events for classes go to the class class_subsys */ +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -131,9 +131,21 @@ static void device_release(struct kobjec + kfree(p); + } + ++static const void *device_namespace(struct kobject *kobj) ++{ ++ struct device *dev = to_dev(kobj); ++ const void *ns = NULL; ++ ++ if (dev->class && dev->class->ns_type) ++ ns = dev->class->namespace(dev); ++ ++ return ns; ++} ++ + static struct kobj_type device_ktype = { + .release = device_release, + .sysfs_ops = &dev_sysfs_ops, ++ .namespace = device_namespace, + }; + + +@@ -595,11 +607,59 @@ static struct kobject *virtual_device_pa + return virtual_dir; + } + +-static struct kobject *get_device_parent(struct device *dev, +- struct device *parent) ++struct class_dir { ++ struct kobject kobj; ++ struct class *class; ++}; ++ ++#define to_class_dir(obj) container_of(obj, struct class_dir, kobj) ++ ++static void class_dir_release(struct kobject *kobj) ++{ ++ struct class_dir *dir = to_class_dir(kobj); ++ kfree(dir); ++} ++ ++static const ++struct kobj_ns_type_operations *class_dir_child_ns_type(struct kobject *kobj) ++{ ++ struct class_dir *dir = to_class_dir(kobj); ++ return dir->class->ns_type; ++} ++ ++static struct kobj_type class_dir_ktype = { ++ .release = class_dir_release, ++ .sysfs_ops = &kobj_sysfs_ops, ++ .child_ns_type = class_dir_child_ns_type ++}; ++ ++static struct kobject * ++class_dir_create_and_add(struct class *class, struct kobject *parent_kobj) + { ++ struct class_dir *dir; + int retval; + ++ dir = kzalloc(sizeof(*dir), GFP_KERNEL); ++ if (!dir) ++ return NULL; ++ ++ dir->class = class; ++ kobject_init(&dir->kobj, &class_dir_ktype); ++ ++ dir->kobj.kset = &class->p->class_dirs; ++ ++ retval = kobject_add(&dir->kobj, parent_kobj, "%s", class->name); ++ if (retval < 0) { ++ kobject_put(&dir->kobj); ++ return NULL; ++ } ++ return &dir->kobj; ++} ++ ++ ++static struct kobject *get_device_parent(struct device *dev, ++ struct device *parent) ++{ + if (dev->class) { + static DEFINE_MUTEX(gdp_mutex); + struct kobject *kobj = NULL; +@@ -634,18 +694,7 @@ static struct kobject *get_device_parent + } + + /* or create a new class-directory at the parent device */ +- k = kobject_create(); +- if (!k) { +- mutex_unlock(&gdp_mutex); +- return NULL; +- } +- k->kset = &dev->class->p->class_dirs; +- retval = kobject_add(k, parent_kobj, "%s", dev->class->name); +- if (retval < 0) { +- mutex_unlock(&gdp_mutex); +- kobject_put(k); +- return NULL; +- } ++ k = class_dir_create_and_add(dev->class, parent_kobj); + /* do not emit an uevent for this simple "glue" directory */ + mutex_unlock(&gdp_mutex); + return k; +--- a/include/linux/device.h ++++ b/include/linux/device.h +@@ -202,6 +202,9 @@ struct class { + int (*suspend)(struct device *dev, pm_message_t state); + int (*resume)(struct device *dev); + ++ const struct kobj_ns_type_operations *ns_type; ++ const void *(*namespace)(struct device *dev); ++ + const struct dev_pm_ops *pm; + + struct class_private *p; +--- a/include/linux/kobject.h ++++ b/include/linux/kobject.h +@@ -108,6 +108,8 @@ struct kobj_type { + void (*release)(struct kobject *kobj); + const struct sysfs_ops *sysfs_ops; + struct attribute **default_attrs; ++ const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); ++ const void *(*namespace)(struct kobject *kobj); + }; + + struct kobj_uevent_env { +@@ -134,6 +136,30 @@ struct kobj_attribute { + + extern const struct sysfs_ops kobj_sysfs_ops; + ++enum kobj_ns_type { ++ KOBJ_NS_TYPE_NONE = 0, ++ KOBJ_NS_TYPES ++}; ++ ++struct sock; ++struct kobj_ns_type_operations { ++ enum kobj_ns_type type; ++ const void *(*current_ns)(void); ++ const void *(*netlink_ns)(struct sock *sk); ++ const void *(*initial_ns)(void); ++}; ++ ++int kobj_ns_type_register(const struct kobj_ns_type_operations *ops); ++int kobj_ns_type_registered(enum kobj_ns_type type); ++const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent); ++const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj); ++ ++const void *kobj_ns_current(enum kobj_ns_type type); ++const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk); ++const void *kobj_ns_initial(enum kobj_ns_type type); ++void kobj_ns_exit(enum kobj_ns_type type, const void *ns); ++ ++ + /** + * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. + * +--- a/lib/kobject.c ++++ b/lib/kobject.c +@@ -850,6 +850,109 @@ struct kset *kset_create_and_add(const c + } + EXPORT_SYMBOL_GPL(kset_create_and_add); + ++ ++static DEFINE_SPINLOCK(kobj_ns_type_lock); ++static const struct kobj_ns_type_operations *kobj_ns_ops_tbl[KOBJ_NS_TYPES]; ++ ++int kobj_ns_type_register(const struct kobj_ns_type_operations *ops) ++{ ++ enum kobj_ns_type type = ops->type; ++ int error; ++ ++ spin_lock(&kobj_ns_type_lock); ++ ++ error = -EINVAL; ++ if (type >= KOBJ_NS_TYPES) ++ goto out; ++ ++ error = -EINVAL; ++ if (type <= KOBJ_NS_TYPE_NONE) ++ goto out; ++ ++ error = -EBUSY; ++ if (kobj_ns_ops_tbl[type]) ++ goto out; ++ ++ error = 0; ++ kobj_ns_ops_tbl[type] = ops; ++ ++out: ++ spin_unlock(&kobj_ns_type_lock); ++ return error; ++} ++ ++int kobj_ns_type_registered(enum kobj_ns_type type) ++{ ++ int registered = 0; ++ ++ spin_lock(&kobj_ns_type_lock); ++ if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES)) ++ registered = kobj_ns_ops_tbl[type] != NULL; ++ spin_unlock(&kobj_ns_type_lock); ++ ++ return registered; ++} ++ ++const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent) ++{ ++ const struct kobj_ns_type_operations *ops = NULL; ++ ++ if (parent && parent->ktype->child_ns_type) ++ ops = parent->ktype->child_ns_type(parent); ++ ++ return ops; ++} ++ ++const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj) ++{ ++ return kobj_child_ns_ops(kobj->parent); ++} ++ ++ ++const void *kobj_ns_current(enum kobj_ns_type type) ++{ ++ const void *ns = NULL; ++ ++ spin_lock(&kobj_ns_type_lock); ++ if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && ++ kobj_ns_ops_tbl[type]) ++ ns = kobj_ns_ops_tbl[type]->current_ns(); ++ spin_unlock(&kobj_ns_type_lock); ++ ++ return ns; ++} ++ ++const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk) ++{ ++ const void *ns = NULL; ++ ++ spin_lock(&kobj_ns_type_lock); ++ if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && ++ kobj_ns_ops_tbl[type]) ++ ns = kobj_ns_ops_tbl[type]->netlink_ns(sk); ++ spin_unlock(&kobj_ns_type_lock); ++ ++ return ns; ++} ++ ++const void *kobj_ns_initial(enum kobj_ns_type type) ++{ ++ const void *ns = NULL; ++ ++ spin_lock(&kobj_ns_type_lock); ++ if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && ++ kobj_ns_ops_tbl[type]) ++ ns = kobj_ns_ops_tbl[type]->initial_ns(); ++ spin_unlock(&kobj_ns_type_lock); ++ ++ return ns; ++} ++ ++void kobj_ns_exit(enum kobj_ns_type type, const void *ns) ++{ ++} ++ ++ + EXPORT_SYMBOL(kobject_get); + EXPORT_SYMBOL(kobject_put); + EXPORT_SYMBOL(kobject_del); diff --git a/driver-core/sysfs-add-support-for-tagged-directories-with-untagged-members.patch b/driver-core/sysfs-add-support-for-tagged-directories-with-untagged-members.patch new file mode 100644 index 00000000000000..8351a25a5b197e --- /dev/null +++ b/driver-core/sysfs-add-support-for-tagged-directories-with-untagged-members.patch @@ -0,0 +1,78 @@ +From ebiederm@xmission.com Thu Apr 29 12:47:00 2010 +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Tue, 30 Mar 2010 11:31:27 -0700 +Subject: sysfs: Add support for tagged directories with untagged members. +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Kay Sievers <kay.sievers@vrfy.org>, linux-kernel@vger.kernel.org, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, linux-fsdevel@vger.kernel.org, Eric Dumazet <eric.dumazet@gmail.com>, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, <netdev@vger.kernel.org>, "Eric W. Biederman" <ebiederm@maxwell.aristanetworks.com>, "Eric W. Biederman" <ebiederm@aristanetworks.com> +Message-ID: <1269973889-25260-4-git-send-email-ebiederm@xmission.com> + + +From: Eric W. Biederman <ebiederm@maxwell.aristanetworks.com> + +I had hopped to avoid this but the bonding driver adds a file +to /sys/class/net/ and the easiest way to handle that file is +to make it untagged and to register it only once. + +So relax the rules on tagged directories, and make bonding work. + +Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + fs/sysfs/dir.c | 12 +++--------- + fs/sysfs/inode.c | 2 ++ + 2 files changed, 5 insertions(+), 9 deletions(-) + +--- a/fs/sysfs/dir.c ++++ b/fs/sysfs/dir.c +@@ -383,12 +383,6 @@ int __sysfs_add_one(struct sysfs_addrm_c + if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name)) + return -EEXIST; + +- if (sysfs_ns_type(acxt->parent_sd) && !sd->s_ns) { +- WARN(1, KERN_WARNING "sysfs: ns required in '%s' for '%s'\n", +- acxt->parent_sd->s_name, sd->s_name); +- return -EINVAL; +- } +- + sd->s_parent = sysfs_get(acxt->parent_sd); + + sysfs_link_sibling(sd); +@@ -545,7 +539,7 @@ struct sysfs_dirent *sysfs_find_dirent(s + struct sysfs_dirent *sd; + + for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) { +- if (sd->s_ns != ns) ++ if (ns && sd->s_ns && (sd->s_ns != ns)) + continue; + if (!strcmp(sd->s_name, name)) + return sd; +@@ -879,7 +873,7 @@ static struct sysfs_dirent *sysfs_dir_po + while (pos && (ino > pos->s_ino)) + pos = pos->s_sibling; + } +- while (pos && pos->s_ns != ns) ++ while (pos && pos->s_ns && pos->s_ns != ns) + pos = pos->s_sibling; + return pos; + } +@@ -890,7 +884,7 @@ static struct sysfs_dirent *sysfs_dir_ne + pos = sysfs_dir_pos(ns, parent_sd, ino, pos); + if (pos) + pos = pos->s_sibling; +- while (pos && pos->s_ns != ns) ++ while (pos && pos->s_ns && pos->s_ns != ns) + pos = pos->s_sibling; + return pos; + } +--- a/fs/sysfs/inode.c ++++ b/fs/sysfs/inode.c +@@ -335,6 +335,8 @@ int sysfs_hash_and_remove(struct sysfs_d + sysfs_addrm_start(&acxt, dir_sd); + + sd = sysfs_find_dirent(dir_sd, ns, name); ++ if (sd && (sd->s_ns != ns)) ++ sd = NULL; + if (sd) + sysfs_remove_one(&acxt, sd); + diff --git a/driver-core/sysfs-basic-support-for-multiple-super-blocks.patch b/driver-core/sysfs-basic-support-for-multiple-super-blocks.patch new file mode 100644 index 00000000000000..70a4e8f05ae023 --- /dev/null +++ b/driver-core/sysfs-basic-support-for-multiple-super-blocks.patch @@ -0,0 +1,110 @@ +From ebiederm@xmission.com Thu Apr 29 12:41:57 2010 +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Tue, 30 Mar 2010 11:31:24 -0700 +Subject: sysfs: Basic support for multiple super blocks +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Kay Sievers <kay.sievers@vrfy.org>, linux-kernel@vger.kernel.org, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, linux-fsdevel@vger.kernel.org, Eric Dumazet <eric.dumazet@gmail.com>, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, <netdev@vger.kernel.org>, "Eric W. Biederman" <ebiederm@xmission.com> +Message-ID: <1269973889-25260-1-git-send-email-ebiederm@xmission.com> + + +From: Eric W. Biederman <ebiederm@xmission.com> + +Add all of the necessary bioler plate to support +multiple superblocks in sysfs. + +Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> +Acked-by: Serge Hallyn <serue@us.ibm.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + fs/sysfs/mount.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- + fs/sysfs/sysfs.h | 3 ++ + 2 files changed, 59 insertions(+), 2 deletions(-) + +--- a/fs/sysfs/mount.c ++++ b/fs/sysfs/mount.c +@@ -72,16 +72,70 @@ static int sysfs_fill_super(struct super + return 0; + } + ++static int sysfs_test_super(struct super_block *sb, void *data) ++{ ++ struct sysfs_super_info *sb_info = sysfs_info(sb); ++ struct sysfs_super_info *info = data; ++ int found = 1; ++ return found; ++} ++ ++static int sysfs_set_super(struct super_block *sb, void *data) ++{ ++ int error; ++ error = set_anon_super(sb, data); ++ if (!error) ++ sb->s_fs_info = data; ++ return error; ++} ++ + static int sysfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) + { +- return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt); ++ struct sysfs_super_info *info; ++ struct super_block *sb; ++ int error; ++ ++ error = -ENOMEM; ++ info = kzalloc(sizeof(*info), GFP_KERNEL); ++ if (!info) ++ goto out; ++ sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info); ++ if (IS_ERR(sb) || sb->s_fs_info != info) ++ kfree(info); ++ if (IS_ERR(sb)) { ++ kfree(info); ++ error = PTR_ERR(sb); ++ goto out; ++ } ++ if (!sb->s_root) { ++ sb->s_flags = flags; ++ error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); ++ if (error) { ++ deactivate_locked_super(sb); ++ goto out; ++ } ++ sb->s_flags |= MS_ACTIVE; ++ } ++ ++ simple_set_mnt(mnt, sb); ++ error = 0; ++out: ++ return error; ++} ++ ++static void sysfs_kill_sb(struct super_block *sb) ++{ ++ struct sysfs_super_info *info = sysfs_info(sb); ++ ++ kill_anon_super(sb); ++ kfree(info); + } + + static struct file_system_type sysfs_fs_type = { + .name = "sysfs", + .get_sb = sysfs_get_sb, +- .kill_sb = kill_anon_super, ++ .kill_sb = sysfs_kill_sb, + }; + + int __init sysfs_init(void) +--- a/fs/sysfs/sysfs.h ++++ b/fs/sysfs/sysfs.h +@@ -114,6 +114,9 @@ struct sysfs_addrm_cxt { + /* + * mount.c + */ ++struct sysfs_super_info { ++}; ++#define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) + extern struct sysfs_dirent sysfs_root; + extern struct kmem_cache *sysfs_dir_cachep; + diff --git a/driver-core/sysfs-implement-sysfs-tagged-directory-support.patch b/driver-core/sysfs-implement-sysfs-tagged-directory-support.patch new file mode 100644 index 00000000000000..fac740e3898310 --- /dev/null +++ b/driver-core/sysfs-implement-sysfs-tagged-directory-support.patch @@ -0,0 +1,791 @@ +From ebiederm@xmission.com Thu Apr 29 12:46:06 2010 +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Tue, 30 Mar 2010 11:31:26 -0700 +Subject: sysfs: Implement sysfs tagged directory support. +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Kay Sievers <kay.sievers@vrfy.org>, linux-kernel@vger.kernel.org, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, linux-fsdevel@vger.kernel.org, Eric Dumazet <eric.dumazet@gmail.com>, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, <netdev@vger.kernel.org>, "Eric W. Biederman" <ebiederm@xmission.com>, Benjamin Thery <benjamin.thery@bull.net> +Message-ID: <1269973889-25260-3-git-send-email-ebiederm@xmission.com> + + +From: Eric W. Biederman <ebiederm@xmission.com> + +The problem. When implementing a network namespace I need to be able +to have multiple network devices with the same name. Currently this +is a problem for /sys/class/net/*, /sys/devices/virtual/net/*, and +potentially a few other directories of the form /sys/ ... /net/*. + +What this patch does is to add an additional tag field to the +sysfs dirent structure. For directories that should show different +contents depending on the context such as /sys/class/net/, and +/sys/devices/virtual/net/ this tag field is used to specify the +context in which those directories should be visible. Effectively +this is the same as creating multiple distinct directories with +the same name but internally to sysfs the result is nicer. + +I am calling the concept of a single directory that looks like multiple +directories all at the same path in the filesystem tagged directories. + +For the networking namespace the set of directories whose contents I need +to filter with tags can depend on the presence or absence of hotplug +hardware or which modules are currently loaded. Which means I need +a simple race free way to setup those directories as tagged. + +To achieve a reace free design all tagged directories are created +and managed by sysfs itself. + +Users of this interface: +- define a type in the sysfs_tag_type enumeration. +- call sysfs_register_ns_types with the type and it's operations +- sysfs_exit_ns when an individual tag is no longer valid + +- Implement mount_ns() which returns the ns of the calling process + so we can attach it to a sysfs superblock. +- Implement ktype.namespace() which returns the ns of a syfs kobject. + +Everything else is left up to sysfs and the driver layer. + +For the network namespace mount_ns and namespace() are essentially +one line functions, and look to remain that. + +Tags are currently represented a const void * pointers as that is +both generic, prevides enough information for equality comparisons, +and is trivial to create for current users, as it is just the +existing namespace pointer. + +The work needed in sysfs is more extensive. At each directory +or symlink creating I need to check if the directory it is being +created in is a tagged directory and if so generate the appropriate +tag to place on the sysfs_dirent. Likewise at each symlink or +directory removal I need to check if the sysfs directory it is +being removed from is a tagged directory and if so figure out +which tag goes along with the name I am deleting. + +Currently only directories which hold kobjects, and +symlinks are supported. There is not enough information +in the current file attribute interfaces to give us anything +to discriminate on which makes it useless, and there are +no potential users which makes it an uninteresting problem +to solve. + +Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> +Signed-off-by: Benjamin Thery <benjamin.thery@bull.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/gpio/gpiolib.c | 2 + drivers/md/bitmap.c | 4 - + drivers/md/md.c | 6 +- + fs/sysfs/bin.c | 2 + fs/sysfs/dir.c | 112 ++++++++++++++++++++++++++++++++++++++----------- + fs/sysfs/file.c | 17 ++++--- + fs/sysfs/group.c | 6 +- + fs/sysfs/inode.c | 4 - + fs/sysfs/mount.c | 33 ++++++++++++++ + fs/sysfs/symlink.c | 15 +++++- + fs/sysfs/sysfs.h | 20 +++++++- + include/linux/sysfs.h | 10 ++++ + lib/kobject.c | 1 + 13 files changed, 181 insertions(+), 51 deletions(-) + +--- a/drivers/gpio/gpiolib.c ++++ b/drivers/gpio/gpiolib.c +@@ -399,7 +399,7 @@ static int gpio_setup_irq(struct gpio_de + goto free_id; + } + +- pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value"); ++ pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value"); + if (!pdesc->value_sd) { + ret = -ENODEV; + goto free_id; +--- a/drivers/md/bitmap.c ++++ b/drivers/md/bitmap.c +@@ -1678,9 +1678,9 @@ int bitmap_create(mddev_t *mddev) + + bitmap->mddev = mddev; + +- bm = sysfs_get_dirent(mddev->kobj.sd, "bitmap"); ++ bm = sysfs_get_dirent(mddev->kobj.sd, NULL, "bitmap"); + if (bm) { +- bitmap->sysfs_can_clear = sysfs_get_dirent(bm, "can_clear"); ++ bitmap->sysfs_can_clear = sysfs_get_dirent(bm, NULL, "can_clear"); + sysfs_put(bm); + } else + bitmap->sysfs_can_clear = NULL; +--- a/drivers/md/md.c ++++ b/drivers/md/md.c +@@ -1766,7 +1766,7 @@ static int bind_rdev_to_array(mdk_rdev_t + kobject_del(&rdev->kobj); + goto fail; + } +- rdev->sysfs_state = sysfs_get_dirent(rdev->kobj.sd, "state"); ++ rdev->sysfs_state = sysfs_get_dirent(rdev->kobj.sd, NULL, "state"); + + list_add_rcu(&rdev->same_set, &mddev->disks); + bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk); +@@ -4183,7 +4183,7 @@ static int md_alloc(dev_t dev, char *nam + mutex_unlock(&disks_mutex); + if (!error) { + kobject_uevent(&mddev->kobj, KOBJ_ADD); +- mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, "array_state"); ++ mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, NULL, "array_state"); + } + mddev_put(mddev); + return error; +@@ -4392,7 +4392,7 @@ static int do_md_run(mddev_t * mddev) + printk(KERN_WARNING + "md: cannot register extra attributes for %s\n", + mdname(mddev)); +- mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, "sync_action"); ++ mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, NULL, "sync_action"); + } else if (mddev->ro == 2) /* auto-readonly not meaningful */ + mddev->ro = 0; + +--- a/fs/sysfs/bin.c ++++ b/fs/sysfs/bin.c +@@ -501,7 +501,7 @@ int sysfs_create_bin_file(struct kobject + void sysfs_remove_bin_file(struct kobject *kobj, + const struct bin_attribute *attr) + { +- sysfs_hash_and_remove(kobj->sd, attr->attr.name); ++ sysfs_hash_and_remove(kobj->sd, NULL, attr->attr.name); + } + + EXPORT_SYMBOL_GPL(sysfs_create_bin_file); +--- a/fs/sysfs/dir.c ++++ b/fs/sysfs/dir.c +@@ -380,9 +380,15 @@ int __sysfs_add_one(struct sysfs_addrm_c + { + struct sysfs_inode_attrs *ps_iattr; + +- if (sysfs_find_dirent(acxt->parent_sd, sd->s_name)) ++ if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name)) + return -EEXIST; + ++ if (sysfs_ns_type(acxt->parent_sd) && !sd->s_ns) { ++ WARN(1, KERN_WARNING "sysfs: ns required in '%s' for '%s'\n", ++ acxt->parent_sd->s_name, sd->s_name); ++ return -EINVAL; ++ } ++ + sd->s_parent = sysfs_get(acxt->parent_sd); + + sysfs_link_sibling(sd); +@@ -533,13 +539,17 @@ void sysfs_addrm_finish(struct sysfs_add + * Pointer to sysfs_dirent if found, NULL if not. + */ + struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, ++ const void *ns, + const unsigned char *name) + { + struct sysfs_dirent *sd; + +- for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) ++ for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) { ++ if (sd->s_ns != ns) ++ continue; + if (!strcmp(sd->s_name, name)) + return sd; ++ } + return NULL; + } + +@@ -558,12 +568,13 @@ struct sysfs_dirent *sysfs_find_dirent(s + * Pointer to sysfs_dirent if found, NULL if not. + */ + struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, ++ const void *ns, + const unsigned char *name) + { + struct sysfs_dirent *sd; + + mutex_lock(&sysfs_mutex); +- sd = sysfs_find_dirent(parent_sd, name); ++ sd = sysfs_find_dirent(parent_sd, ns, name); + sysfs_get(sd); + mutex_unlock(&sysfs_mutex); + +@@ -572,7 +583,8 @@ struct sysfs_dirent *sysfs_get_dirent(st + EXPORT_SYMBOL_GPL(sysfs_get_dirent); + + static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, +- const char *name, struct sysfs_dirent **p_sd) ++ enum kobj_ns_type type, const void *ns, const char *name, ++ struct sysfs_dirent **p_sd) + { + umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; + struct sysfs_addrm_cxt acxt; +@@ -583,6 +595,9 @@ static int create_dir(struct kobject *ko + sd = sysfs_new_dirent(name, mode, SYSFS_DIR); + if (!sd) + return -ENOMEM; ++ ++ sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT); ++ sd->s_ns = ns; + sd->s_dir.kobj = kobj; + + /* link in */ +@@ -601,7 +616,25 @@ static int create_dir(struct kobject *ko + int sysfs_create_subdir(struct kobject *kobj, const char *name, + struct sysfs_dirent **p_sd) + { +- return create_dir(kobj, kobj->sd, name, p_sd); ++ return create_dir(kobj, kobj->sd, ++ KOBJ_NS_TYPE_NONE, NULL, name, p_sd); ++} ++ ++static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj) ++{ ++ const struct kobj_ns_type_operations *ops; ++ enum kobj_ns_type type; ++ ++ ops = kobj_child_ns_ops(kobj); ++ if (!ops) ++ return KOBJ_NS_TYPE_NONE; ++ ++ type = ops->type; ++ BUG_ON(type <= KOBJ_NS_TYPE_NONE); ++ BUG_ON(type >= KOBJ_NS_TYPES); ++ BUG_ON(!kobj_ns_type_registered(type)); ++ ++ return type; + } + + /** +@@ -610,7 +643,9 @@ int sysfs_create_subdir(struct kobject * + */ + int sysfs_create_dir(struct kobject * kobj) + { ++ enum kobj_ns_type type; + struct sysfs_dirent *parent_sd, *sd; ++ const void *ns = NULL; + int error = 0; + + BUG_ON(!kobj); +@@ -620,7 +655,11 @@ int sysfs_create_dir(struct kobject * ko + else + parent_sd = &sysfs_root; + +- error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd); ++ if (sysfs_ns_type(parent_sd)) ++ ns = kobj->ktype->namespace(kobj); ++ type = sysfs_read_ns_type(kobj); ++ ++ error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd); + if (!error) + kobj->sd = sd; + return error; +@@ -630,13 +669,19 @@ static struct dentry * sysfs_lookup(stru + struct nameidata *nd) + { + struct dentry *ret = NULL; +- struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata; ++ struct dentry *parent = dentry->d_parent; ++ struct sysfs_dirent *parent_sd = parent->d_fsdata; + struct sysfs_dirent *sd; + struct inode *inode; ++ enum kobj_ns_type type; ++ const void *ns; + + mutex_lock(&sysfs_mutex); + +- sd = sysfs_find_dirent(parent_sd, dentry->d_name.name); ++ type = sysfs_ns_type(parent_sd); ++ ns = sysfs_info(dir->i_sb)->ns[type]; ++ ++ sd = sysfs_find_dirent(parent_sd, ns, dentry->d_name.name); + + /* no such entry */ + if (!sd) { +@@ -735,7 +780,8 @@ void sysfs_remove_dir(struct kobject * k + } + + int sysfs_rename(struct sysfs_dirent *sd, +- struct sysfs_dirent *new_parent_sd, const char *new_name) ++ struct sysfs_dirent *new_parent_sd, const void *new_ns, ++ const char *new_name) + { + const char *dup_name = NULL; + int error; +@@ -743,12 +789,12 @@ int sysfs_rename(struct sysfs_dirent *sd + mutex_lock(&sysfs_mutex); + + error = 0; +- if ((sd->s_parent == new_parent_sd) && ++ if ((sd->s_parent == new_parent_sd) && (sd->s_ns == new_ns) && + (strcmp(sd->s_name, new_name) == 0)) + goto out; /* nothing to rename */ + + error = -EEXIST; +- if (sysfs_find_dirent(new_parent_sd, new_name)) ++ if (sysfs_find_dirent(new_parent_sd, new_ns, new_name)) + goto out; + + /* rename sysfs_dirent */ +@@ -770,6 +816,7 @@ int sysfs_rename(struct sysfs_dirent *sd + sd->s_parent = new_parent_sd; + sysfs_link_sibling(sd); + } ++ sd->s_ns = new_ns; + + error = 0; + out: +@@ -780,19 +827,28 @@ int sysfs_rename(struct sysfs_dirent *sd + + int sysfs_rename_dir(struct kobject *kobj, const char *new_name) + { +- return sysfs_rename(kobj->sd, kobj->sd->s_parent, new_name); ++ struct sysfs_dirent *parent_sd = kobj->sd->s_parent; ++ const void *new_ns = NULL; ++ ++ if (sysfs_ns_type(parent_sd)) ++ new_ns = kobj->ktype->namespace(kobj); ++ ++ return sysfs_rename(kobj->sd, parent_sd, new_ns, new_name); + } + + int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj) + { + struct sysfs_dirent *sd = kobj->sd; + struct sysfs_dirent *new_parent_sd; ++ const void *new_ns = NULL; + + BUG_ON(!sd->s_parent); ++ if (sysfs_ns_type(sd->s_parent)) ++ new_ns = kobj->ktype->namespace(kobj); + new_parent_sd = new_parent_kobj && new_parent_kobj->sd ? + new_parent_kobj->sd : &sysfs_root; + +- return sysfs_rename(sd, new_parent_sd, sd->s_name); ++ return sysfs_rename(sd, new_parent_sd, new_ns, sd->s_name); + } + + /* Relationship between s_mode and the DT_xxx types */ +@@ -807,32 +863,35 @@ static int sysfs_dir_release(struct inod + return 0; + } + +-static struct sysfs_dirent *sysfs_dir_pos(struct sysfs_dirent *parent_sd, +- ino_t ino, struct sysfs_dirent *pos) ++static struct sysfs_dirent *sysfs_dir_pos(const void *ns, ++ struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) + { + if (pos) { + int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && + pos->s_parent == parent_sd && + ino == pos->s_ino; + sysfs_put(pos); +- if (valid) +- return pos; ++ if (!valid) ++ pos = NULL; + } +- pos = NULL; +- if ((ino > 1) && (ino < INT_MAX)) { ++ if (!pos && (ino > 1) && (ino < INT_MAX)) { + pos = parent_sd->s_dir.children; + while (pos && (ino > pos->s_ino)) + pos = pos->s_sibling; + } ++ while (pos && pos->s_ns != ns) ++ pos = pos->s_sibling; + return pos; + } + +-static struct sysfs_dirent *sysfs_dir_next_pos(struct sysfs_dirent *parent_sd, +- ino_t ino, struct sysfs_dirent *pos) ++static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns, ++ struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) + { +- pos = sysfs_dir_pos(parent_sd, ino, pos); ++ pos = sysfs_dir_pos(ns, parent_sd, ino, pos); + if (pos) + pos = pos->s_sibling; ++ while (pos && pos->s_ns != ns) ++ pos = pos->s_sibling; + return pos; + } + +@@ -841,8 +900,13 @@ static int sysfs_readdir(struct file * f + struct dentry *dentry = filp->f_path.dentry; + struct sysfs_dirent * parent_sd = dentry->d_fsdata; + struct sysfs_dirent *pos = filp->private_data; ++ enum kobj_ns_type type; ++ const void *ns; + ino_t ino; + ++ type = sysfs_ns_type(parent_sd); ++ ns = sysfs_info(dentry->d_sb)->ns[type]; ++ + if (filp->f_pos == 0) { + ino = parent_sd->s_ino; + if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) == 0) +@@ -857,9 +921,9 @@ static int sysfs_readdir(struct file * f + filp->f_pos++; + } + mutex_lock(&sysfs_mutex); +- for (pos = sysfs_dir_pos(parent_sd, filp->f_pos, pos); ++ for (pos = sysfs_dir_pos(ns, parent_sd, filp->f_pos, pos); + pos; +- pos = sysfs_dir_next_pos(parent_sd, filp->f_pos, pos)) { ++ pos = sysfs_dir_next_pos(ns, parent_sd, filp->f_pos, pos)) { + const char * name; + unsigned int type; + int len, ret; +--- a/fs/sysfs/file.c ++++ b/fs/sysfs/file.c +@@ -478,9 +478,12 @@ void sysfs_notify(struct kobject *k, con + mutex_lock(&sysfs_mutex); + + if (sd && dir) +- sd = sysfs_find_dirent(sd, dir); ++ /* Only directories are tagged, so no need to pass ++ * a tag explicitly. ++ */ ++ sd = sysfs_find_dirent(sd, NULL, dir); + if (sd && attr) +- sd = sysfs_find_dirent(sd, attr); ++ sd = sysfs_find_dirent(sd, NULL, attr); + if (sd) + sysfs_notify_dirent(sd); + +@@ -569,7 +572,7 @@ int sysfs_add_file_to_group(struct kobje + int error; + + if (group) +- dir_sd = sysfs_get_dirent(kobj->sd, group); ++ dir_sd = sysfs_get_dirent(kobj->sd, NULL, group); + else + dir_sd = sysfs_get(kobj->sd); + +@@ -599,7 +602,7 @@ int sysfs_chmod_file(struct kobject *kob + mutex_lock(&sysfs_mutex); + + rc = -ENOENT; +- sd = sysfs_find_dirent(kobj->sd, attr->name); ++ sd = sysfs_find_dirent(kobj->sd, NULL, attr->name); + if (!sd) + goto out; + +@@ -624,7 +627,7 @@ EXPORT_SYMBOL_GPL(sysfs_chmod_file); + + void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) + { +- sysfs_hash_and_remove(kobj->sd, attr->name); ++ sysfs_hash_and_remove(kobj->sd, NULL, attr->name); + } + + void sysfs_remove_files(struct kobject * kobj, const struct attribute **ptr) +@@ -646,11 +649,11 @@ void sysfs_remove_file_from_group(struct + struct sysfs_dirent *dir_sd; + + if (group) +- dir_sd = sysfs_get_dirent(kobj->sd, group); ++ dir_sd = sysfs_get_dirent(kobj->sd, NULL, group); + else + dir_sd = sysfs_get(kobj->sd); + if (dir_sd) { +- sysfs_hash_and_remove(dir_sd, attr->name); ++ sysfs_hash_and_remove(dir_sd, NULL, attr->name); + sysfs_put(dir_sd); + } + } +--- a/fs/sysfs/group.c ++++ b/fs/sysfs/group.c +@@ -23,7 +23,7 @@ static void remove_files(struct sysfs_di + int i; + + for (i = 0, attr = grp->attrs; *attr; i++, attr++) +- sysfs_hash_and_remove(dir_sd, (*attr)->name); ++ sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); + } + + static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, +@@ -39,7 +39,7 @@ static int create_files(struct sysfs_dir + * visibility. Do this by first removing then + * re-adding (if required) the file */ + if (update) +- sysfs_hash_and_remove(dir_sd, (*attr)->name); ++ sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); + if (grp->is_visible) { + mode = grp->is_visible(kobj, *attr, i); + if (!mode) +@@ -132,7 +132,7 @@ void sysfs_remove_group(struct kobject * + struct sysfs_dirent *sd; + + if (grp->name) { +- sd = sysfs_get_dirent(dir_sd, grp->name); ++ sd = sysfs_get_dirent(dir_sd, NULL, grp->name); + if (!sd) { + WARN(!sd, KERN_WARNING "sysfs group %p not found for " + "kobject '%s'\n", grp, kobject_name(kobj)); +--- a/fs/sysfs/inode.c ++++ b/fs/sysfs/inode.c +@@ -324,7 +324,7 @@ void sysfs_delete_inode(struct inode *in + sysfs_put(sd); + } + +-int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) ++int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns, const char *name) + { + struct sysfs_addrm_cxt acxt; + struct sysfs_dirent *sd; +@@ -334,7 +334,7 @@ int sysfs_hash_and_remove(struct sysfs_d + + sysfs_addrm_start(&acxt, dir_sd); + +- sd = sysfs_find_dirent(dir_sd, name); ++ sd = sysfs_find_dirent(dir_sd, ns, name); + if (sd) + sysfs_remove_one(&acxt, sd); + +--- a/fs/sysfs/mount.c ++++ b/fs/sysfs/mount.c +@@ -35,7 +35,7 @@ static const struct super_operations sys + struct sysfs_dirent sysfs_root = { + .s_name = "", + .s_count = ATOMIC_INIT(1), +- .s_flags = SYSFS_DIR, ++ .s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT), + .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, + .s_ino = 1, + }; +@@ -76,7 +76,13 @@ static int sysfs_test_super(struct super + { + struct sysfs_super_info *sb_info = sysfs_info(sb); + struct sysfs_super_info *info = data; ++ enum kobj_ns_type type; + int found = 1; ++ ++ for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { ++ if (sb_info->ns[type] != info->ns[type]) ++ found = 0; ++ } + return found; + } + +@@ -93,6 +99,7 @@ static int sysfs_get_sb(struct file_syst + int flags, const char *dev_name, void *data, struct vfsmount *mnt) + { + struct sysfs_super_info *info; ++ enum kobj_ns_type type; + struct super_block *sb; + int error; + +@@ -100,6 +107,10 @@ static int sysfs_get_sb(struct file_syst + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + goto out; ++ ++ for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) ++ info->ns[type] = kobj_ns_current(type); ++ + sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info); + if (IS_ERR(sb) || sb->s_fs_info != info) + kfree(info); +@@ -137,6 +148,26 @@ static struct file_system_type sysfs_fs_ + .kill_sb = sysfs_kill_sb, + }; + ++void sysfs_exit_ns(enum kobj_ns_type type, const void *ns) ++{ ++ struct super_block *sb; ++ ++ mutex_lock(&sysfs_mutex); ++ spin_lock(&sb_lock); ++ list_for_each_entry(sb, &sysfs_fs_type.fs_supers, s_instances) { ++ struct sysfs_super_info *info = sysfs_info(sb); ++ /* Ignore superblocks that are in the process of unmounting */ ++ if (sb->s_count <= S_BIAS) ++ continue; ++ /* Ignore superblocks with the wrong ns */ ++ if (info->ns[type] != ns) ++ continue; ++ info->ns[type] = NULL; ++ } ++ spin_unlock(&sb_lock); ++ mutex_unlock(&sysfs_mutex); ++} ++ + int __init sysfs_init(void) + { + int err = -ENOMEM; +--- a/fs/sysfs/symlink.c ++++ b/fs/sysfs/symlink.c +@@ -58,6 +58,8 @@ static int sysfs_do_create_link(struct k + if (!sd) + goto out_put; + ++ if (sysfs_ns_type(parent_sd)) ++ sd->s_ns = target->ktype->namespace(target); + sd->s_symlink.target_sd = target_sd; + target_sd = NULL; /* reference is now owned by the symlink */ + +@@ -121,7 +123,7 @@ void sysfs_remove_link(struct kobject * + else + parent_sd = kobj->sd; + +- sysfs_hash_and_remove(parent_sd, name); ++ sysfs_hash_and_remove(parent_sd, NULL, name); + } + + /** +@@ -137,6 +139,7 @@ int sysfs_rename_link(struct kobject *ko + const char *old, const char *new) + { + struct sysfs_dirent *parent_sd, *sd = NULL; ++ const void *old_ns = NULL, *new_ns = NULL; + int result; + + if (!kobj) +@@ -144,8 +147,11 @@ int sysfs_rename_link(struct kobject *ko + else + parent_sd = kobj->sd; + ++ if (targ->sd) ++ old_ns = targ->sd->s_ns; ++ + result = -ENOENT; +- sd = sysfs_get_dirent(parent_sd, old); ++ sd = sysfs_get_dirent(parent_sd, old_ns, old); + if (!sd) + goto out; + +@@ -155,7 +161,10 @@ int sysfs_rename_link(struct kobject *ko + if (sd->s_symlink.target_sd->s_dir.kobj != targ) + goto out; + +- result = sysfs_rename(sd, parent_sd, new); ++ if (sysfs_ns_type(parent_sd)) ++ new_ns = targ->ktype->namespace(targ); ++ ++ result = sysfs_rename(sd, parent_sd, new_ns, new); + + out: + sysfs_put(sd); +--- a/fs/sysfs/sysfs.h ++++ b/fs/sysfs/sysfs.h +@@ -58,6 +58,7 @@ struct sysfs_dirent { + struct sysfs_dirent *s_sibling; + const char *s_name; + ++ const void *s_ns; + union { + struct sysfs_elem_dir s_dir; + struct sysfs_elem_symlink s_symlink; +@@ -81,14 +82,22 @@ struct sysfs_dirent { + #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) + #define SYSFS_ACTIVE_REF (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR) + +-#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK +-#define SYSFS_FLAG_REMOVED 0x0200 ++#define SYSFS_NS_TYPE_MASK 0xff00 ++#define SYSFS_NS_TYPE_SHIFT 8 ++ ++#define SYSFS_FLAG_MASK ~(SYSFS_NS_TYPE_MASK|SYSFS_TYPE_MASK) ++#define SYSFS_FLAG_REMOVED 0x020000 + + static inline unsigned int sysfs_type(struct sysfs_dirent *sd) + { + return sd->s_flags & SYSFS_TYPE_MASK; + } + ++static inline enum kobj_ns_type sysfs_ns_type(struct sysfs_dirent *sd) ++{ ++ return (sd->s_flags & SYSFS_NS_TYPE_MASK) >> SYSFS_NS_TYPE_SHIFT; ++} ++ + #ifdef CONFIG_DEBUG_LOCK_ALLOC + #define sysfs_dirent_init_lockdep(sd) \ + do { \ +@@ -115,6 +124,7 @@ struct sysfs_addrm_cxt { + * mount.c + */ + struct sysfs_super_info { ++ const void *ns[KOBJ_NS_TYPES]; + }; + #define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) + extern struct sysfs_dirent sysfs_root; +@@ -140,8 +150,10 @@ void sysfs_remove_one(struct sysfs_addrm + void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); + + struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, ++ const void *ns, + const unsigned char *name); + struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, ++ const void *ns, + const unsigned char *name); + struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type); + +@@ -152,7 +164,7 @@ int sysfs_create_subdir(struct kobject * + void sysfs_remove_subdir(struct sysfs_dirent *sd); + + int sysfs_rename(struct sysfs_dirent *sd, +- struct sysfs_dirent *new_parent_sd, const char *new_name); ++ struct sysfs_dirent *new_parent_sd, const void *ns, const char *new_name); + + static inline struct sysfs_dirent *__sysfs_get(struct sysfs_dirent *sd) + { +@@ -182,7 +194,7 @@ int sysfs_setattr(struct dentry *dentry, + int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); + int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, + size_t size, int flags); +-int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name); ++int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns, const char *name); + int sysfs_inode_init(void); + + /* +--- a/include/linux/sysfs.h ++++ b/include/linux/sysfs.h +@@ -20,6 +20,7 @@ + + struct kobject; + struct module; ++enum kobj_ns_type; + + /* FIXME + * The *owner field is no longer used. +@@ -168,10 +169,14 @@ void sysfs_remove_file_from_group(struct + void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr); + void sysfs_notify_dirent(struct sysfs_dirent *sd); + struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, ++ const void *ns, + const unsigned char *name); + struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd); + void sysfs_put(struct sysfs_dirent *sd); + void sysfs_printk_last_file(void); ++ ++void sysfs_exit_ns(enum kobj_ns_type type, const void *tag); ++ + int __must_check sysfs_init(void); + + #else /* CONFIG_SYSFS */ +@@ -301,6 +306,7 @@ static inline void sysfs_notify_dirent(s + } + static inline + struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, ++ const void *ns, + const unsigned char *name) + { + return NULL; +@@ -313,6 +319,10 @@ static inline void sysfs_put(struct sysf + { + } + ++static inline void sysfs_exit_ns(enum kobj_ns_type type, const void *tag) ++{ ++} ++ + static inline int __must_check sysfs_init(void) + { + return 0; +--- a/lib/kobject.c ++++ b/lib/kobject.c +@@ -950,6 +950,7 @@ const void *kobj_ns_initial(enum kobj_ns + + void kobj_ns_exit(enum kobj_ns_type type, const void *ns) + { ++ sysfs_exit_ns(type, ns); + } + + diff --git a/driver-core/sysfs-implement-sysfs_delete_link.patch b/driver-core/sysfs-implement-sysfs_delete_link.patch new file mode 100644 index 00000000000000..6cbd3eece7cc63 --- /dev/null +++ b/driver-core/sysfs-implement-sysfs_delete_link.patch @@ -0,0 +1,92 @@ +From ebiederm@xmission.com Thu Apr 29 12:47:27 2010 +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Tue, 30 Mar 2010 11:31:28 -0700 +Subject: sysfs: Implement sysfs_delete_link +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Kay Sievers <kay.sievers@vrfy.org>, linux-kernel@vger.kernel.org, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, linux-fsdevel@vger.kernel.org, Eric Dumazet <eric.dumazet@gmail.com>, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, <netdev@vger.kernel.org>, "Eric W. Biederman" <ebiederm@xmission.com>, Benjamin Thery <benjamin.thery@bull.net>, Daniel Lezcano <dlezcano@fr.ibm.com> +Message-ID: <1269973889-25260-5-git-send-email-ebiederm@xmission.com> + + +From: Eric W. Biederman <ebiederm@xmission.com> + +When removing a symlink sysfs_remove_link does not provide +enough information to figure out which tagged directory the symlink +falls in. So I need sysfs_delete_link which is passed the target +of the symlink to delete. + +sysfs_rename_link is updated to call sysfs_delete_link instead +of sysfs_remove_link as we have all of the information necessary +and the callers are interesting. + +Both of these functions now have enough information to find a symlink +in a tagged directory. The only restriction is that they must be called +before the target kobject is renamed or deleted. If they are called +later I loose track of which tag the target kobject was marked with +and can no longer find the old symlink to remove it. + +This patch was split from an earlier patch. + +Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> +Signed-off-by: Benjamin Thery <benjamin.thery@bull.net> +Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com> +Acked-by: Tejun Heo <tj@kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + fs/sysfs/symlink.c | 20 ++++++++++++++++++++ + include/linux/sysfs.h | 8 ++++++++ + 2 files changed, 28 insertions(+) + +--- a/fs/sysfs/symlink.c ++++ b/fs/sysfs/symlink.c +@@ -109,6 +109,26 @@ int sysfs_create_link_nowarn(struct kobj + } + + /** ++ * sysfs_delete_link - remove symlink in object's directory. ++ * @kobj: object we're acting for. ++ * @targ: object we're pointing to. ++ * @name: name of the symlink to remove. ++ * ++ * Unlike sysfs_remove_link sysfs_delete_link has enough information ++ * to successfully delete symlinks in tagged directories. ++ */ ++void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, ++ const char *name) ++{ ++ const void *ns = NULL; ++ spin_lock(&sysfs_assoc_lock); ++ if (targ->sd) ++ ns = targ->sd->s_ns; ++ spin_unlock(&sysfs_assoc_lock); ++ sysfs_hash_and_remove(kobj->sd, ns, name); ++} ++ ++/** + * sysfs_remove_link - remove symlink in object's directory. + * @kobj: object we're acting for. + * @name: name of the symlink to remove. +--- a/include/linux/sysfs.h ++++ b/include/linux/sysfs.h +@@ -155,6 +155,9 @@ void sysfs_remove_link(struct kobject *k + int sysfs_rename_link(struct kobject *kobj, struct kobject *target, + const char *old_name, const char *new_name); + ++void sysfs_delete_link(struct kobject *dir, struct kobject *targ, ++ const char *name); ++ + int __must_check sysfs_create_group(struct kobject *kobj, + const struct attribute_group *grp); + int sysfs_update_group(struct kobject *kobj, +@@ -269,6 +272,11 @@ static inline int sysfs_rename_link(stru + return 0; + } + ++static inline void sysfs_delete_link(struct kobject *k, struct kobject *t, ++ const char *name) ++{ ++} ++ + static inline int sysfs_create_group(struct kobject *kobj, + const struct attribute_group *grp) + { diff --git a/driver-core/sysfs-remove-double-free-sysfs_get_sb.patch b/driver-core/sysfs-remove-double-free-sysfs_get_sb.patch new file mode 100644 index 00000000000000..4f229b848f524f --- /dev/null +++ b/driver-core/sysfs-remove-double-free-sysfs_get_sb.patch @@ -0,0 +1,27 @@ +From ebiederm@xmission.com Thu Apr 29 12:42:22 2010 +From: ebiederm@xmission.com (Eric W. Biederman) +Date: Tue, 30 Mar 2010 16:50:26 -0700 +Subject: sysfs: Remove double free sysfs_get_sb +To: Eric Dumazet <eric.dumazet@gmail.com> +Cc: Greg Kroah-Hartman <gregkh@suse.de>, Kay Sievers <kay.sievers@vrfy.org>, linux-kernel@vger.kernel.org, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, linux-fsdevel@vger.kernel.org, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, netdev@vger.kernel.org +Message-ID: <m1zl1pbc8t.fsf_-_@fess.ebiederm.org> + + + +Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + fs/sysfs/mount.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/fs/sysfs/mount.c ++++ b/fs/sysfs/mount.c +@@ -104,7 +104,6 @@ static int sysfs_get_sb(struct file_syst + if (IS_ERR(sb) || sb->s_fs_info != info) + kfree(info); + if (IS_ERR(sb)) { +- kfree(info); + error = PTR_ERR(sb); + goto out; + } @@ -14,6 +14,7 @@ driver-core.current/sysfs-use-sysfs_attr_init-in-asus-atk0110-driver.patch # round 2 driver-core.current/drivers-base-cpu.c-fix-the-output-from-sys-devices-system-cpu-offline.patch +driver-core.current/firmware_class-fix-memory-leak-free-allocated-pages.patch ################################# # TTY patches for 2.6.34 @@ -63,6 +64,7 @@ usb.current/usb-serial-option-add-cinterion-device-id.patch usb.current/usb-oxu210hp-release-spinlock-on-error-path.patch usb.current/usb-fix-build-on-omaps-if-config_pm_runtime-is-not-set.patch usb.current/usb-gadget-s3c-hsotg-add-missing-unlock.patch +usb.current/usb-ti_usb-fix-printk-format-warning.patch ################################# # Staging patches for 2.6.34 @@ -87,6 +89,7 @@ staging.current/staging-iio-fix-up-the-iio_get_new_idr_val-comment.patch staging.current/staging-batman-adv-don-t-have-interrupts-disabled-while-sending.patch staging.current/staging-batman-adv-fix-vis-output-bug-for-secondary-interfaces.patch staging.current/staging-batman-adv-fixing-wrap-around-bug-in-vis.patch +staging.current/staging-vme-re-introduce-necessary-brackets.patch ##################################################################### @@ -115,6 +118,14 @@ driver-core/firmware-loader-split-out-builtin-firmware-handling.patch driver-core/firmware-loader-do-not-allocate-firmare-id-separately.patch driver-core/driver-core-protect-device-shutdown-from-hot-unplug-events.patch +driver-core/sysfs-basic-support-for-multiple-super-blocks.patch +driver-core/sysfs-remove-double-free-sysfs_get_sb.patch +driver-core/kobj-add-basic-infrastructure-for-dealing-with-namespaces.patch +driver-core/sysfs-implement-sysfs-tagged-directory-support.patch +driver-core/sysfs-add-support-for-tagged-directories-with-untagged-members.patch +driver-core/sysfs-implement-sysfs_delete_link.patch +driver-core/driver-core-implement-ns-directory-support-for-device-classes.patch + ##################################### # TTY patches for after 2.6.34 is out ##################################### @@ -133,6 +144,7 @@ tty/serial-bfin_sport_uart-drop-the-experimental-markings.patch tty/serial-bfin_sport_uart-drop-redundant-cpu-depends.patch tty/serial-tty-new-ldiscs-for-staging.patch tty/tty-n_gsm-line-discipline.patch +tty/serial-uartlite-move-from-byte-accesses-to-word-accesses.patch @@ -190,6 +202,41 @@ usb/usb-serial-generalise-write-buffer-preparation.patch usb/usb-ftdi_sio-fix-some-coding-style-issues.patch usb/usb-gadget-add-hid-gadget-driver.patch usb/usb-option.c-option_indat_callback-resubmit-some-unsuccessful-urbs.patch +usb/usb-musb-debugfs-musb_exit_debugfs-can-be-called-on-probe.patch +usb/usb-musb-support-host-gadget-role-switching-on-blackfin-parts.patch +usb/musb-omap-remove-omap_vbus_power.patch +usb/musb-blackfin-remove-bfin_vbus_power.patch +usb/musb-omap-make-musb_platform_suspend-static.patch +usb/musb-blackfin-remove-musb_platform_-suspend-resume.patch +usb/usb-musb-omap2430-remove-unused-define.patch +usb/usb-musb-omap2430.c-remove-unnecessary-includes.patch +usb/usb-musb-makefile-remove-unexistent-config-option.patch +usb/usb-musb-add-extvbus-in-musb_board_data.patch +usb/usb-musb-add-ulpi-access-operations.patch +usb/usb-gadget-allow-larger-configuration-descriptors.patch +usb/usb-gadget-f_mass_storage-per-function.patch +usb/usb-otg-twl4030-use-the-global-ulpi-register-definitions.patch +usb/usb-pxa27x_udc-use-four-bits-to-store-endpoint-addresses.patch +usb/usb-fix-usbmon-and-dma-mapping-for-scatter-gather-urbs.patch +usb/usb-improve-runtime-remote-wakeup-settings.patch +usb/usb-don-t-enable-remote-wakeup-by-default.patch +usb/usb-use-pm-core-routines-to-enable-disable-autosuspend.patch +usb/usb-deprecate-the-power-level-sysfs-attribute.patch +usb/usb-serial-add-generic-usb-wwan-support.patch +usb/usb-option-use-generic-usb-wwan-code.patch +usb/usb-qcserial-use-generic-usb-wwan-code.patch +usb/usb-qcserial-add-support-for-qualcomm-gobi-2000-devices.patch +usb/usb-add-parsing-of-superspeed-endpoint-companion-descriptor.patch +usb/usb-add-stream-id-field-to-struct-urb.patch +usb/xhci-add-memory-allocation-for-usb3-bulk-streams.patch +usb/usb-xhci-correct-assumptions-about-number-of-rings-per-endpoint.patch +usb/usb-support-for-allocating-usb-3.0-streams.patch +usb/usb-storage-remove-unneeded-sl11r-unusual_devs-entry.patch +usb/usb-ehci-elide-i-o-watchdog-on-nec-parts.patch +usb/usb-omap-switch-to-subsys_initcall-for-isp1301-transceiver.patch +usb/usb-ueagle-fix-coding-styles.patch +usb/devices-fix-coding-styles.patch +usb/short-new-graph-for-usb-serial.txt.patch ####################################### # Staging stuff for after 2.6.34 is out @@ -199,3 +246,4 @@ usb/usb-option.c-option_indat_callback-resubmit-some-unsuccessful-urbs.patch # new stuff is in the staging-next git tree on git.kernel.org + diff --git a/staging.current/staging-rtl8192su-add-support-for-belkin-f5d8053-v6.patch b/staging.current/staging-rtl8192su-add-support-for-belkin-f5d8053-v6.patch index cb8a1e2b779204..5d2e741cb4e700 100644 --- a/staging.current/staging-rtl8192su-add-support-for-belkin-f5d8053-v6.patch +++ b/staging.current/staging-rtl8192su-add-support-for-belkin-f5d8053-v6.patch @@ -10,7 +10,7 @@ Please find attached a patch which adds the device ID for the Belkin F5D8053 v6 to the rtl8192su driver. I've tested this in 2.6.34-rc3 (Ubuntu 9.10 amd64) and the network adapter is working flawlessly. -From: Richard Airlie <richard@backtrace.co.uk> +Signed-off-by: Richard Airlie <richard@backtrace.co.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> diff --git a/staging.current/staging-vme-re-introduce-necessary-brackets.patch b/staging.current/staging-vme-re-introduce-necessary-brackets.patch new file mode 100644 index 00000000000000..d15b3e935d4a9c --- /dev/null +++ b/staging.current/staging-vme-re-introduce-necessary-brackets.patch @@ -0,0 +1,33 @@ +From martyn.welch@ge.com Mon Mar 22 08:00:56 2010 +From: Martyn Welch <martyn.welch@ge.com> +Date: Mon, 22 Mar 2010 14:58:43 +0000 +Subject: Staging: vme: Re-introduce necessary brackets +To: greg@kroah.com +Cc: devel@linuxdriverproject.org +Message-ID: <20100322145843.2209.75977.stgit@ES-J7S4D2J.amer.consind.ge.com> + + +Somehow I managed to remove a set of rather necessary brackets in commit +29848ac9f3b33bf171439ae2d66d40e6a71446c4. Put them back. + +Signed-off-by: Martyn Welch <martyn.welch@ge.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/staging/vme/bridges/vme_tsi148.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/staging/vme/bridges/vme_tsi148.c ++++ b/drivers/staging/vme/bridges/vme_tsi148.c +@@ -2455,9 +2455,10 @@ static int tsi148_probe(struct pci_dev * + dev_info(&pdev->dev, "VME Write and flush and error check is %s\n", + err_chk ? "enabled" : "disabled"); + +- if (tsi148_crcsr_init(tsi148_bridge, pdev)) ++ if (tsi148_crcsr_init(tsi148_bridge, pdev)) { + dev_err(&pdev->dev, "CR/CSR configuration failed.\n"); + goto err_crcsr; ++ } + + retval = vme_register_bridge(tsi148_bridge); + if (retval != 0) { diff --git a/tty/serial-uartlite-move-from-byte-accesses-to-word-accesses.patch b/tty/serial-uartlite-move-from-byte-accesses-to-word-accesses.patch new file mode 100644 index 00000000000000..93443b6c6d7744 --- /dev/null +++ b/tty/serial-uartlite-move-from-byte-accesses-to-word-accesses.patch @@ -0,0 +1,152 @@ +From john.linn@xilinx.com Thu Apr 29 13:07:20 2010 +From: John Linn <john.linn@xilinx.com> +Date: Wed, 7 Apr 2010 09:32:55 -0600 +Subject: serial: uartlite: move from byte accesses to word accesses +To: linux-serial@vger.kernel.org, jacmet@sunsite.dk, grant.likely@secretlab.ca, michal.simek@petalogix.com, gregkh@suse.de, john.williams@petalogix.com +Cc: John Linn <john.linn@xilinx.com> +Message-ID: <0c9c4833-e369-4c21-9def-b53100f885fa@SG2EHSMHS007.ehs.local> + + +Byte accesses for I/O devices in Xilinx IP is going to be less +desired in the future such that the driver is being changed to +use 32 bit accesses. + +This change facilitates using the uartlite IP over a PCIe bus +which only allows 32 bit accesses. + +Signed-off-by: John Linn <john.linn@xilinx.com> +Tested-by: Michal Simek <monstr@monstr.eu> +Acked-by: Grant Likely <grant.likely@secretlab.ca> +Acked-by: Peter Korsgaard <jacmet@sunsite.dk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/serial/uartlite.c | 32 ++++++++++++++++---------------- + 1 file changed, 16 insertions(+), 16 deletions(-) + +--- a/drivers/serial/uartlite.c ++++ b/drivers/serial/uartlite.c +@@ -86,7 +86,7 @@ static int ulite_receive(struct uart_por + /* stats */ + if (stat & ULITE_STATUS_RXVALID) { + port->icount.rx++; +- ch = readb(port->membase + ULITE_RX); ++ ch = ioread32be(port->membase + ULITE_RX); + + if (stat & ULITE_STATUS_PARITY) + port->icount.parity++; +@@ -131,7 +131,7 @@ static int ulite_transmit(struct uart_po + return 0; + + if (port->x_char) { +- writeb(port->x_char, port->membase + ULITE_TX); ++ iowrite32be(port->x_char, port->membase + ULITE_TX); + port->x_char = 0; + port->icount.tx++; + return 1; +@@ -140,7 +140,7 @@ static int ulite_transmit(struct uart_po + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return 0; + +- writeb(xmit->buf[xmit->tail], port->membase + ULITE_TX); ++ iowrite32be(xmit->buf[xmit->tail], port->membase + ULITE_TX); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); + port->icount.tx++; + +@@ -157,7 +157,7 @@ static irqreturn_t ulite_isr(int irq, vo + int busy, n = 0; + + do { +- int stat = readb(port->membase + ULITE_STATUS); ++ int stat = ioread32be(port->membase + ULITE_STATUS); + busy = ulite_receive(port, stat); + busy |= ulite_transmit(port, stat); + n++; +@@ -178,7 +178,7 @@ static unsigned int ulite_tx_empty(struc + unsigned int ret; + + spin_lock_irqsave(&port->lock, flags); +- ret = readb(port->membase + ULITE_STATUS); ++ ret = ioread32be(port->membase + ULITE_STATUS); + spin_unlock_irqrestore(&port->lock, flags); + + return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0; +@@ -201,7 +201,7 @@ static void ulite_stop_tx(struct uart_po + + static void ulite_start_tx(struct uart_port *port) + { +- ulite_transmit(port, readb(port->membase + ULITE_STATUS)); ++ ulite_transmit(port, ioread32be(port->membase + ULITE_STATUS)); + } + + static void ulite_stop_rx(struct uart_port *port) +@@ -230,17 +230,17 @@ static int ulite_startup(struct uart_por + if (ret) + return ret; + +- writeb(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, ++ iowrite32be(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, + port->membase + ULITE_CONTROL); +- writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); ++ iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + + return 0; + } + + static void ulite_shutdown(struct uart_port *port) + { +- writeb(0, port->membase + ULITE_CONTROL); +- readb(port->membase + ULITE_CONTROL); /* dummy */ ++ iowrite32be(0, port->membase + ULITE_CONTROL); ++ ioread32be(port->membase + ULITE_CONTROL); /* dummy */ + free_irq(port->irq, port); + } + +@@ -352,7 +352,7 @@ static void ulite_console_wait_tx(struct + + /* Spin waiting for TX fifo to have space available */ + for (i = 0; i < 100000; i++) { +- val = readb(port->membase + ULITE_STATUS); ++ val = ioread32be(port->membase + ULITE_STATUS); + if ((val & ULITE_STATUS_TXFULL) == 0) + break; + cpu_relax(); +@@ -362,7 +362,7 @@ static void ulite_console_wait_tx(struct + static void ulite_console_putchar(struct uart_port *port, int ch) + { + ulite_console_wait_tx(port); +- writeb(ch, port->membase + ULITE_TX); ++ iowrite32be(ch, port->membase + ULITE_TX); + } + + static void ulite_console_write(struct console *co, const char *s, +@@ -379,8 +379,8 @@ static void ulite_console_write(struct c + spin_lock_irqsave(&port->lock, flags); + + /* save and disable interrupt */ +- ier = readb(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; +- writeb(0, port->membase + ULITE_CONTROL); ++ ier = ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; ++ iowrite32be(0, port->membase + ULITE_CONTROL); + + uart_console_write(port, s, count, ulite_console_putchar); + +@@ -388,7 +388,7 @@ static void ulite_console_write(struct c + + /* restore interrupt state */ + if (ier) +- writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); ++ iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + + if (locked) + spin_unlock_irqrestore(&port->lock, flags); +@@ -601,7 +601,7 @@ ulite_of_probe(struct of_device *op, con + + id = of_get_property(op->node, "port-number", NULL); + +- return ulite_assign(&op->dev, id ? *id : -1, res.start+3, irq); ++ return ulite_assign(&op->dev, id ? *id : -1, res.start, irq); + } + + static int __devexit ulite_of_remove(struct of_device *op) diff --git a/usb.current/usb-ti_usb-fix-printk-format-warning.patch b/usb.current/usb-ti_usb-fix-printk-format-warning.patch new file mode 100644 index 00000000000000..49c66ad3d6c51b --- /dev/null +++ b/usb.current/usb-ti_usb-fix-printk-format-warning.patch @@ -0,0 +1,32 @@ +From randy.dunlap@oracle.com Thu Apr 29 13:13:38 2010 +From: Randy Dunlap <randy.dunlap@oracle.com> +Date: Thu, 15 Apr 2010 11:38:56 -0700 +Subject: USB: ti_usb: fix printk format warning +To: Stephen Rothwell <sfr@canb.auug.org.au>, linux-usb@vger.kernel.org, gregkh@suse.de +Message-ID: <20100415113856.45564d4f.randy.dunlap@oracle.com> + + +From: Randy Dunlap <randy.dunlap@oracle.com> + +Fix printk format warning in usbserial/ti_usb: + +drivers/usb/serial/ti_usb_3410_5052.c:1738: warning: format '%d' expects type 'int', but argument 5 has type 'size_t' + +Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/ti_usb_3410_5052.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/serial/ti_usb_3410_5052.c ++++ b/drivers/usb/serial/ti_usb_3410_5052.c +@@ -1735,7 +1735,7 @@ static int ti_download_firmware(struct t + return -ENOENT; + } + if (fw_p->size > TI_FIRMWARE_BUF_SIZE) { +- dev_err(&dev->dev, "%s - firmware too large %d \n", __func__, fw_p->size); ++ dev_err(&dev->dev, "%s - firmware too large %zu\n", __func__, fw_p->size); + return -ENOENT; + } + diff --git a/usb/devices-fix-coding-styles.patch b/usb/devices-fix-coding-styles.patch new file mode 100644 index 00000000000000..195090f8104c6a --- /dev/null +++ b/usb/devices-fix-coding-styles.patch @@ -0,0 +1,72 @@ +From csanchez@neurowork.net Thu Apr 29 13:12:22 2010 +From: "Carlos S�nchez Acosta" <csanchez@neurowork.net> +Date: Wed, 14 Apr 2010 06:58:53 -0500 +Subject: USB: devices: fix Coding Styles +To: "Greg KH" <gregkh@suse.de> +Message-ID: <b2ff38f0d4b7b167a0691997a238cb8e.squirrel@neurowork.net> + + +Fixed coding styles in the config usb driver. + +Signed-off-by: Carlos S�nchez Acosta <csanchez@neurowork.net> +Signed-off-by: Alejandro S�nchez Acosta <asanchez@neurowork.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/core/devices.c | 17 ++++++++++------- + 1 file changed, 10 insertions(+), 7 deletions(-) + +--- a/drivers/usb/core/devices.c ++++ b/drivers/usb/core/devices.c +@@ -1,7 +1,8 @@ + /* + * devices.c + * (C) Copyright 1999 Randy Dunlap. +- * (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. (proc file per device) ++ * (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. ++ * (proc file per device) + * (C) Copyright 1999 Deti Fliegl (new USB architecture) + * + * This program is free software; you can redistribute it and/or modify +@@ -56,7 +57,7 @@ + #include <linux/smp_lock.h> + #include <linux/usbdevice_fs.h> + #include <linux/mutex.h> +-#include <asm/uaccess.h> ++#include <linux/uaccess.h> + + #include "usb.h" + #include "hcd.h" +@@ -138,8 +139,8 @@ struct class_info { + char *class_name; + }; + +-static const struct class_info clas_info[] = +-{ /* max. 5 chars. per name string */ ++static const struct class_info clas_info[] = { ++ /* max. 5 chars. per name string */ + {USB_CLASS_PER_INTERFACE, ">ifc"}, + {USB_CLASS_AUDIO, "audio"}, + {USB_CLASS_COMM, "comm."}, +@@ -191,8 +192,10 @@ static char *usb_dump_endpoint_descripto + + if (speed == USB_SPEED_HIGH) { + switch (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) { +- case 1 << 11: bandwidth = 2; break; +- case 2 << 11: bandwidth = 3; break; ++ case 1 << 11: ++ bandwidth = 2; break; ++ case 2 << 11: ++ bandwidth = 3; break; + } + } + +@@ -200,7 +203,7 @@ static char *usb_dump_endpoint_descripto + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_CONTROL: + type = "Ctrl"; +- if (speed == USB_SPEED_HIGH) /* uframes per NAK */ ++ if (speed == USB_SPEED_HIGH) /* uframes per NAK */ + interval = desc->bInterval; + else + interval = 0; diff --git a/usb/musb-blackfin-remove-bfin_vbus_power.patch b/usb/musb-blackfin-remove-bfin_vbus_power.patch new file mode 100644 index 00000000000000..458891193faf56 --- /dev/null +++ b/usb/musb-blackfin-remove-bfin_vbus_power.patch @@ -0,0 +1,41 @@ +From felipe.balbi@nokia.com Thu Apr 29 12:36:20 2010 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 25 Mar 2010 13:25:21 +0200 +Subject: MUSB: Blackfin: remove bfin_vbus_power() +To: Greg KH <greg@kroah.com> +Message-ID: <1269516328-28267-5-git-send-email-felipe.balbi@nokia.com> + + +From: Sergei Shtylyov <sshtylyov@ru.mvista.com> + +This function does nothing... + +Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/blackfin.c | 5 ----- + 1 file changed, 5 deletions(-) + +--- a/drivers/usb/musb/blackfin.c ++++ b/drivers/usb/musb/blackfin.c +@@ -289,10 +289,6 @@ void musb_platform_disable(struct musb * + { + } + +-static void bfin_vbus_power(struct musb *musb, int is_on, int sleeping) +-{ +-} +- + static void bfin_set_vbus(struct musb *musb, int is_on) + { + int value = musb->config->gpio_vrsel_active; +@@ -410,7 +406,6 @@ int musb_platform_resume(struct musb *mu + int musb_platform_exit(struct musb *musb) + { + +- bfin_vbus_power(musb, 0 /*off*/, 1); + gpio_free(musb->config->gpio_vrsel); + musb_platform_suspend(musb); + diff --git a/usb/musb-blackfin-remove-musb_platform_-suspend-resume.patch b/usb/musb-blackfin-remove-musb_platform_-suspend-resume.patch new file mode 100644 index 00000000000000..3c1aa9be3023c9 --- /dev/null +++ b/usb/musb-blackfin-remove-musb_platform_-suspend-resume.patch @@ -0,0 +1,46 @@ +From felipe.balbi@nokia.com Thu Apr 29 12:37:02 2010 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 25 Mar 2010 13:25:23 +0200 +Subject: MUSB: Blackfin: remove musb_platform_{suspend|resume}() +To: Greg KH <greg@kroah.com> +Message-ID: <1269516328-28267-7-git-send-email-felipe.balbi@nokia.com> + + +From: Sergei Shtylyov <sshtylyov@ru.mvista.com> + +These functions do nothing and also are both unnecessarily 'extern'; actually, +musb_platform_resume() in not even called... + +Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/blackfin.c | 12 ------------ + 1 file changed, 12 deletions(-) + +--- a/drivers/usb/musb/blackfin.c ++++ b/drivers/usb/musb/blackfin.c +@@ -392,22 +392,10 @@ int __init musb_platform_init(struct mus + return 0; + } + +-int musb_platform_suspend(struct musb *musb) +-{ +- return 0; +-} +- +-int musb_platform_resume(struct musb *musb) +-{ +- return 0; +-} +- +- + int musb_platform_exit(struct musb *musb) + { + + gpio_free(musb->config->gpio_vrsel); +- musb_platform_suspend(musb); + + return 0; + } diff --git a/usb/musb-omap-make-musb_platform_suspend-static.patch b/usb/musb-omap-make-musb_platform_suspend-static.patch new file mode 100644 index 00000000000000..47fffe2a3a5f1e --- /dev/null +++ b/usb/musb-omap-make-musb_platform_suspend-static.patch @@ -0,0 +1,31 @@ +From felipe.balbi@nokia.com Thu Apr 29 12:36:51 2010 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 25 Mar 2010 13:25:22 +0200 +Subject: MUSB: OMAP: make musb_platform_suspend() 'static' +To: Greg KH <greg@kroah.com> +Message-ID: <1269516328-28267-6-git-send-email-felipe.balbi@nokia.com> + + +From: Sergei Shtylyov <sshtylyov@ru.mvista.com> + +This function is only called inside omap2430.c... + +Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/omap2430.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/musb/omap2430.c ++++ b/drivers/usb/musb/omap2430.c +@@ -275,7 +275,7 @@ void musb_platform_restore_context(struc + } + #endif + +-int musb_platform_suspend(struct musb *musb) ++static int musb_platform_suspend(struct musb *musb) + { + u32 l; + diff --git a/usb/musb-omap-remove-omap_vbus_power.patch b/usb/musb-omap-remove-omap_vbus_power.patch new file mode 100644 index 00000000000000..28ab0d92a05a83 --- /dev/null +++ b/usb/musb-omap-remove-omap_vbus_power.patch @@ -0,0 +1,51 @@ +From felipe.balbi@nokia.com Thu Apr 29 12:36:10 2010 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 25 Mar 2010 13:25:20 +0200 +Subject: MUSB: OMAP: remove omap_vbus_power() +To: Greg KH <greg@kroah.com> +Message-ID: <1269516328-28267-4-git-send-email-felipe.balbi@nokia.com> + + +From: Sergei Shtylyov <sshtylyov@ru.mvista.com> + +This function does nothing... + +Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/omap2430.c | 8 -------- + 1 file changed, 8 deletions(-) + +--- a/drivers/usb/musb/omap2430.c ++++ b/drivers/usb/musb/omap2430.c +@@ -145,10 +145,6 @@ void musb_platform_enable(struct musb *m + void musb_platform_disable(struct musb *musb) + { + } +-static void omap_vbus_power(struct musb *musb, int is_on, int sleeping) +-{ +-} +- + static void omap_set_vbus(struct musb *musb, int is_on) + { + u8 devctl; +@@ -255,8 +251,6 @@ int __init musb_platform_init(struct mus + musb_readl(musb->mregs, OTG_INTERFSEL), + musb_readl(musb->mregs, OTG_SIMENABLE)); + +- omap_vbus_power(musb, musb->board_mode == MUSB_HOST, 1); +- + if (is_host_enabled(musb)) + musb->board_set_vbus = omap_set_vbus; + +@@ -336,8 +330,6 @@ static int musb_platform_resume(struct m + int musb_platform_exit(struct musb *musb) + { + +- omap_vbus_power(musb, 0 /*off*/, 1); +- + musb_platform_suspend(musb); + + return 0; diff --git a/usb/short-new-graph-for-usb-serial.txt.patch b/usb/short-new-graph-for-usb-serial.txt.patch new file mode 100644 index 00000000000000..5c64137916e83a --- /dev/null +++ b/usb/short-new-graph-for-usb-serial.txt.patch @@ -0,0 +1,56 @@ +From esr@thyrsus.com Thu Apr 29 13:12:53 2010 +From: Eric Raymond <esr@thyrsus.com> +Date: Thu, 15 Apr 2010 01:35:50 -0400 +Subject: USB: Short new 'graph for usb-serial.txt +To: Greg KH <greg@kroah.com> +Cc: Eric Raymond <esr@snark.thyrsus.com> +Message-ID: <20100415053550.GA21098@thyrsus.com> +Content-Disposition: inline + +From: Eric Raymond <esr@thyrsus.com> + +Documentation update + + +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/usb/usb-serial.txt | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +--- a/Documentation/usb/usb-serial.txt ++++ b/Documentation/usb/usb-serial.txt +@@ -194,6 +194,10 @@ FTDI Single Port Serial Driver + + This is a single port DB-25 serial adapter. + ++ Devices supported include: ++ -TripNav TN-200 USB GPS ++ -Navis Engineering Bureau CH-4711 USB GPS ++ + For any questions or problems with this driver, please contact Bill Ryder. + + +@@ -216,7 +220,7 @@ Cypress M8 CY4601 Family Serial Driver + + Devices supported: + +- -DeLorme's USB Earthmate (SiRF Star II lp arch) ++ -DeLorme's USB Earthmate GPS (SiRF Star II lp arch) + -Cypress HID->COM RS232 adapter + + Note: Cypress Semiconductor claims no affiliation with the +@@ -392,9 +396,10 @@ REINER SCT cyberJack pinpad/e-com USB ch + Prolific PL2303 Driver + + This driver supports any device that has the PL2303 chip from Prolific +- in it. This includes a number of single port USB to serial +- converters and USB GPS devices. Devices from Aten (the UC-232) and +- IO-Data work with this driver, as does the DCU-11 mobile-phone cable. ++ in it. This includes a number of single port USB to serial converters, ++ more than 70% of USB GPS devices (in 2010), and some USB UPSes. Devices ++ from Aten (the UC-232) and IO-Data work with this driver, as does ++ the DCU-11 mobile-phone cable. + + For any questions or problems with this driver, please contact Greg + Kroah-Hartman at greg@kroah.com diff --git a/usb/usb-add-parsing-of-superspeed-endpoint-companion-descriptor.patch b/usb/usb-add-parsing-of-superspeed-endpoint-companion-descriptor.patch new file mode 100644 index 00000000000000..48a4f471e8e71c --- /dev/null +++ b/usb/usb-add-parsing-of-superspeed-endpoint-companion-descriptor.patch @@ -0,0 +1,33 @@ +From sarah.a.sharp@linux.intel.com Thu Apr 29 12:57:38 2010 +From: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Date: Fri, 2 Apr 2010 15:33:56 -0700 +Subject: USB: Add parsing of SuperSpeed endpoint companion descriptor. +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, usb-storage@lists.one-eyed-alien.net, Hrant Dalalyan <Hrant.Dalalyan@synopsys.com>, Alan Stern <stern@rowland.harvard.edu>, Paul Zimmerman <Paul.Zimmerman@synopsys.com>, Ashot Madatyan <Ashot.Madatyan@synopsys.com> +Message-ID: <20100402223356.GA1235@xanatos> +Content-Disposition: inline + + +Allow the xHCI drivers (and any new USB 3.0 drivers) to parse the +SuperSpeed endpoint companion descriptor to find the maximum number of +bulk endpoint streams the endpoint supports. This is used to calculate +the maximum total number of streams the driver can allocate. + +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + include/linux/usb/ch9.h | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/include/linux/usb/ch9.h ++++ b/include/linux/usb/ch9.h +@@ -556,6 +556,8 @@ struct usb_ss_ep_comp_descriptor { + } __attribute__ ((packed)); + + #define USB_DT_SS_EP_COMP_SIZE 6 ++/* Bits 4:0 of bmAttributes if this is a bulk endpoint */ ++#define USB_SS_MAX_STREAMS(p) (1 << (p & 0x1f)) + + /*-------------------------------------------------------------------------*/ + diff --git a/usb/usb-add-stream-id-field-to-struct-urb.patch b/usb/usb-add-stream-id-field-to-struct-urb.patch new file mode 100644 index 00000000000000..322927c15eccb1 --- /dev/null +++ b/usb/usb-add-stream-id-field-to-struct-urb.patch @@ -0,0 +1,34 @@ +From sarah.a.sharp@linux.intel.com Thu Apr 29 12:57:58 2010 +From: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Date: Fri, 2 Apr 2010 15:34:10 -0700 +Subject: USB: Add stream ID field to struct urb. +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, usb-storage@lists.one-eyed-alien.net, Hrant Dalalyan <Hrant.Dalalyan@synopsys.com>, Alan Stern <stern@rowland.harvard.edu>, Paul Zimmerman <Paul.Zimmerman@synopsys.com>, Ashot Madatyan <Ashot.Madatyan@synopsys.com> +Message-ID: <20100402223410.GA1399@xanatos> +Content-Disposition: inline + + +Bulk endpoint streams were added in the USB 3.0 specification. Streams +allow a device driver to overload a bulk endpoint so that multiple +transfers can be queued at once. + +Add a new field, stream_id, to struct urb so that USB 3.0 drivers can +specify which stream they want the URB to be queued to. + +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + include/linux/usb.h | 1 + + 1 file changed, 1 insertion(+) + +--- a/include/linux/usb.h ++++ b/include/linux/usb.h +@@ -1196,6 +1196,7 @@ struct urb { + struct usb_device *dev; /* (in) pointer to associated device */ + struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */ + unsigned int pipe; /* (in) pipe information */ ++ unsigned int stream_id; /* (in) stream ID */ + int status; /* (return) non-ISO status */ + unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/ + void *transfer_buffer; /* (in) associated data buffer */ diff --git a/usb/usb-deprecate-the-power-level-sysfs-attribute.patch b/usb/usb-deprecate-the-power-level-sysfs-attribute.patch new file mode 100644 index 00000000000000..2c8373abead109 --- /dev/null +++ b/usb/usb-deprecate-the-power-level-sysfs-attribute.patch @@ -0,0 +1,203 @@ +From stern@rowland.harvard.edu Thu Apr 29 12:52:02 2010 +From: Alan Stern <stern@rowland.harvard.edu> +Date: Fri, 2 Apr 2010 13:22:16 -0400 (EDT) +Subject: USB: deprecate the power/level sysfs attribute +To: Greg KH <greg@kroah.com> +Message-ID: <Pine.LNX.4.44L0.1004021314040.1324-100000@iolanthe.rowland.org> + + +This patch (as1367) deprecates USB's power/level sysfs attribute in +favor of the power/control attribute provided by the runtime PM core. +The two attributes do the same thing. + +It would be nice to replace power/level with a symlink to +power/control, but at the moment sysfs doesn't offer any way to do so. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/ABI/obsolete/sysfs-bus-usb | 31 +++++++++++++++++++++++++++++++ + Documentation/ABI/testing/sysfs-bus-usb | 28 ---------------------------- + Documentation/usb/power-management.txt | 19 +++++++++++-------- + drivers/usb/core/sysfs.c | 12 ++++++++++++ + 4 files changed, 54 insertions(+), 36 deletions(-) + +--- /dev/null ++++ b/Documentation/ABI/obsolete/sysfs-bus-usb +@@ -0,0 +1,31 @@ ++What: /sys/bus/usb/devices/.../power/level ++Date: March 2007 ++KernelVersion: 2.6.21 ++Contact: Alan Stern <stern@rowland.harvard.edu> ++Description: ++ Each USB device directory will contain a file named ++ power/level. This file holds a power-level setting for ++ the device, either "on" or "auto". ++ ++ "on" means that the device is not allowed to autosuspend, ++ although normal suspends for system sleep will still ++ be honored. "auto" means the device will autosuspend ++ and autoresume in the usual manner, according to the ++ capabilities of its driver. ++ ++ During normal use, devices should be left in the "auto" ++ level. The "on" level is meant for administrative uses. ++ If you want to suspend a device immediately but leave it ++ free to wake up in response to I/O requests, you should ++ write "0" to power/autosuspend. ++ ++ Device not capable of proper suspend and resume should be ++ left in the "on" level. Although the USB spec requires ++ devices to support suspend/resume, many of them do not. ++ In fact so many don't that by default, the USB core ++ initializes all non-hub devices in the "on" level. Some ++ drivers may change this setting when they are bound. ++ ++ This file is deprecated and will be removed after 2010. ++ Use the power/control file instead; it does exactly the ++ same thing. +--- a/Documentation/ABI/testing/sysfs-bus-usb ++++ b/Documentation/ABI/testing/sysfs-bus-usb +@@ -14,34 +14,6 @@ Description: + The autosuspend delay for newly-created devices is set to + the value of the usbcore.autosuspend module parameter. + +-What: /sys/bus/usb/devices/.../power/level +-Date: March 2007 +-KernelVersion: 2.6.21 +-Contact: Alan Stern <stern@rowland.harvard.edu> +-Description: +- Each USB device directory will contain a file named +- power/level. This file holds a power-level setting for +- the device, either "on" or "auto". +- +- "on" means that the device is not allowed to autosuspend, +- although normal suspends for system sleep will still +- be honored. "auto" means the device will autosuspend +- and autoresume in the usual manner, according to the +- capabilities of its driver. +- +- During normal use, devices should be left in the "auto" +- level. The "on" level is meant for administrative uses. +- If you want to suspend a device immediately but leave it +- free to wake up in response to I/O requests, you should +- write "0" to power/autosuspend. +- +- Device not capable of proper suspend and resume should be +- left in the "on" level. Although the USB spec requires +- devices to support suspend/resume, many of them do not. +- In fact so many don't that by default, the USB core +- initializes all non-hub devices in the "on" level. Some +- drivers may change this setting when they are bound. +- + What: /sys/bus/usb/devices/.../power/persist + Date: May 2007 + KernelVersion: 2.6.23 +--- a/Documentation/usb/power-management.txt ++++ b/Documentation/usb/power-management.txt +@@ -107,7 +107,9 @@ allowed to issue dynamic suspends. + The user interface for controlling dynamic PM is located in the power/ + subdirectory of each USB device's sysfs directory, that is, in + /sys/bus/usb/devices/.../power/ where "..." is the device's ID. The +-relevant attribute files are: wakeup, level, and autosuspend. ++relevant attribute files are: wakeup, control, and autosuspend. ++(There may also be a file named "level"; this file was deprecated ++as of the 2.6.35 kernel and replaced by the "control" file.) + + power/wakeup + +@@ -120,7 +122,7 @@ relevant attribute files are: wakeup, le + while the device is suspended, the change won't take + effect until the following suspend.) + +- power/level ++ power/control + + This file contains one of two words: "on" or "auto". + You can write those words to the file to change the +@@ -148,14 +150,15 @@ relevant attribute files are: wakeup, le + never to autosuspend. You can write a number to the + file to change the autosuspend idle-delay time. + +-Writing "-1" to power/autosuspend and writing "on" to power/level do ++Writing "-1" to power/autosuspend and writing "on" to power/control do + essentially the same thing -- they both prevent the device from being + autosuspended. Yes, this is a redundancy in the API. + + (In 2.6.21 writing "0" to power/autosuspend would prevent the device + from being autosuspended; the behavior was changed in 2.6.22. The + power/autosuspend attribute did not exist prior to 2.6.21, and the +-power/level attribute did not exist prior to 2.6.22.) ++power/level attribute did not exist prior to 2.6.22. power/control ++was added in 2.6.34.) + + + Changing the default idle-delay time +@@ -212,7 +215,7 @@ among printers and scanners, but plenty + the same deficiency. + + For this reason, by default the kernel disables autosuspend (the +-power/level attribute is initialized to "on") for all devices other ++power/control attribute is initialized to "on") for all devices other + than hubs. Hubs, at least, appear to be reasonably well-behaved in + this regard. + +@@ -373,7 +376,7 @@ usb_autopm_put_interface() in its close + patterns are possible. + + The autosuspend attempts mentioned above will often fail for one +-reason or another. For example, the power/level attribute might be ++reason or another. For example, the power/control attribute might be + set to "on", or another interface in the same device might not be + idle. This is perfectly normal. If the reason for failure was that + the device hasn't been idle for long enough, a timer is scheduled to +@@ -394,12 +397,12 @@ Drivers can enable autosuspend for their + + in their probe() routine, if they know that the device is capable of + suspending and resuming correctly. This is exactly equivalent to +-writing "auto" to the device's power/level attribute. Likewise, ++writing "auto" to the device's power/control attribute. Likewise, + drivers can disable autosuspend by calling + + usb_disable_autosuspend(struct usb_device *udev); + +-This is exactly the same as writing "on" to the power/level attribute. ++This is exactly the same as writing "on" to the power/control attribute. + + Sometimes a driver needs to make sure that remote wakeup is enabled + during autosuspend. For example, there's not much point +--- a/drivers/usb/core/sysfs.c ++++ b/drivers/usb/core/sysfs.c +@@ -383,12 +383,23 @@ static DEVICE_ATTR(autosuspend, S_IRUGO + static const char on_string[] = "on"; + static const char auto_string[] = "auto"; + ++static void warn_level(void) { ++ static int level_warned; ++ ++ if (!level_warned) { ++ level_warned = 1; ++ printk(KERN_WARNING "WARNING! power/level is deprecated; " ++ "use power/control instead\n"); ++ } ++} ++ + static ssize_t + show_level(struct device *dev, struct device_attribute *attr, char *buf) + { + struct usb_device *udev = to_usb_device(dev); + const char *p = auto_string; + ++ warn_level(); + if (udev->state != USB_STATE_SUSPENDED && !udev->dev.power.runtime_auto) + p = on_string; + return sprintf(buf, "%s\n", p); +@@ -403,6 +414,7 @@ set_level(struct device *dev, struct dev + char *cp; + int rc = count; + ++ warn_level(); + cp = memchr(buf, '\n', count); + if (cp) + len = cp - buf; diff --git a/usb/usb-don-t-enable-remote-wakeup-by-default.patch b/usb/usb-don-t-enable-remote-wakeup-by-default.patch new file mode 100644 index 00000000000000..0eef17f4964813 --- /dev/null +++ b/usb/usb-don-t-enable-remote-wakeup-by-default.patch @@ -0,0 +1,36 @@ +From stern@rowland.harvard.edu Thu Apr 29 12:51:34 2010 +From: Alan Stern <stern@rowland.harvard.edu> +Date: Fri, 2 Apr 2010 13:21:33 -0400 (EDT) +Subject: USB: don't enable remote wakeup by default +To: Greg KH <greg@kroah.com> +Message-ID: <Pine.LNX.4.44L0.1004021309260.1324-100000@iolanthe.rowland.org> + + +This patch (as1364) avoids enabling remote wakeup by default on all +non-root-hub USB devices. Individual drivers or userspace will have +to enable it wherever it is needed, such as for keyboards or network +interfaces. Note: This affects only system sleep, not autosuspend. + +External hubs will continue to relay wakeup requests received from +downstream through their upstream port, even when remote wakeup is not +enabled for the hub itself. Disabling remote wakeup on a hub merely +prevents it from generating wakeup requests in response to connect, +disconnect, and overcurrent events. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/core/hub.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -1784,7 +1784,6 @@ int usb_new_device(struct usb_device *ud + * sysfs power/wakeup controls wakeup enabled/disabled + */ + device_init_wakeup(&udev->dev, 0); +- device_set_wakeup_enable(&udev->dev, 1); + } + + /* Tell the runtime-PM framework the device is active */ diff --git a/usb/usb-ehci-elide-i-o-watchdog-on-nec-parts.patch b/usb/usb-ehci-elide-i-o-watchdog-on-nec-parts.patch new file mode 100644 index 00000000000000..86de5bc11b377a --- /dev/null +++ b/usb/usb-ehci-elide-i-o-watchdog-on-nec-parts.patch @@ -0,0 +1,34 @@ +From davem@davemloft.net Thu Apr 29 13:05:42 2010 +From: David Miller <davem@davemloft.net> +Date: Tue, 06 Apr 2010 18:26:03 -0700 (PDT) +Subject: USB: ehci: Elide I/O watchdog on NEC parts +To: linux-usb@vger.kernel.org +Cc: dbrownell@users.sourceforge.net +Message-ID: <20100406.182603.267636346.davem@davemloft.net> + + + +I've been running with this patch on my Niagara2 boxes for some time +and have not seen any ill effects yet. Maybe we can stash this into +the USB tree to get exposure for some time in -next and if anything +crops up we can simply revert? + +Signed-off-by: David S. Miller <davem@davemloft.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ehci-pci.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/usb/host/ehci-pci.c ++++ b/drivers/usb/host/ehci-pci.c +@@ -109,6 +109,9 @@ static int ehci_pci_setup(struct usb_hcd + return retval; + + switch (pdev->vendor) { ++ case PCI_VENDOR_ID_NEC: ++ ehci->need_io_watchdog = 0; ++ break; + case PCI_VENDOR_ID_INTEL: + ehci->need_io_watchdog = 0; + if (pdev->device == 0x27cc) { diff --git a/usb/usb-fix-usbmon-and-dma-mapping-for-scatter-gather-urbs.patch b/usb/usb-fix-usbmon-and-dma-mapping-for-scatter-gather-urbs.patch new file mode 100644 index 00000000000000..1bd7002f0875ba --- /dev/null +++ b/usb/usb-fix-usbmon-and-dma-mapping-for-scatter-gather-urbs.patch @@ -0,0 +1,508 @@ +From stern@rowland.harvard.edu Thu Apr 29 12:50:58 2010 +From: Alan Stern <stern@rowland.harvard.edu> +Date: Fri, 2 Apr 2010 13:27:28 -0400 (EDT) +Subject: USB: fix usbmon and DMA mapping for scatter-gather URBs +To: Greg KH <greg@kroah.com> +Cc: David Vrabel <david.vrabel@csr.com>, Sarah Sharp <sarah.a.sharp@linux.intel.com> +Message-ID: <Pine.LNX.4.44L0.1004021324010.1324-100000@iolanthe.rowland.org> + + +This patch (as1368) fixes a rather obscure bug in usbmon: When tracing +URBs sent by the scatter-gather library, it accesses the data buffers +while they are still mapped for DMA. + +The solution is to move the mapping and unmapping out of the s-g +library and into the usual place in hcd.c. This requires the addition +of new URB flag bits to describe the kind of mapping needed, since we +have to call dma_map_sg() if the HCD supports native scatter-gather +operation and dma_map_page() if it doesn't. The nice thing about +having the new flags is that they simplify the testing for unmapping. + +The patch removes the only caller of usb_buffer_[un]map_sg(), so those +functions are #if'ed out. A later patch will remove them entirely. + +As a result of this change, urb->sg will be set in situations where +it wasn't set previously. Hence the xhci and whci drivers are +adjusted to test urb->num_sgs instead, which retains its original +meaning and is nonzero only when the HCD has to handle a scatterlist. + +Finally, even when a submission error occurs we don't want to hand +URBs to usbmon before they are unmapped. The submission path is +rearranged so that map_urb_for_dma() is called only for non-root-hub +URBs and unmap_urb_for_dma() is called immediately after a submission +error. This simplifies the error handling. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +CC: <stable@kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/usb/core/hcd.c | 169 ++++++++++++++++++++++++++----------------- + drivers/usb/core/message.c | 45 ++--------- + drivers/usb/core/urb.c | 9 +- + drivers/usb/core/usb.c | 4 + + drivers/usb/host/whci/qset.c | 2 + drivers/usb/host/xhci-ring.c | 2 + drivers/usb/mon/mon_bin.c | 2 + drivers/usb/mon/mon_text.c | 4 - + include/linux/usb.h | 9 ++ + 9 files changed, 138 insertions(+), 108 deletions(-) + +--- a/drivers/usb/core/hcd.c ++++ b/drivers/usb/core/hcd.c +@@ -1260,6 +1260,51 @@ static void hcd_free_coherent(struct usb + *dma_handle = 0; + } + ++static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) ++{ ++ enum dma_data_direction dir; ++ ++ if (urb->transfer_flags & URB_SETUP_MAP_SINGLE) ++ dma_unmap_single(hcd->self.controller, ++ urb->setup_dma, ++ sizeof(struct usb_ctrlrequest), ++ DMA_TO_DEVICE); ++ else if (urb->transfer_flags & URB_SETUP_MAP_LOCAL) ++ hcd_free_coherent(urb->dev->bus, ++ &urb->setup_dma, ++ (void **) &urb->setup_packet, ++ sizeof(struct usb_ctrlrequest), ++ DMA_TO_DEVICE); ++ ++ dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; ++ if (urb->transfer_flags & URB_DMA_MAP_SG) ++ dma_unmap_sg(hcd->self.controller, ++ urb->sg->sg, ++ urb->num_sgs, ++ dir); ++ else if (urb->transfer_flags & URB_DMA_MAP_PAGE) ++ dma_unmap_page(hcd->self.controller, ++ urb->transfer_dma, ++ urb->transfer_buffer_length, ++ dir); ++ else if (urb->transfer_flags & URB_DMA_MAP_SINGLE) ++ dma_unmap_single(hcd->self.controller, ++ urb->transfer_dma, ++ urb->transfer_buffer_length, ++ dir); ++ else if (urb->transfer_flags & URB_MAP_LOCAL) ++ hcd_free_coherent(urb->dev->bus, ++ &urb->transfer_dma, ++ &urb->transfer_buffer, ++ urb->transfer_buffer_length, ++ dir); ++ ++ /* Make it safe to call this routine more than once */ ++ urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL | ++ URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | ++ URB_DMA_MAP_SINGLE | URB_MAP_LOCAL); ++} ++ + static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) + { +@@ -1271,8 +1316,6 @@ static int map_urb_for_dma(struct usb_hc + * unless it uses pio or talks to another transport, + * or uses the provided scatter gather list for bulk. + */ +- if (is_root_hub(urb->dev)) +- return 0; + + if (usb_endpoint_xfer_control(&urb->ep->desc) + && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) { +@@ -1285,6 +1328,7 @@ static int map_urb_for_dma(struct usb_hc + if (dma_mapping_error(hcd->self.controller, + urb->setup_dma)) + return -EAGAIN; ++ urb->transfer_flags |= URB_SETUP_MAP_SINGLE; + } else if (hcd->driver->flags & HCD_LOCAL_MEM) + ret = hcd_alloc_coherent( + urb->dev->bus, mem_flags, +@@ -1292,20 +1336,57 @@ static int map_urb_for_dma(struct usb_hc + (void **)&urb->setup_packet, + sizeof(struct usb_ctrlrequest), + DMA_TO_DEVICE); ++ if (ret) ++ return ret; ++ urb->transfer_flags |= URB_SETUP_MAP_LOCAL; + } + + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; +- if (ret == 0 && urb->transfer_buffer_length != 0 ++ if (urb->transfer_buffer_length != 0 + && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { + if (hcd->self.uses_dma) { +- urb->transfer_dma = dma_map_single ( +- hcd->self.controller, +- urb->transfer_buffer, +- urb->transfer_buffer_length, +- dir); +- if (dma_mapping_error(hcd->self.controller, ++ if (urb->num_sgs) { ++ int n = dma_map_sg( ++ hcd->self.controller, ++ urb->sg->sg, ++ urb->num_sgs, ++ dir); ++ if (n <= 0) ++ ret = -EAGAIN; ++ else ++ urb->transfer_flags |= URB_DMA_MAP_SG; ++ if (n != urb->num_sgs) { ++ urb->num_sgs = n; ++ urb->transfer_flags |= ++ URB_DMA_SG_COMBINED; ++ } ++ } else if (urb->sg) { ++ struct scatterlist *sg; ++ ++ sg = (struct scatterlist *) urb->sg; ++ urb->transfer_dma = dma_map_page( ++ hcd->self.controller, ++ sg_page(sg), ++ sg->offset, ++ urb->transfer_buffer_length, ++ dir); ++ if (dma_mapping_error(hcd->self.controller, + urb->transfer_dma)) +- return -EAGAIN; ++ ret = -EAGAIN; ++ else ++ urb->transfer_flags |= URB_DMA_MAP_PAGE; ++ } else { ++ urb->transfer_dma = dma_map_single( ++ hcd->self.controller, ++ urb->transfer_buffer, ++ urb->transfer_buffer_length, ++ dir); ++ if (dma_mapping_error(hcd->self.controller, ++ urb->transfer_dma)) ++ ret = -EAGAIN; ++ else ++ urb->transfer_flags |= URB_DMA_MAP_SINGLE; ++ } + } else if (hcd->driver->flags & HCD_LOCAL_MEM) { + ret = hcd_alloc_coherent( + urb->dev->bus, mem_flags, +@@ -1313,55 +1394,16 @@ static int map_urb_for_dma(struct usb_hc + &urb->transfer_buffer, + urb->transfer_buffer_length, + dir); +- +- if (ret && usb_endpoint_xfer_control(&urb->ep->desc) +- && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) +- hcd_free_coherent(urb->dev->bus, +- &urb->setup_dma, +- (void **)&urb->setup_packet, +- sizeof(struct usb_ctrlrequest), +- DMA_TO_DEVICE); ++ if (ret == 0) ++ urb->transfer_flags |= URB_MAP_LOCAL; + } ++ if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE | ++ URB_SETUP_MAP_LOCAL))) ++ unmap_urb_for_dma(hcd, urb); + } + return ret; + } + +-static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +-{ +- enum dma_data_direction dir; +- +- if (is_root_hub(urb->dev)) +- return; +- +- if (usb_endpoint_xfer_control(&urb->ep->desc) +- && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) { +- if (hcd->self.uses_dma) +- dma_unmap_single(hcd->self.controller, urb->setup_dma, +- sizeof(struct usb_ctrlrequest), +- DMA_TO_DEVICE); +- else if (hcd->driver->flags & HCD_LOCAL_MEM) +- hcd_free_coherent(urb->dev->bus, &urb->setup_dma, +- (void **)&urb->setup_packet, +- sizeof(struct usb_ctrlrequest), +- DMA_TO_DEVICE); +- } +- +- dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; +- if (urb->transfer_buffer_length != 0 +- && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { +- if (hcd->self.uses_dma) +- dma_unmap_single(hcd->self.controller, +- urb->transfer_dma, +- urb->transfer_buffer_length, +- dir); +- else if (hcd->driver->flags & HCD_LOCAL_MEM) +- hcd_free_coherent(urb->dev->bus, &urb->transfer_dma, +- &urb->transfer_buffer, +- urb->transfer_buffer_length, +- dir); +- } +-} +- + /*-------------------------------------------------------------------------*/ + + /* may be called in any context with a valid urb->dev usecount +@@ -1390,21 +1432,20 @@ int usb_hcd_submit_urb (struct urb *urb, + * URBs must be submitted in process context with interrupts + * enabled. + */ +- status = map_urb_for_dma(hcd, urb, mem_flags); +- if (unlikely(status)) { +- usbmon_urb_submit_error(&hcd->self, urb, status); +- goto error; +- } + +- if (is_root_hub(urb->dev)) ++ if (is_root_hub(urb->dev)) { + status = rh_urb_enqueue(hcd, urb); +- else +- status = hcd->driver->urb_enqueue(hcd, urb, mem_flags); ++ } else { ++ status = map_urb_for_dma(hcd, urb, mem_flags); ++ if (likely(status == 0)) { ++ status = hcd->driver->urb_enqueue(hcd, urb, mem_flags); ++ if (unlikely(status)) ++ unmap_urb_for_dma(hcd, urb); ++ } ++ } + + if (unlikely(status)) { + usbmon_urb_submit_error(&hcd->self, urb, status); +- unmap_urb_for_dma(hcd, urb); +- error: + urb->hcpriv = NULL; + INIT_LIST_HEAD(&urb->urb_list); + atomic_dec(&urb->use_count); +--- a/drivers/usb/core/message.c ++++ b/drivers/usb/core/message.c +@@ -259,9 +259,6 @@ static void sg_clean(struct usb_sg_reque + kfree(io->urbs); + io->urbs = NULL; + } +- if (io->dev->dev.dma_mask != NULL) +- usb_buffer_unmap_sg(io->dev, usb_pipein(io->pipe), +- io->sg, io->nents); + io->dev = NULL; + } + +@@ -364,7 +361,6 @@ int usb_sg_init(struct usb_sg_request *i + { + int i; + int urb_flags; +- int dma; + int use_sg; + + if (!io || !dev || !sg +@@ -378,21 +374,9 @@ int usb_sg_init(struct usb_sg_request *i + io->pipe = pipe; + io->sg = sg; + io->nents = nents; +- +- /* not all host controllers use DMA (like the mainstream pci ones); +- * they can use PIO (sl811) or be software over another transport. +- */ +- dma = (dev->dev.dma_mask != NULL); +- if (dma) +- io->entries = usb_buffer_map_sg(dev, usb_pipein(pipe), +- sg, nents); +- else +- io->entries = nents; ++ io->entries = nents; + + /* initialize all the urbs we'll use */ +- if (io->entries <= 0) +- return io->entries; +- + if (dev->bus->sg_tablesize > 0) { + io->urbs = kmalloc(sizeof *io->urbs, mem_flags); + use_sg = true; +@@ -404,8 +388,6 @@ int usb_sg_init(struct usb_sg_request *i + goto nomem; + + urb_flags = 0; +- if (dma) +- urb_flags |= URB_NO_TRANSFER_DMA_MAP; + if (usb_pipein(pipe)) + urb_flags |= URB_SHORT_NOT_OK; + +@@ -423,12 +405,13 @@ int usb_sg_init(struct usb_sg_request *i + + io->urbs[0]->complete = sg_complete; + io->urbs[0]->context = io; ++ + /* A length of zero means transfer the whole sg list */ + io->urbs[0]->transfer_buffer_length = length; + if (length == 0) { + for_each_sg(sg, sg, io->entries, i) { + io->urbs[0]->transfer_buffer_length += +- sg_dma_len(sg); ++ sg->length; + } + } + io->urbs[0]->sg = io; +@@ -454,26 +437,16 @@ int usb_sg_init(struct usb_sg_request *i + io->urbs[i]->context = io; + + /* +- * Some systems need to revert to PIO when DMA is temporarily +- * unavailable. For their sakes, both transfer_buffer and +- * transfer_dma are set when possible. +- * +- * Note that if IOMMU coalescing occurred, we cannot +- * trust sg_page anymore, so check if S/G list shrunk. ++ * Some systems can't use DMA; they use PIO instead. ++ * For their sakes, transfer_buffer is set whenever ++ * possible. + */ +- if (io->nents == io->entries && !PageHighMem(sg_page(sg))) ++ if (!PageHighMem(sg_page(sg))) + io->urbs[i]->transfer_buffer = sg_virt(sg); + else + io->urbs[i]->transfer_buffer = NULL; + +- if (dma) { +- io->urbs[i]->transfer_dma = sg_dma_address(sg); +- len = sg_dma_len(sg); +- } else { +- /* hc may use _only_ transfer_buffer */ +- len = sg->length; +- } +- ++ len = sg->length; + if (length) { + len = min_t(unsigned, len, length); + length -= len; +@@ -481,6 +454,8 @@ int usb_sg_init(struct usb_sg_request *i + io->entries = i + 1; + } + io->urbs[i]->transfer_buffer_length = len; ++ ++ io->urbs[i]->sg = (struct usb_sg_request *) sg; + } + io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT; + } +--- a/drivers/usb/core/urb.c ++++ b/drivers/usb/core/urb.c +@@ -333,9 +333,12 @@ int usb_submit_urb(struct urb *urb, gfp_ + is_out = usb_endpoint_dir_out(&ep->desc); + } + +- /* Cache the direction for later use */ +- urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) | +- (is_out ? URB_DIR_OUT : URB_DIR_IN); ++ /* Clear the internal flags and cache the direction for later use */ ++ urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE | ++ URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL | ++ URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL | ++ URB_DMA_SG_COMBINED); ++ urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN); + + if (xfertype != USB_ENDPOINT_XFER_CONTROL && + dev->state < USB_STATE_CONFIGURED) +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -881,6 +881,7 @@ void usb_buffer_unmap(struct urb *urb) + EXPORT_SYMBOL_GPL(usb_buffer_unmap); + #endif /* 0 */ + ++#if 0 + /** + * usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint + * @dev: device to which the scatterlist will be mapped +@@ -924,6 +925,7 @@ int usb_buffer_map_sg(const struct usb_d + is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE) ? : -ENOMEM; + } + EXPORT_SYMBOL_GPL(usb_buffer_map_sg); ++#endif + + /* XXX DISABLED, no users currently. If you wish to re-enable this + * XXX please determine whether the sync is to transfer ownership of +@@ -960,6 +962,7 @@ void usb_buffer_dmasync_sg(const struct + EXPORT_SYMBOL_GPL(usb_buffer_dmasync_sg); + #endif + ++#if 0 + /** + * usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist + * @dev: device to which the scatterlist will be mapped +@@ -985,6 +988,7 @@ void usb_buffer_unmap_sg(const struct us + is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + } + EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg); ++#endif + + /* To disable USB, kernel command line is 'nousb' not 'usbcore.nousb' */ + #ifdef MODULE +--- a/drivers/usb/host/whci/qset.c ++++ b/drivers/usb/host/whci/qset.c +@@ -646,7 +646,7 @@ int qset_add_urb(struct whc *whc, struct + wurb->urb = urb; + INIT_WORK(&wurb->dequeue_work, urb_dequeue_work); + +- if (urb->sg) { ++ if (urb->num_sgs) { + ret = qset_add_urb_sg(whc, qset, urb, mem_flags); + if (ret == -EINVAL) { + qset_free_stds(qset, urb); +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1938,7 +1938,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd * + int running_total, trb_buff_len, ret; + u64 addr; + +- if (urb->sg) ++ if (urb->num_sgs) + return queue_bulk_sg_tx(xhci, mem_flags, urb, slot_id, ep_index); + + ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; +--- a/drivers/usb/mon/mon_bin.c ++++ b/drivers/usb/mon/mon_bin.c +@@ -416,7 +416,7 @@ static unsigned int mon_bin_get_data(con + + } else { + /* If IOMMU coalescing occurred, we cannot trust sg_page */ +- if (urb->sg->nents != urb->num_sgs) { ++ if (urb->transfer_flags & URB_DMA_SG_COMBINED) { + *flag = 'D'; + return length; + } +--- a/drivers/usb/mon/mon_text.c ++++ b/drivers/usb/mon/mon_text.c +@@ -161,9 +161,7 @@ static inline char mon_text_get_data(str + } else { + struct scatterlist *sg = urb->sg->sg; + +- /* If IOMMU coalescing occurred, we cannot trust sg_page */ +- if (urb->sg->nents != urb->num_sgs || +- PageHighMem(sg_page(sg))) ++ if (PageHighMem(sg_page(sg))) + return 'D'; + + /* For the text interface we copy only the first sg buffer */ +--- a/include/linux/usb.h ++++ b/include/linux/usb.h +@@ -965,10 +965,19 @@ extern int usb_disabled(void); + * needed */ + #define URB_FREE_BUFFER 0x0100 /* Free transfer buffer with the URB */ + ++/* The following flags are used internally by usbcore and HCDs */ + #define URB_DIR_IN 0x0200 /* Transfer from device to host */ + #define URB_DIR_OUT 0 + #define URB_DIR_MASK URB_DIR_IN + ++#define URB_DMA_MAP_SINGLE 0x00010000 /* Non-scatter-gather mapping */ ++#define URB_DMA_MAP_PAGE 0x00020000 /* HCD-unsupported S-G */ ++#define URB_DMA_MAP_SG 0x00040000 /* HCD-supported S-G */ ++#define URB_MAP_LOCAL 0x00080000 /* HCD-local-memory mapping */ ++#define URB_SETUP_MAP_SINGLE 0x00100000 /* Setup packet DMA mapped */ ++#define URB_SETUP_MAP_LOCAL 0x00200000 /* HCD-local setup packet */ ++#define URB_DMA_SG_COMBINED 0x00400000 /* S-G entries were combined */ ++ + struct usb_iso_packet_descriptor { + unsigned int offset; + unsigned int length; /* expected length */ diff --git a/usb/usb-gadget-allow-larger-configuration-descriptors.patch b/usb/usb-gadget-allow-larger-configuration-descriptors.patch new file mode 100644 index 00000000000000..17b8cc46ee8131 --- /dev/null +++ b/usb/usb-gadget-allow-larger-configuration-descriptors.patch @@ -0,0 +1,30 @@ +From Robert.Lukassen@tomtom.com Thu Apr 29 12:38:47 2010 +From: "Robert Lukassen" <Robert.Lukassen@tomtom.com> +Date: Tue, 30 Mar 2010 14:14:01 +0200 +Subject: usb: gadget: Allow larger configuration descriptors +Message-ID: <D6DB9C7EDECDA944B870F62B5870086229B68C@NL-EXC-06.intra.local> + + +The composite framework allows gadgets with more than one function. This +can lead to situations where the configuration descriptor is larger than +the maximum of 512 bytes currently allowed by the composite framework. +This patch proposes to double that limit to 1024. + +Signed-off-by: Robert Lukassen <robert.lukassen@tomtom.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/composite.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/gadget/composite.c ++++ b/drivers/usb/gadget/composite.c +@@ -36,7 +36,7 @@ + */ + + /* big enough to hold our biggest descriptor */ +-#define USB_BUFSIZ 512 ++#define USB_BUFSIZ 1024 + + static struct usb_composite_driver *composite; + diff --git a/usb/usb-gadget-f_mass_storage-per-function.patch b/usb/usb-gadget-f_mass_storage-per-function.patch new file mode 100644 index 00000000000000..4db35b39d744df --- /dev/null +++ b/usb/usb-gadget-f_mass_storage-per-function.patch @@ -0,0 +1,88 @@ +From m.nazarewicz@samsung.com Thu Apr 29 12:40:28 2010 +From: Michal Nazarewicz <m.nazarewicz@samsung.com> +Date: Mon, 29 Mar 2010 14:01:32 +0200 +Subject: USB: gadget: f_mass_storage: per function +To: Johannes Weiner <hannes@cmpxchg.org>, David Brownell <dbrownell@users.sourceforge.net> +Cc: Peter Korsgaard <jacmet@sunsite.dk>, Marek Szyprowski <m.szyprowski@samsung.com>, Kyungmin Park <kyungmin.park@samsung.com>, Michal Nazarewicz <m.nazarewicz@samsung.com> +Message-ID: <7d23e1b58c7263c7b2479a409f9bf9d7922df3ab.1269863632.git.mina86@mina86.com> + + +Mass Storage Function (MSF) used the same descriptors for each +usb_function instance (meaning usb_function::descriptors of different +functions pointed to the same static area (the same was true for +usb_function::hs_descriptors)). + +This would leads to problems if MSF were used in several USB +configurations with different interface and/or endpoint numbers. +Descriptors for all configurations would have interface/endpoint +numbers overwritten by the values valid for the last configuration. + +This patch adds code that copies the descriptors each time MSF is +added to USB configuration (that is for each usb_function). + +Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> +Cc: Kyungmin Park <kyungmin.park@samsung.com> + +--- + drivers/usb/gadget/f_mass_storage.c | 28 ++++++++++++++++++++++------ + 1 file changed, 22 insertions(+), 6 deletions(-) + +--- a/drivers/usb/gadget/f_mass_storage.c ++++ b/drivers/usb/gadget/f_mass_storage.c +@@ -2919,6 +2919,8 @@ static void fsg_unbind(struct usb_config + + DBG(fsg, "unbind\n"); + fsg_common_put(fsg->common); ++ usb_free_descriptors(fsg->function.descriptors); ++ usb_free_descriptors(fsg->function.hs_descriptors); + kfree(fsg); + } + +@@ -2959,7 +2961,9 @@ static int __init fsg_bind(struct usb_co + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_hs_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; +- f->hs_descriptors = fsg_hs_function; ++ f->hs_descriptors = usb_copy_descriptors(fsg_hs_function); ++ if (unlikely(!f->hs_descriptors)) ++ return -ENOMEM; + } + + return 0; +@@ -2991,7 +2995,11 @@ static int fsg_add(struct usb_composite_ + + fsg->function.name = FSG_DRIVER_DESC; + fsg->function.strings = fsg_strings_array; +- fsg->function.descriptors = fsg_fs_function; ++ fsg->function.descriptors = usb_copy_descriptors(fsg_fs_function); ++ if (unlikely(!fsg->function.descriptors)) { ++ rc = -ENOMEM; ++ goto error_free_fsg; ++ } + fsg->function.bind = fsg_bind; + fsg->function.unbind = fsg_unbind; + fsg->function.setup = fsg_setup; +@@ -3006,11 +3014,19 @@ static int fsg_add(struct usb_composite_ + * call to usb_add_function() was successful. */ + + rc = usb_add_function(c, &fsg->function); ++ if (unlikely(rc)) ++ goto error_free_all; ++ ++ fsg_common_get(fsg->common); ++ return 0; + +- if (likely(rc == 0)) +- fsg_common_get(fsg->common); +- else +- kfree(fsg); ++error_free_all: ++ usb_free_descriptors(fsg->function.descriptors); ++ /* fsg_bind() might have copied those; or maybe not? who cares ++ * -- free it just in case. */ ++ usb_free_descriptors(fsg->function.hs_descriptors); ++error_free_fsg: ++ kfree(fsg); + + return rc; + } diff --git a/usb/usb-improve-runtime-remote-wakeup-settings.patch b/usb/usb-improve-runtime-remote-wakeup-settings.patch new file mode 100644 index 00000000000000..d716bd5357879c --- /dev/null +++ b/usb/usb-improve-runtime-remote-wakeup-settings.patch @@ -0,0 +1,77 @@ +From stern@rowland.harvard.edu Thu Apr 29 12:51:13 2010 +From: Alan Stern <stern@rowland.harvard.edu> +Date: Fri, 2 Apr 2010 13:18:50 -0400 (EDT) +Subject: USB: improve runtime remote wakeup settings +To: Greg KH <greg@kroah.com> +Message-ID: <Pine.LNX.4.44L0.1004021306440.1324-100000@iolanthe.rowland.org> + + +This patch (as1362) adjusts the way the USB autosuspend routines +handle remote-wakeup settings. They aren't supposed to use +device_may_wakeup(); that test is intended only for system sleep, not +runtime power management. Instead the code checks to see if any +interface drivers need remote wakeup; if they do then it is enabled, +provided the device is capable of it. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/core/driver.c | 19 ++++++++----------- + 1 file changed, 8 insertions(+), 11 deletions(-) + +--- a/drivers/usb/core/driver.c ++++ b/drivers/usb/core/driver.c +@@ -1485,9 +1485,6 @@ int usb_autoresume_device(struct usb_dev + * 0, a delayed autosuspend request for @intf's device is attempted. The + * attempt may fail (see autosuspend_check()). + * +- * If the driver has set @intf->needs_remote_wakeup then autosuspend will +- * take place only if the device's remote-wakeup facility is enabled. +- * + * This routine can run only in process context. + */ + void usb_autopm_put_interface(struct usb_interface *intf) +@@ -1672,14 +1669,14 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interfa + /* Internal routine to check whether we may autosuspend a device. */ + static int autosuspend_check(struct usb_device *udev) + { +- int i; ++ int w, i; + struct usb_interface *intf; + unsigned long suspend_time, j; + + /* Fail if autosuspend is disabled, or any interfaces are in use, or + * any interface drivers require remote wakeup but it isn't available. + */ +- udev->do_remote_wakeup = device_may_wakeup(&udev->dev); ++ w = 0; + if (udev->actconfig) { + for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { + intf = udev->actconfig->interface[i]; +@@ -1693,12 +1690,7 @@ static int autosuspend_check(struct usb_ + continue; + if (atomic_read(&intf->dev.power.usage_count) > 0) + return -EBUSY; +- if (intf->needs_remote_wakeup && +- !udev->do_remote_wakeup) { +- dev_dbg(&udev->dev, "remote wakeup needed " +- "for autosuspend\n"); +- return -EOPNOTSUPP; +- } ++ w |= intf->needs_remote_wakeup; + + /* Don't allow autosuspend if the device will need + * a reset-resume and any of its interface drivers +@@ -1714,6 +1706,11 @@ static int autosuspend_check(struct usb_ + } + } + } ++ if (w && !device_can_wakeup(&udev->dev)) { ++ dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n"); ++ return -EOPNOTSUPP; ++ } ++ udev->do_remote_wakeup = w; + + /* If everything is okay but the device hasn't been idle for long + * enough, queue a delayed autosuspend request. diff --git a/usb/usb-musb-add-extvbus-in-musb_board_data.patch b/usb/usb-musb-add-extvbus-in-musb_board_data.patch new file mode 100644 index 00000000000000..fb63d60b0b5b3f --- /dev/null +++ b/usb/usb-musb-add-extvbus-in-musb_board_data.patch @@ -0,0 +1,56 @@ +From felipe.balbi@nokia.com Thu Apr 29 12:37:54 2010 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 25 Mar 2010 13:25:27 +0200 +Subject: usb: musb: Add extvbus in musb_board_data +To: Greg KH <greg@kroah.com> +Message-ID: <1269516328-28267-11-git-send-email-felipe.balbi@nokia.com> + + +From: Ajay Kumar Gupta <ajay.gupta@ti.com> + +EXTVBUS programming is required by OMAP3EVM REV >=E to supply 500mA +power so adding a flag which can be used by musb driver to program +EXTVBUS. + +Signed-off-by: Ajay Kumar Gupta <ajay.gupta@ti.com> +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + arch/arm/mach-omap2/board-omap3evm.c | 3 +++ + arch/arm/mach-omap2/usb-musb.c | 1 + + arch/arm/plat-omap/include/plat/usb.h | 1 + + 3 files changed, 5 insertions(+) + +--- a/arch/arm/mach-omap2/board-omap3evm.c ++++ b/arch/arm/mach-omap2/board-omap3evm.c +@@ -702,6 +702,9 @@ static void __init omap3_evm_init(void) + omap_mux_init_gpio(21, OMAP_PIN_INPUT_PULLUP); + ehci_pdata.reset_gpio_port[1] = 21; + ++ /* EVM REV >= E can supply 500mA with EXTVBUS programming */ ++ musb_board_data.power = 500; ++ musb_board_data.extvbus = 1; + } else { + /* setup EHCI phy reset on MDC */ + omap_mux_init_gpio(135, OMAP_PIN_OUTPUT); +--- a/arch/arm/mach-omap2/usb-musb.c ++++ b/arch/arm/mach-omap2/usb-musb.c +@@ -107,6 +107,7 @@ void __init usb_musb_init(struct omap_mu + musb_plat.board_data = board_data; + musb_plat.power = board_data->power >> 1; + musb_plat.mode = board_data->mode; ++ musb_plat.extvbus = board_data->extvbus; + + if (platform_device_register(&musb_device) < 0) + printk(KERN_ERR "Unable to register HS-USB (MUSB) device\n"); +--- a/arch/arm/plat-omap/include/plat/usb.h ++++ b/arch/arm/plat-omap/include/plat/usb.h +@@ -47,6 +47,7 @@ struct omap_musb_board_data { + u8 interface_type; + u8 mode; + u16 power; ++ unsigned extvbus:1; + }; + + enum musb_interface {MUSB_INTERFACE_ULPI, MUSB_INTERFACE_UTMI}; diff --git a/usb/usb-musb-add-ulpi-access-operations.patch b/usb/usb-musb-add-ulpi-access-operations.patch new file mode 100644 index 00000000000000..953c2607b1bccb --- /dev/null +++ b/usb/usb-musb-add-ulpi-access-operations.patch @@ -0,0 +1,150 @@ +From felipe.balbi@nokia.com Thu Apr 29 12:38:12 2010 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 25 Mar 2010 13:25:28 +0200 +Subject: usb: musb: add ulpi access operations +To: Greg KH <greg@kroah.com> +Message-ID: <1269516328-28267-12-git-send-email-felipe.balbi@nokia.com> + + +From: Heikki Krogerus <ext-heikki.krogerus@nokia.com> + +This adds helper functions for ULPI access, and implements +otg_io_access_ops for musb. + +Signed-off-by: Heikki Krogerus <ext-heikki.krogerus@nokia.com> +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musb_core.c | 86 +++++++++++++++++++++++++++++++++++++++++++ + drivers/usb/musb/musb_regs.h | 10 +++++ + 2 files changed, 96 insertions(+) + +--- a/drivers/usb/musb/musb_core.c ++++ b/drivers/usb/musb/musb_core.c +@@ -149,6 +149,87 @@ static inline struct musb *dev_to_musb(s + + /*-------------------------------------------------------------------------*/ + ++#ifndef CONFIG_BLACKFIN ++static int musb_ulpi_read(struct otg_transceiver *otg, u32 offset) ++{ ++ void __iomem *addr = otg->io_priv; ++ int i = 0; ++ u8 r; ++ u8 power; ++ ++ /* Make sure the transceiver is not in low power mode */ ++ power = musb_readb(addr, MUSB_POWER); ++ power &= ~MUSB_POWER_SUSPENDM; ++ musb_writeb(addr, MUSB_POWER, power); ++ ++ /* REVISIT: musbhdrc_ulpi_an.pdf recommends setting the ++ * ULPICarKitControlDisableUTMI after clearing POWER_SUSPENDM. ++ */ ++ ++ musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset); ++ musb_writeb(addr, MUSB_ULPI_REG_CONTROL, ++ MUSB_ULPI_REG_REQ | MUSB_ULPI_RDN_WR); ++ ++ while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) ++ & MUSB_ULPI_REG_CMPLT)) { ++ i++; ++ if (i == 10000) { ++ DBG(3, "ULPI read timed out\n"); ++ return -ETIMEDOUT; ++ } ++ ++ } ++ r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); ++ r &= ~MUSB_ULPI_REG_CMPLT; ++ musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r); ++ ++ return musb_readb(addr, MUSB_ULPI_REG_DATA); ++} ++ ++static int musb_ulpi_write(struct otg_transceiver *otg, ++ u32 offset, u32 data) ++{ ++ void __iomem *addr = otg->io_priv; ++ int i = 0; ++ u8 r = 0; ++ u8 power; ++ ++ /* Make sure the transceiver is not in low power mode */ ++ power = musb_readb(addr, MUSB_POWER); ++ power &= ~MUSB_POWER_SUSPENDM; ++ musb_writeb(addr, MUSB_POWER, power); ++ ++ musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset); ++ musb_writeb(addr, MUSB_ULPI_REG_DATA, (u8)data); ++ musb_writeb(addr, MUSB_ULPI_REG_CONTROL, MUSB_ULPI_REG_REQ); ++ ++ while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) ++ & MUSB_ULPI_REG_CMPLT)) { ++ i++; ++ if (i == 10000) { ++ DBG(3, "ULPI write timed out\n"); ++ return -ETIMEDOUT; ++ } ++ } ++ ++ r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); ++ r &= ~MUSB_ULPI_REG_CMPLT; ++ musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r); ++ ++ return 0; ++} ++#else ++#define musb_ulpi_read(a, b) NULL ++#define musb_ulpi_write(a, b, c) NULL ++#endif ++ ++static struct otg_io_access_ops musb_ulpi_access = { ++ .read = musb_ulpi_read, ++ .write = musb_ulpi_write, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ + #if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN) + + /* +@@ -1954,6 +2035,11 @@ bad_config: + goto fail3; + } + ++ if (!musb->xceiv->io_ops) { ++ musb->xceiv->io_priv = musb->mregs; ++ musb->xceiv->io_ops = &musb_ulpi_access; ++ } ++ + #ifndef CONFIG_MUSB_PIO_ONLY + if (use_dma && dev->dma_mask) { + struct dma_controller *c; +--- a/drivers/usb/musb/musb_regs.h ++++ b/drivers/usb/musb/musb_regs.h +@@ -75,6 +75,10 @@ + /* MUSB ULPI VBUSCONTROL */ + #define MUSB_ULPI_USE_EXTVBUS 0x01 + #define MUSB_ULPI_USE_EXTVBUSIND 0x02 ++/* ULPI_REG_CONTROL */ ++#define MUSB_ULPI_REG_REQ (1 << 0) ++#define MUSB_ULPI_REG_CMPLT (1 << 1) ++#define MUSB_ULPI_RDN_WR (1 << 2) + + /* TESTMODE */ + #define MUSB_TEST_FORCE_HOST 0x80 +@@ -251,6 +255,12 @@ + /* REVISIT: vctrl/vstatus: optional vendor utmi+phy register at 0x68 */ + #define MUSB_HWVERS 0x6C /* 8 bit */ + #define MUSB_ULPI_BUSCONTROL 0x70 /* 8 bit */ ++#define MUSB_ULPI_INT_MASK 0x72 /* 8 bit */ ++#define MUSB_ULPI_INT_SRC 0x73 /* 8 bit */ ++#define MUSB_ULPI_REG_DATA 0x74 /* 8 bit */ ++#define MUSB_ULPI_REG_ADDR 0x75 /* 8 bit */ ++#define MUSB_ULPI_REG_CONTROL 0x76 /* 8 bit */ ++#define MUSB_ULPI_RAW_DATA 0x77 /* 8 bit */ + + #define MUSB_EPINFO 0x78 /* 8 bit */ + #define MUSB_RAMINFO 0x79 /* 8 bit */ diff --git a/usb/usb-musb-debugfs-musb_exit_debugfs-can-be-called-on-probe.patch b/usb/usb-musb-debugfs-musb_exit_debugfs-can-be-called-on-probe.patch new file mode 100644 index 00000000000000..d8d98e5f69d477 --- /dev/null +++ b/usb/usb-musb-debugfs-musb_exit_debugfs-can-be-called-on-probe.patch @@ -0,0 +1,57 @@ +From felipe.balbi@nokia.com Thu Apr 29 12:35:06 2010 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 25 Mar 2010 13:25:18 +0200 +Subject: usb: musb: debugfs: musb_exit_debugfs() can be called on probe +To: Greg KH <greg@kroah.com> +Message-ID: <1269516328-28267-2-git-send-email-felipe.balbi@nokia.com> + + +when we fail to probe(), we can call musb_exit_debugfs(). +Allow that by removing section annotations. + +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musb_core.c | 7 +++++-- + drivers/usb/musb/musb_debugfs.c | 2 +- + 2 files changed, 6 insertions(+), 3 deletions(-) + +--- a/drivers/usb/musb/musb_core.c ++++ b/drivers/usb/musb/musb_core.c +@@ -2056,12 +2056,12 @@ bad_config: + + status = musb_init_debugfs(musb); + if (status < 0) +- goto fail2; ++ goto fail4; + + #ifdef CONFIG_SYSFS + status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group); + if (status) +- goto fail4; ++ goto fail5; + #endif + + dev_info(dev, "USB %s mode controller at %p using %s, IRQ %d\n", +@@ -2078,6 +2078,9 @@ bad_config: + + return 0; + ++fail5: ++ musb_exit_debugfs(musb); ++ + fail4: + if (!is_otg_enabled(musb) && is_host_enabled(musb)) + usb_remove_hcd(musb_to_hcd(musb)); +--- a/drivers/usb/musb/musb_debugfs.c ++++ b/drivers/usb/musb/musb_debugfs.c +@@ -289,7 +289,7 @@ err0: + return ret; + } + +-void __exit musb_exit_debugfs(struct musb *musb) ++void /* __init_or_exit */ musb_exit_debugfs(struct musb *musb) + { + debugfs_remove_recursive(musb_debugfs_root); + } diff --git a/usb/usb-musb-makefile-remove-unexistent-config-option.patch b/usb/usb-musb-makefile-remove-unexistent-config-option.patch new file mode 100644 index 00000000000000..a0a0229ff51d66 --- /dev/null +++ b/usb/usb-musb-makefile-remove-unexistent-config-option.patch @@ -0,0 +1,35 @@ +From felipe.balbi@nokia.com Thu Apr 29 12:37:42 2010 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 25 Mar 2010 13:25:26 +0200 +Subject: usb: musb: Makefile: remove unexistent config option +To: Greg KH <greg@kroah.com> +Message-ID: <1269516328-28267-10-git-send-email-felipe.balbi@nokia.com> + + +Remove the unexistent CONFIG_USB_INVENTRA_MUSB_HAS_AHB_ID +option from our Makefile. + +Problem reported by Sergei Shtylyov <sshtylyov@mvista.com> + +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/Makefile | 6 ------ + 1 file changed, 6 deletions(-) + +--- a/drivers/usb/musb/Makefile ++++ b/drivers/usb/musb/Makefile +@@ -72,12 +72,6 @@ endif + + ################################################################################ + +-# FIXME remove all these extra "-DMUSB_* things, stick to CONFIG_* +- +-ifeq ($(CONFIG_USB_INVENTRA_MUSB_HAS_AHB_ID),y) +- EXTRA_CFLAGS += -DMUSB_AHB_ID +-endif +- + # Debugging + + ifeq ($(CONFIG_USB_MUSB_DEBUG),y) diff --git a/usb/usb-musb-omap2430-remove-unused-define.patch b/usb/usb-musb-omap2430-remove-unused-define.patch new file mode 100644 index 00000000000000..b4aa2458f7a36a --- /dev/null +++ b/usb/usb-musb-omap2430-remove-unused-define.patch @@ -0,0 +1,34 @@ +From felipe.balbi@nokia.com Thu Apr 29 12:37:19 2010 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 25 Mar 2010 13:25:24 +0200 +Subject: usb: musb: omap2430: remove unused define +To: Greg KH <greg@kroah.com> +Message-ID: <1269516328-28267-8-git-send-email-felipe.balbi@nokia.com> + + +From: Anand Gadiyar <gadiyar@ti.com> + +get_cpu_rev() is unused in this driver. It is probably legacy +code. So remove it. + +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/omap2430.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/drivers/usb/musb/omap2430.c ++++ b/drivers/usb/musb/omap2430.c +@@ -39,10 +39,6 @@ + #include "musb_core.h" + #include "omap2430.h" + +-#ifdef CONFIG_ARCH_OMAP3430 +-#define get_cpu_rev() 2 +-#endif +- + + static struct timer_list musb_idle_timer; + diff --git a/usb/usb-musb-omap2430.c-remove-unnecessary-includes.patch b/usb/usb-musb-omap2430.c-remove-unnecessary-includes.patch new file mode 100644 index 00000000000000..c685df0750c210 --- /dev/null +++ b/usb/usb-musb-omap2430.c-remove-unnecessary-includes.patch @@ -0,0 +1,31 @@ +From felipe.balbi@nokia.com Thu Apr 29 12:37:30 2010 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 25 Mar 2010 13:25:25 +0200 +Subject: usb: musb: omap2430.c: remove unnecessary includes +To: Greg KH <greg@kroah.com> +Message-ID: <1269516328-28267-9-git-send-email-felipe.balbi@nokia.com> + + +From: Anand Gadiyar <gadiyar@ti.com> + +We don't need mach-types and hardware.h + +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/omap2430.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/usb/musb/omap2430.c ++++ b/drivers/usb/musb/omap2430.c +@@ -32,8 +32,6 @@ + #include <linux/clk.h> + #include <linux/io.h> + +-#include <asm/mach-types.h> +-#include <mach/hardware.h> + #include <plat/mux.h> + + #include "musb_core.h" diff --git a/usb/usb-musb-support-host-gadget-role-switching-on-blackfin-parts.patch b/usb/usb-musb-support-host-gadget-role-switching-on-blackfin-parts.patch new file mode 100644 index 00000000000000..a7fd811c4d414f --- /dev/null +++ b/usb/usb-musb-support-host-gadget-role-switching-on-blackfin-parts.patch @@ -0,0 +1,138 @@ +From felipe.balbi@nokia.com Thu Apr 29 12:35:57 2010 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 25 Mar 2010 13:25:19 +0200 +Subject: USB: musb: support host/gadget role switching on Blackfin parts +To: Greg KH <greg@kroah.com> +Message-ID: <1269516328-28267-3-git-send-email-felipe.balbi@nokia.com> + + +From: Cliff Cai <cliff.cai@analog.com> + +Signed-off-by: Cliff Cai <cliff.cai@analog.com> +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/blackfin.c | 69 ++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 63 insertions(+), 6 deletions(-) + +--- a/drivers/usb/musb/blackfin.c ++++ b/drivers/usb/musb/blackfin.c +@@ -170,6 +170,13 @@ static irqreturn_t blackfin_interrupt(in + retval = musb_interrupt(musb); + } + ++ /* Start sampling ID pin, when plug is removed from MUSB */ ++ if (is_otg_enabled(musb) && (musb->xceiv->state == OTG_STATE_B_IDLE ++ || musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { ++ mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); ++ musb->a_wait_bcon = TIMER_DELAY; ++ } ++ + spin_unlock_irqrestore(&musb->lock, flags); + + return retval; +@@ -180,6 +187,7 @@ static void musb_conn_timer_handler(unsi + struct musb *musb = (void *)_musb; + unsigned long flags; + u16 val; ++ static u8 toggle; + + spin_lock_irqsave(&musb->lock, flags); + switch (musb->xceiv->state) { +@@ -187,10 +195,44 @@ static void musb_conn_timer_handler(unsi + case OTG_STATE_A_WAIT_BCON: + /* Start a new session */ + val = musb_readw(musb->mregs, MUSB_DEVCTL); ++ val &= ~MUSB_DEVCTL_SESSION; ++ musb_writew(musb->mregs, MUSB_DEVCTL, val); + val |= MUSB_DEVCTL_SESSION; + musb_writew(musb->mregs, MUSB_DEVCTL, val); ++ /* Check if musb is host or peripheral. */ ++ val = musb_readw(musb->mregs, MUSB_DEVCTL); ++ ++ if (!(val & MUSB_DEVCTL_BDEVICE)) { ++ gpio_set_value(musb->config->gpio_vrsel, 1); ++ musb->xceiv->state = OTG_STATE_A_WAIT_BCON; ++ } else { ++ gpio_set_value(musb->config->gpio_vrsel, 0); ++ /* Ignore VBUSERROR and SUSPEND IRQ */ ++ val = musb_readb(musb->mregs, MUSB_INTRUSBE); ++ val &= ~MUSB_INTR_VBUSERROR; ++ musb_writeb(musb->mregs, MUSB_INTRUSBE, val); ++ ++ val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR; ++ musb_writeb(musb->mregs, MUSB_INTRUSB, val); ++ if (is_otg_enabled(musb)) ++ musb->xceiv->state = OTG_STATE_B_IDLE; ++ else ++ musb_writeb(musb->mregs, MUSB_POWER, MUSB_POWER_HSENAB); ++ } ++ mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); ++ break; ++ case OTG_STATE_B_IDLE: + ++ if (!is_peripheral_enabled(musb)) ++ break; ++ /* Start a new session. It seems that MUSB needs taking ++ * some time to recognize the type of the plug inserted? ++ */ ++ val = musb_readw(musb->mregs, MUSB_DEVCTL); ++ val |= MUSB_DEVCTL_SESSION; ++ musb_writew(musb->mregs, MUSB_DEVCTL, val); + val = musb_readw(musb->mregs, MUSB_DEVCTL); ++ + if (!(val & MUSB_DEVCTL_BDEVICE)) { + gpio_set_value(musb->config->gpio_vrsel, 1); + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; +@@ -205,12 +247,27 @@ static void musb_conn_timer_handler(unsi + val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR; + musb_writeb(musb->mregs, MUSB_INTRUSB, val); + +- val = MUSB_POWER_HSENAB; +- musb_writeb(musb->mregs, MUSB_POWER, val); ++ /* Toggle the Soft Conn bit, so that we can response to ++ * the inserting of either A-plug or B-plug. ++ */ ++ if (toggle) { ++ val = musb_readb(musb->mregs, MUSB_POWER); ++ val &= ~MUSB_POWER_SOFTCONN; ++ musb_writeb(musb->mregs, MUSB_POWER, val); ++ toggle = 0; ++ } else { ++ val = musb_readb(musb->mregs, MUSB_POWER); ++ val |= MUSB_POWER_SOFTCONN; ++ musb_writeb(musb->mregs, MUSB_POWER, val); ++ toggle = 1; ++ } ++ /* The delay time is set to 1/4 second by default, ++ * shortening it, if accelerating A-plug detection ++ * is needed in OTG mode. ++ */ ++ mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY / 4); + } +- mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); + break; +- + default: + DBG(1, "%s state not handled\n", otg_state_string(musb)); + break; +@@ -222,7 +279,7 @@ static void musb_conn_timer_handler(unsi + + void musb_platform_enable(struct musb *musb) + { +- if (is_host_enabled(musb)) { ++ if (!is_otg_enabled(musb) && is_host_enabled(musb)) { + mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); + musb->a_wait_bcon = TIMER_DELAY; + } +@@ -256,7 +313,7 @@ static int bfin_set_power(struct otg_tra + + void musb_platform_try_idle(struct musb *musb, unsigned long timeout) + { +- if (is_host_enabled(musb)) ++ if (!is_otg_enabled(musb) && is_host_enabled(musb)) + mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); + } + diff --git a/usb/usb-omap-switch-to-subsys_initcall-for-isp1301-transceiver.patch b/usb/usb-omap-switch-to-subsys_initcall-for-isp1301-transceiver.patch new file mode 100644 index 00000000000000..99c0a57b5f3974 --- /dev/null +++ b/usb/usb-omap-switch-to-subsys_initcall-for-isp1301-transceiver.patch @@ -0,0 +1,27 @@ +From Viral.Mehta@lntinfotech.com Thu Apr 29 13:05:59 2010 +From: Viral Mehta <Viral.Mehta@lntinfotech.com> +Date: Tue, 6 Apr 2010 11:51:00 +0530 +Subject: USB: omap: switch to subsys_initcall for isp1301 transceiver +Message-ID: <70376CA23424B34D86F1C7DE6B9973430254343A39@VSHINMSMBX01.vshodc.lntinfotech.com> + + +isp1301 transceiver driver init should be done before we do ohci omap init + +Signed-off-by: Viral Mehta <viral.mehta@lntinfotech.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/otg/isp1301_omap.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/otg/isp1301_omap.c ++++ b/drivers/usb/otg/isp1301_omap.c +@@ -1654,7 +1654,7 @@ static int __init isp_init(void) + { + return i2c_add_driver(&isp1301_driver); + } +-module_init(isp_init); ++subsys_initcall(isp_init); + + static void __exit isp_exit(void) + { diff --git a/usb/usb-option-use-generic-usb-wwan-code.patch b/usb/usb-option-use-generic-usb-wwan-code.patch new file mode 100644 index 00000000000000..ca57a8e1d08786 --- /dev/null +++ b/usb/usb-option-use-generic-usb-wwan-code.patch @@ -0,0 +1,783 @@ +From mjg@redhat.com Thu Apr 29 12:53:45 2010 +From: Matthew Garrett <mjg@redhat.com> +Date: Thu, 1 Apr 2010 12:31:08 -0400 +Subject: USB: option: Use generic USB wwan code +To: linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Matthew Garrett <mjg@redhat.com> +Message-ID: <1270139470-24360-2-git-send-email-mjg@redhat.com> + + +As this code was simply factored out of option, this is a simple +conversion. + +Signed-off-by: Matthew Garrett <mjg@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/Kconfig | 1 + drivers/usb/serial/option.c | 672 +------------------------------------------- + 2 files changed, 27 insertions(+), 646 deletions(-) + +--- a/drivers/usb/serial/Kconfig ++++ b/drivers/usb/serial/Kconfig +@@ -581,6 +581,7 @@ config USB_SERIAL_WWAN + + config USB_SERIAL_OPTION + tristate "USB driver for GSM and CDMA modems" ++ select USB_SERIAL_WWAN + help + Say Y here if you have a GSM or CDMA modem that's connected to USB. + +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -42,35 +42,14 @@ + #include <linux/bitops.h> + #include <linux/usb.h> + #include <linux/usb/serial.h> ++#include "usb-wwan.h" + + /* Function prototypes */ + static int option_probe(struct usb_serial *serial, + const struct usb_device_id *id); +-static int option_open(struct tty_struct *tty, struct usb_serial_port *port); +-static void option_close(struct usb_serial_port *port); +-static void option_dtr_rts(struct usb_serial_port *port, int on); +- +-static int option_startup(struct usb_serial *serial); +-static void option_disconnect(struct usb_serial *serial); +-static void option_release(struct usb_serial *serial); +-static int option_write_room(struct tty_struct *tty); +- ++static int option_send_setup(struct usb_serial_port *port); + static void option_instat_callback(struct urb *urb); + +-static int option_write(struct tty_struct *tty, struct usb_serial_port *port, +- const unsigned char *buf, int count); +-static int option_chars_in_buffer(struct tty_struct *tty); +-static void option_set_termios(struct tty_struct *tty, +- struct usb_serial_port *port, struct ktermios *old); +-static int option_tiocmget(struct tty_struct *tty, struct file *file); +-static int option_tiocmset(struct tty_struct *tty, struct file *file, +- unsigned int set, unsigned int clear); +-static int option_send_setup(struct usb_serial_port *port); +-#ifdef CONFIG_PM +-static int option_suspend(struct usb_serial *serial, pm_message_t message); +-static int option_resume(struct usb_serial *serial); +-#endif +- + /* Vendor and product IDs */ + #define OPTION_VENDOR_ID 0x0AF0 + #define OPTION_PRODUCT_COLT 0x5000 +@@ -757,22 +736,22 @@ static struct usb_serial_driver option_1 + .id_table = option_ids, + .num_ports = 1, + .probe = option_probe, +- .open = option_open, +- .close = option_close, +- .dtr_rts = option_dtr_rts, +- .write = option_write, +- .write_room = option_write_room, +- .chars_in_buffer = option_chars_in_buffer, +- .set_termios = option_set_termios, +- .tiocmget = option_tiocmget, +- .tiocmset = option_tiocmset, +- .attach = option_startup, +- .disconnect = option_disconnect, +- .release = option_release, ++ .open = usb_wwan_open, ++ .close = usb_wwan_close, ++ .dtr_rts = usb_wwan_dtr_rts, ++ .write = usb_wwan_write, ++ .write_room = usb_wwan_write_room, ++ .chars_in_buffer = usb_wwan_chars_in_buffer, ++ .set_termios = usb_wwan_set_termios, ++ .tiocmget = usb_wwan_tiocmget, ++ .tiocmset = usb_wwan_tiocmset, ++ .attach = usb_wwan_startup, ++ .disconnect = usb_wwan_disconnect, ++ .release = usb_wwan_release, + .read_int_callback = option_instat_callback, + #ifdef CONFIG_PM +- .suspend = option_suspend, +- .resume = option_resume, ++ .suspend = usb_wwan_suspend, ++ .resume = usb_wwan_resume, + #endif + }; + +@@ -785,13 +764,6 @@ static int debug; + #define IN_BUFLEN 4096 + #define OUT_BUFLEN 4096 + +-struct option_intf_private { +- spinlock_t susp_lock; +- unsigned int suspended:1; +- int in_flight; +- struct option_blacklist_info *blacklist_info; +-}; +- + struct option_port_private { + /* Input endpoints and buffer for this port */ + struct urb *in_urbs[N_IN_URB]; +@@ -848,8 +820,7 @@ module_exit(option_exit); + static int option_probe(struct usb_serial *serial, + const struct usb_device_id *id) + { +- struct option_intf_private *data; +- ++ struct usb_wwan_intf_private *data; + /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */ + if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID && + serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 && +@@ -862,11 +833,13 @@ static int option_probe(struct usb_seria + serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff) + return -ENODEV; + +- data = serial->private = kzalloc(sizeof(struct option_intf_private), GFP_KERNEL); ++ data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); ++ + if (!data) + return -ENOMEM; ++ data->send_setup = option_send_setup; + spin_lock_init(&data->susp_lock); +- data->blacklist_info = (struct option_blacklist_info*) id->driver_info; ++ data->private = (void *)id->driver_info; + return 0; + } + +@@ -887,205 +860,6 @@ static enum option_blacklist_reason is_b + return OPTION_BLACKLIST_NONE; + } + +-static void option_set_termios(struct tty_struct *tty, +- struct usb_serial_port *port, struct ktermios *old_termios) +-{ +- dbg("%s", __func__); +- /* Doesn't support option setting */ +- tty_termios_copy_hw(tty->termios, old_termios); +- option_send_setup(port); +-} +- +-static int option_tiocmget(struct tty_struct *tty, struct file *file) +-{ +- struct usb_serial_port *port = tty->driver_data; +- unsigned int value; +- struct option_port_private *portdata; +- +- portdata = usb_get_serial_port_data(port); +- +- value = ((portdata->rts_state) ? TIOCM_RTS : 0) | +- ((portdata->dtr_state) ? TIOCM_DTR : 0) | +- ((portdata->cts_state) ? TIOCM_CTS : 0) | +- ((portdata->dsr_state) ? TIOCM_DSR : 0) | +- ((portdata->dcd_state) ? TIOCM_CAR : 0) | +- ((portdata->ri_state) ? TIOCM_RNG : 0); +- +- return value; +-} +- +-static int option_tiocmset(struct tty_struct *tty, struct file *file, +- unsigned int set, unsigned int clear) +-{ +- struct usb_serial_port *port = tty->driver_data; +- struct option_port_private *portdata; +- +- portdata = usb_get_serial_port_data(port); +- +- /* FIXME: what locks portdata fields ? */ +- if (set & TIOCM_RTS) +- portdata->rts_state = 1; +- if (set & TIOCM_DTR) +- portdata->dtr_state = 1; +- +- if (clear & TIOCM_RTS) +- portdata->rts_state = 0; +- if (clear & TIOCM_DTR) +- portdata->dtr_state = 0; +- return option_send_setup(port); +-} +- +-/* Write */ +-static int option_write(struct tty_struct *tty, struct usb_serial_port *port, +- const unsigned char *buf, int count) +-{ +- struct option_port_private *portdata; +- struct option_intf_private *intfdata; +- int i; +- int left, todo; +- struct urb *this_urb = NULL; /* spurious */ +- int err; +- unsigned long flags; +- +- portdata = usb_get_serial_port_data(port); +- intfdata = port->serial->private; +- +- dbg("%s: write (%d chars)", __func__, count); +- +- i = 0; +- left = count; +- for (i = 0; left > 0 && i < N_OUT_URB; i++) { +- todo = left; +- if (todo > OUT_BUFLEN) +- todo = OUT_BUFLEN; +- +- this_urb = portdata->out_urbs[i]; +- if (test_and_set_bit(i, &portdata->out_busy)) { +- if (time_before(jiffies, +- portdata->tx_start_time[i] + 10 * HZ)) +- continue; +- usb_unlink_urb(this_urb); +- continue; +- } +- dbg("%s: endpoint %d buf %d", __func__, +- usb_pipeendpoint(this_urb->pipe), i); +- +- err = usb_autopm_get_interface_async(port->serial->interface); +- if (err < 0) +- break; +- +- /* send the data */ +- memcpy(this_urb->transfer_buffer, buf, todo); +- this_urb->transfer_buffer_length = todo; +- +- spin_lock_irqsave(&intfdata->susp_lock, flags); +- if (intfdata->suspended) { +- usb_anchor_urb(this_urb, &portdata->delayed); +- spin_unlock_irqrestore(&intfdata->susp_lock, flags); +- } else { +- intfdata->in_flight++; +- spin_unlock_irqrestore(&intfdata->susp_lock, flags); +- err = usb_submit_urb(this_urb, GFP_ATOMIC); +- if (err) { +- dbg("usb_submit_urb %p (write bulk) failed " +- "(%d)", this_urb, err); +- clear_bit(i, &portdata->out_busy); +- spin_lock_irqsave(&intfdata->susp_lock, flags); +- intfdata->in_flight--; +- spin_unlock_irqrestore(&intfdata->susp_lock, flags); +- continue; +- } +- } +- +- portdata->tx_start_time[i] = jiffies; +- buf += todo; +- left -= todo; +- } +- +- count -= left; +- dbg("%s: wrote (did %d)", __func__, count); +- return count; +-} +- +-static void option_indat_callback(struct urb *urb) +-{ +- int err; +- int endpoint; +- struct usb_serial_port *port; +- struct tty_struct *tty; +- unsigned char *data = urb->transfer_buffer; +- int status = urb->status; +- +- dbg("%s: %p", __func__, urb); +- +- endpoint = usb_pipeendpoint(urb->pipe); +- port = urb->context; +- +- switch (status) { +- case 0: +- /* success */ +- break; +- case -ECONNRESET: +- case -ENOENT: +- case -ESHUTDOWN: +- /* this urb is terminated, clean up */ +- dbg("%s: urb shutting down with status: %d on endpoint %02x.", +- __func__, status, endpoint); +- return; +- default: +- dbg("%s: nonzero status: %d on endpoint %02x.", +- __func__, status, endpoint); +- goto exit; +- } +- +- if (urb->actual_length) { +- tty = tty_port_tty_get(&port->port); +- tty_insert_flip_string(tty, data, urb->actual_length); +- tty_flip_buffer_push(tty); +- tty_kref_put(tty); +- } else +- dbg("%s: empty read urb received", __func__); +- +-exit: +- /* Resubmit urb so we continue receiving */ +- err = usb_submit_urb(urb, GFP_ATOMIC); +- if (err && err != -EPERM) +- printk(KERN_ERR "%s: resubmit read urb failed. " +- "(%d)", __func__, err); +- else +- usb_mark_last_busy(port->serial->dev); +- +- return; +-} +- +-static void option_outdat_callback(struct urb *urb) +-{ +- struct usb_serial_port *port; +- struct option_port_private *portdata; +- struct option_intf_private *intfdata; +- int i; +- +- dbg("%s", __func__); +- +- port = urb->context; +- intfdata = port->serial->private; +- +- usb_serial_port_softint(port); +- usb_autopm_put_interface_async(port->serial->interface); +- portdata = usb_get_serial_port_data(port); +- spin_lock(&intfdata->susp_lock); +- intfdata->in_flight--; +- spin_unlock(&intfdata->susp_lock); +- +- for (i = 0; i < N_OUT_URB; ++i) { +- if (portdata->out_urbs[i] == urb) { +- smp_mb__before_clear_bit(); +- clear_bit(i, &portdata->out_busy); +- break; +- } +- } +-} +- + static void option_instat_callback(struct urb *urb) + { + int err; +@@ -1142,183 +916,6 @@ static void option_instat_callback(struc + } + } + +-static int option_write_room(struct tty_struct *tty) +-{ +- struct usb_serial_port *port = tty->driver_data; +- struct option_port_private *portdata; +- int i; +- int data_len = 0; +- struct urb *this_urb; +- +- portdata = usb_get_serial_port_data(port); +- +- for (i = 0; i < N_OUT_URB; i++) { +- this_urb = portdata->out_urbs[i]; +- if (this_urb && !test_bit(i, &portdata->out_busy)) +- data_len += OUT_BUFLEN; +- } +- +- dbg("%s: %d", __func__, data_len); +- return data_len; +-} +- +-static int option_chars_in_buffer(struct tty_struct *tty) +-{ +- struct usb_serial_port *port = tty->driver_data; +- struct option_port_private *portdata; +- int i; +- int data_len = 0; +- struct urb *this_urb; +- +- portdata = usb_get_serial_port_data(port); +- +- for (i = 0; i < N_OUT_URB; i++) { +- this_urb = portdata->out_urbs[i]; +- /* FIXME: This locking is insufficient as this_urb may +- go unused during the test */ +- if (this_urb && test_bit(i, &portdata->out_busy)) +- data_len += this_urb->transfer_buffer_length; +- } +- dbg("%s: %d", __func__, data_len); +- return data_len; +-} +- +-static int option_open(struct tty_struct *tty, struct usb_serial_port *port) +-{ +- struct option_port_private *portdata; +- struct option_intf_private *intfdata; +- struct usb_serial *serial = port->serial; +- int i, err; +- struct urb *urb; +- +- portdata = usb_get_serial_port_data(port); +- intfdata = serial->private; +- +- dbg("%s", __func__); +- +- /* Start reading from the IN endpoint */ +- for (i = 0; i < N_IN_URB; i++) { +- urb = portdata->in_urbs[i]; +- if (!urb) +- continue; +- err = usb_submit_urb(urb, GFP_KERNEL); +- if (err) { +- dbg("%s: submit urb %d failed (%d) %d", +- __func__, i, err, +- urb->transfer_buffer_length); +- } +- } +- +- option_send_setup(port); +- +- serial->interface->needs_remote_wakeup = 1; +- spin_lock_irq(&intfdata->susp_lock); +- portdata->opened = 1; +- spin_unlock_irq(&intfdata->susp_lock); +- usb_autopm_put_interface(serial->interface); +- +- return 0; +-} +- +-static void option_dtr_rts(struct usb_serial_port *port, int on) +-{ +- struct usb_serial *serial = port->serial; +- struct option_port_private *portdata; +- +- dbg("%s", __func__); +- portdata = usb_get_serial_port_data(port); +- mutex_lock(&serial->disc_mutex); +- portdata->rts_state = on; +- portdata->dtr_state = on; +- if (serial->dev) +- option_send_setup(port); +- mutex_unlock(&serial->disc_mutex); +-} +- +- +-static void option_close(struct usb_serial_port *port) +-{ +- int i; +- struct usb_serial *serial = port->serial; +- struct option_port_private *portdata; +- struct option_intf_private *intfdata = port->serial->private; +- +- dbg("%s", __func__); +- portdata = usb_get_serial_port_data(port); +- +- if (serial->dev) { +- /* Stop reading/writing urbs */ +- spin_lock_irq(&intfdata->susp_lock); +- portdata->opened = 0; +- spin_unlock_irq(&intfdata->susp_lock); +- +- for (i = 0; i < N_IN_URB; i++) +- usb_kill_urb(portdata->in_urbs[i]); +- for (i = 0; i < N_OUT_URB; i++) +- usb_kill_urb(portdata->out_urbs[i]); +- usb_autopm_get_interface(serial->interface); +- serial->interface->needs_remote_wakeup = 0; +- } +-} +- +-/* Helper functions used by option_setup_urbs */ +-static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint, +- int dir, void *ctx, char *buf, int len, +- void (*callback)(struct urb *)) +-{ +- struct urb *urb; +- +- if (endpoint == -1) +- return NULL; /* endpoint not needed */ +- +- urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ +- if (urb == NULL) { +- dbg("%s: alloc for endpoint %d failed.", __func__, endpoint); +- return NULL; +- } +- +- /* Fill URB using supplied data. */ +- usb_fill_bulk_urb(urb, serial->dev, +- usb_sndbulkpipe(serial->dev, endpoint) | dir, +- buf, len, callback, ctx); +- +- return urb; +-} +- +-/* Setup urbs */ +-static void option_setup_urbs(struct usb_serial *serial) +-{ +- int i, j; +- struct usb_serial_port *port; +- struct option_port_private *portdata; +- +- dbg("%s", __func__); +- +- for (i = 0; i < serial->num_ports; i++) { +- port = serial->port[i]; +- portdata = usb_get_serial_port_data(port); +- +- /* Do indat endpoints first */ +- for (j = 0; j < N_IN_URB; ++j) { +- portdata->in_urbs[j] = option_setup_urb(serial, +- port->bulk_in_endpointAddress, +- USB_DIR_IN, port, +- portdata->in_buffer[j], +- IN_BUFLEN, option_indat_callback); +- } +- +- /* outdat endpoints */ +- for (j = 0; j < N_OUT_URB; ++j) { +- portdata->out_urbs[j] = option_setup_urb(serial, +- port->bulk_out_endpointAddress, +- USB_DIR_OUT, port, +- portdata->out_buffer[j], +- OUT_BUFLEN, option_outdat_callback); +- } +- } +-} +- +- + /** send RTS/DTR state to the port. + * + * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN +@@ -1327,15 +924,16 @@ static void option_setup_urbs(struct usb + static int option_send_setup(struct usb_serial_port *port) + { + struct usb_serial *serial = port->serial; +- struct option_intf_private *intfdata = +- (struct option_intf_private *) serial->private; ++ struct usb_wwan_intf_private *intfdata = ++ (struct usb_wwan_intf_private *) serial->private; + struct option_port_private *portdata; + int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber; + int val = 0; + dbg("%s", __func__); + +- if (is_blacklisted(ifNum, intfdata->blacklist_info) == +- OPTION_BLACKLIST_SENDSETUP) { ++ if (is_blacklisted(ifNum, ++ (struct option_blacklist_info *) intfdata->private) ++ == OPTION_BLACKLIST_SENDSETUP) { + dbg("No send_setup on blacklisted interface #%d\n", ifNum); + return -EIO; + } +@@ -1352,224 +950,6 @@ static int option_send_setup(struct usb_ + 0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT); + } + +-static int option_startup(struct usb_serial *serial) +-{ +- int i, j, err; +- struct usb_serial_port *port; +- struct option_port_private *portdata; +- u8 *buffer; +- +- dbg("%s", __func__); +- +- /* Now setup per port private data */ +- for (i = 0; i < serial->num_ports; i++) { +- port = serial->port[i]; +- portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); +- if (!portdata) { +- dbg("%s: kmalloc for option_port_private (%d) failed!.", +- __func__, i); +- return 1; +- } +- init_usb_anchor(&portdata->delayed); +- +- for (j = 0; j < N_IN_URB; j++) { +- buffer = (u8 *)__get_free_page(GFP_KERNEL); +- if (!buffer) +- goto bail_out_error; +- portdata->in_buffer[j] = buffer; +- } +- +- for (j = 0; j < N_OUT_URB; j++) { +- buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); +- if (!buffer) +- goto bail_out_error2; +- portdata->out_buffer[j] = buffer; +- } +- +- usb_set_serial_port_data(port, portdata); +- +- if (!port->interrupt_in_urb) +- continue; +- err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); +- if (err) +- dbg("%s: submit irq_in urb failed %d", +- __func__, err); +- } +- option_setup_urbs(serial); +- return 0; +- +-bail_out_error2: +- for (j = 0; j < N_OUT_URB; j++) +- kfree(portdata->out_buffer[j]); +-bail_out_error: +- for (j = 0; j < N_IN_URB; j++) +- if (portdata->in_buffer[j]) +- free_page((unsigned long)portdata->in_buffer[j]); +- kfree(portdata); +- return 1; +-} +- +-static void stop_read_write_urbs(struct usb_serial *serial) +-{ +- int i, j; +- struct usb_serial_port *port; +- struct option_port_private *portdata; +- +- /* Stop reading/writing urbs */ +- for (i = 0; i < serial->num_ports; ++i) { +- port = serial->port[i]; +- portdata = usb_get_serial_port_data(port); +- for (j = 0; j < N_IN_URB; j++) +- usb_kill_urb(portdata->in_urbs[j]); +- for (j = 0; j < N_OUT_URB; j++) +- usb_kill_urb(portdata->out_urbs[j]); +- } +-} +- +-static void option_disconnect(struct usb_serial *serial) +-{ +- dbg("%s", __func__); +- +- stop_read_write_urbs(serial); +-} +- +-static void option_release(struct usb_serial *serial) +-{ +- int i, j; +- struct usb_serial_port *port; +- struct option_port_private *portdata; +- +- dbg("%s", __func__); +- +- /* Now free them */ +- for (i = 0; i < serial->num_ports; ++i) { +- port = serial->port[i]; +- portdata = usb_get_serial_port_data(port); +- +- for (j = 0; j < N_IN_URB; j++) { +- if (portdata->in_urbs[j]) { +- usb_free_urb(portdata->in_urbs[j]); +- free_page((unsigned long) +- portdata->in_buffer[j]); +- portdata->in_urbs[j] = NULL; +- } +- } +- for (j = 0; j < N_OUT_URB; j++) { +- if (portdata->out_urbs[j]) { +- usb_free_urb(portdata->out_urbs[j]); +- kfree(portdata->out_buffer[j]); +- portdata->out_urbs[j] = NULL; +- } +- } +- } +- +- /* Now free per port private data */ +- for (i = 0; i < serial->num_ports; i++) { +- port = serial->port[i]; +- kfree(usb_get_serial_port_data(port)); +- } +-} +- +-#ifdef CONFIG_PM +-static int option_suspend(struct usb_serial *serial, pm_message_t message) +-{ +- struct option_intf_private *intfdata = serial->private; +- int b; +- +- dbg("%s entered", __func__); +- +- if (message.event & PM_EVENT_AUTO) { +- spin_lock_irq(&intfdata->susp_lock); +- b = intfdata->in_flight; +- spin_unlock_irq(&intfdata->susp_lock); +- +- if (b) +- return -EBUSY; +- } +- +- spin_lock_irq(&intfdata->susp_lock); +- intfdata->suspended = 1; +- spin_unlock_irq(&intfdata->susp_lock); +- stop_read_write_urbs(serial); +- +- return 0; +-} +- +-static void play_delayed(struct usb_serial_port *port) +-{ +- struct option_intf_private *data; +- struct option_port_private *portdata; +- struct urb *urb; +- int err; +- +- portdata = usb_get_serial_port_data(port); +- data = port->serial->private; +- while ((urb = usb_get_from_anchor(&portdata->delayed))) { +- err = usb_submit_urb(urb, GFP_ATOMIC); +- if (!err) +- data->in_flight++; +- } +-} +- +-static int option_resume(struct usb_serial *serial) +-{ +- int i, j; +- struct usb_serial_port *port; +- struct option_intf_private *intfdata = serial->private; +- struct option_port_private *portdata; +- struct urb *urb; +- int err = 0; +- +- dbg("%s entered", __func__); +- /* get the interrupt URBs resubmitted unconditionally */ +- for (i = 0; i < serial->num_ports; i++) { +- port = serial->port[i]; +- if (!port->interrupt_in_urb) { +- dbg("%s: No interrupt URB for port %d", __func__, i); +- continue; +- } +- err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); +- dbg("Submitted interrupt URB for port %d (result %d)", i, err); +- if (err < 0) { +- err("%s: Error %d for interrupt URB of port%d", +- __func__, err, i); +- goto err_out; +- } +- } +- +- for (i = 0; i < serial->num_ports; i++) { +- /* walk all ports */ +- port = serial->port[i]; +- portdata = usb_get_serial_port_data(port); +- +- /* skip closed ports */ +- spin_lock_irq(&intfdata->susp_lock); +- if (!portdata->opened) { +- spin_unlock_irq(&intfdata->susp_lock); +- continue; +- } +- +- for (j = 0; j < N_IN_URB; j++) { +- urb = portdata->in_urbs[j]; +- err = usb_submit_urb(urb, GFP_ATOMIC); +- if (err < 0) { +- err("%s: Error %d for bulk URB %d", +- __func__, err, i); +- spin_unlock_irq(&intfdata->susp_lock); +- goto err_out; +- } +- } +- play_delayed(port); +- spin_unlock_irq(&intfdata->susp_lock); +- } +- spin_lock_irq(&intfdata->susp_lock); +- intfdata->suspended = 0; +- spin_unlock_irq(&intfdata->susp_lock); +-err_out: +- return err; +-} +-#endif +- + MODULE_AUTHOR(DRIVER_AUTHOR); + MODULE_DESCRIPTION(DRIVER_DESC); + MODULE_VERSION(DRIVER_VERSION); diff --git a/usb/usb-otg-twl4030-use-the-global-ulpi-register-definitions.patch b/usb/usb-otg-twl4030-use-the-global-ulpi-register-definitions.patch new file mode 100644 index 00000000000000..28b2c9f9a6b9f8 --- /dev/null +++ b/usb/usb-otg-twl4030-use-the-global-ulpi-register-definitions.patch @@ -0,0 +1,215 @@ +From ext-heikki.krogerus@nokia.com Thu Apr 29 12:48:28 2010 +From: "Krogerus Heikki (EXT-Teleca/Helsinki)" <ext-heikki.krogerus@nokia.com> +Date: Wed, 31 Mar 2010 10:18:05 +0300 +Subject: usb: otg: twl4030: use the global ULPI register definitions +To: sshtylyov@mvista.com +Cc: linux-usb@vger.kernel.org, felipe.balbi@nokia.com, Heikki Krogerus <ext-heikki.krogerus@nokia.com> +Message-ID: <1270019885-23689-1-git-send-email-ext-heikki.krogerus@nokia.com> + + +From: Heikki Krogerus <ext-heikki.krogerus@nokia.com> + +Rely on the global ULPI register definitions + +Signed-off-by: Heikki Krogerus <ext-heikki.krogerus@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/otg/twl4030-usb.c | 108 +++--------------------------------------- + 1 file changed, 8 insertions(+), 100 deletions(-) + +--- a/drivers/usb/otg/twl4030-usb.c ++++ b/drivers/usb/otg/twl4030-usb.c +@@ -33,6 +33,7 @@ + #include <linux/io.h> + #include <linux/delay.h> + #include <linux/usb/otg.h> ++#include <linux/usb/ulpi.h> + #include <linux/i2c/twl.h> + #include <linux/regulator/consumer.h> + #include <linux/err.h> +@@ -41,81 +42,7 @@ + + /* Register defines */ + +-#define VENDOR_ID_LO 0x00 +-#define VENDOR_ID_HI 0x01 +-#define PRODUCT_ID_LO 0x02 +-#define PRODUCT_ID_HI 0x03 +- +-#define FUNC_CTRL 0x04 +-#define FUNC_CTRL_SET 0x05 +-#define FUNC_CTRL_CLR 0x06 +-#define FUNC_CTRL_SUSPENDM (1 << 6) +-#define FUNC_CTRL_RESET (1 << 5) +-#define FUNC_CTRL_OPMODE_MASK (3 << 3) /* bits 3 and 4 */ +-#define FUNC_CTRL_OPMODE_NORMAL (0 << 3) +-#define FUNC_CTRL_OPMODE_NONDRIVING (1 << 3) +-#define FUNC_CTRL_OPMODE_DISABLE_BIT_NRZI (2 << 3) +-#define FUNC_CTRL_TERMSELECT (1 << 2) +-#define FUNC_CTRL_XCVRSELECT_MASK (3 << 0) /* bits 0 and 1 */ +-#define FUNC_CTRL_XCVRSELECT_HS (0 << 0) +-#define FUNC_CTRL_XCVRSELECT_FS (1 << 0) +-#define FUNC_CTRL_XCVRSELECT_LS (2 << 0) +-#define FUNC_CTRL_XCVRSELECT_FS4LS (3 << 0) +- +-#define IFC_CTRL 0x07 +-#define IFC_CTRL_SET 0x08 +-#define IFC_CTRL_CLR 0x09 +-#define IFC_CTRL_INTERFACE_PROTECT_DISABLE (1 << 7) +-#define IFC_CTRL_AUTORESUME (1 << 4) +-#define IFC_CTRL_CLOCKSUSPENDM (1 << 3) +-#define IFC_CTRL_CARKITMODE (1 << 2) +-#define IFC_CTRL_FSLSSERIALMODE_3PIN (1 << 1) +- +-#define TWL4030_OTG_CTRL 0x0A +-#define TWL4030_OTG_CTRL_SET 0x0B +-#define TWL4030_OTG_CTRL_CLR 0x0C +-#define TWL4030_OTG_CTRL_DRVVBUS (1 << 5) +-#define TWL4030_OTG_CTRL_CHRGVBUS (1 << 4) +-#define TWL4030_OTG_CTRL_DISCHRGVBUS (1 << 3) +-#define TWL4030_OTG_CTRL_DMPULLDOWN (1 << 2) +-#define TWL4030_OTG_CTRL_DPPULLDOWN (1 << 1) +-#define TWL4030_OTG_CTRL_IDPULLUP (1 << 0) +- +-#define USB_INT_EN_RISE 0x0D +-#define USB_INT_EN_RISE_SET 0x0E +-#define USB_INT_EN_RISE_CLR 0x0F +-#define USB_INT_EN_FALL 0x10 +-#define USB_INT_EN_FALL_SET 0x11 +-#define USB_INT_EN_FALL_CLR 0x12 +-#define USB_INT_STS 0x13 +-#define USB_INT_LATCH 0x14 +-#define USB_INT_IDGND (1 << 4) +-#define USB_INT_SESSEND (1 << 3) +-#define USB_INT_SESSVALID (1 << 2) +-#define USB_INT_VBUSVALID (1 << 1) +-#define USB_INT_HOSTDISCONNECT (1 << 0) +- +-#define CARKIT_CTRL 0x19 +-#define CARKIT_CTRL_SET 0x1A +-#define CARKIT_CTRL_CLR 0x1B +-#define CARKIT_CTRL_MICEN (1 << 6) +-#define CARKIT_CTRL_SPKRIGHTEN (1 << 5) +-#define CARKIT_CTRL_SPKLEFTEN (1 << 4) +-#define CARKIT_CTRL_RXDEN (1 << 3) +-#define CARKIT_CTRL_TXDEN (1 << 2) +-#define CARKIT_CTRL_IDGNDDRV (1 << 1) +-#define CARKIT_CTRL_CARKITPWR (1 << 0) +-#define CARKIT_PLS_CTRL 0x22 +-#define CARKIT_PLS_CTRL_SET 0x23 +-#define CARKIT_PLS_CTRL_CLR 0x24 +-#define CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3) +-#define CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2) +-#define CARKIT_PLS_CTRL_RXPLSEN (1 << 1) +-#define CARKIT_PLS_CTRL_TXPLSEN (1 << 0) +- + #define MCPC_CTRL 0x30 +-#define MCPC_CTRL_SET 0x31 +-#define MCPC_CTRL_CLR 0x32 + #define MCPC_CTRL_RTSOL (1 << 7) + #define MCPC_CTRL_EXTSWR (1 << 6) + #define MCPC_CTRL_EXTSWC (1 << 5) +@@ -125,8 +52,6 @@ + #define MCPC_CTRL_HS_UART (1 << 0) + + #define MCPC_IO_CTRL 0x33 +-#define MCPC_IO_CTRL_SET 0x34 +-#define MCPC_IO_CTRL_CLR 0x35 + #define MCPC_IO_CTRL_MICBIASEN (1 << 5) + #define MCPC_IO_CTRL_CTS_NPU (1 << 4) + #define MCPC_IO_CTRL_RXD_PU (1 << 3) +@@ -135,19 +60,13 @@ + #define MCPC_IO_CTRL_RTSTYP (1 << 0) + + #define MCPC_CTRL2 0x36 +-#define MCPC_CTRL2_SET 0x37 +-#define MCPC_CTRL2_CLR 0x38 + #define MCPC_CTRL2_MCPC_CK_EN (1 << 0) + + #define OTHER_FUNC_CTRL 0x80 +-#define OTHER_FUNC_CTRL_SET 0x81 +-#define OTHER_FUNC_CTRL_CLR 0x82 + #define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) + #define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) + + #define OTHER_IFC_CTRL 0x83 +-#define OTHER_IFC_CTRL_SET 0x84 +-#define OTHER_IFC_CTRL_CLR 0x85 + #define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) + #define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) + #define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) +@@ -156,11 +75,7 @@ + #define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) + + #define OTHER_INT_EN_RISE 0x86 +-#define OTHER_INT_EN_RISE_SET 0x87 +-#define OTHER_INT_EN_RISE_CLR 0x88 + #define OTHER_INT_EN_FALL 0x89 +-#define OTHER_INT_EN_FALL_SET 0x8A +-#define OTHER_INT_EN_FALL_CLR 0x8B + #define OTHER_INT_STS 0x8C + #define OTHER_INT_LATCH 0x8D + #define OTHER_INT_VB_SESS_VLD (1 << 7) +@@ -178,13 +93,9 @@ + #define ID_RES_GND (1 << 0) + + #define POWER_CTRL 0xAC +-#define POWER_CTRL_SET 0xAD +-#define POWER_CTRL_CLR 0xAE + #define POWER_CTRL_OTG_ENAB (1 << 5) + + #define OTHER_IFC_CTRL2 0xAF +-#define OTHER_IFC_CTRL2_SET 0xB0 +-#define OTHER_IFC_CTRL2_CLR 0xB1 + #define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) + #define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) + #define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) +@@ -193,14 +104,10 @@ + #define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) + + #define REG_CTRL_EN 0xB2 +-#define REG_CTRL_EN_SET 0xB3 +-#define REG_CTRL_EN_CLR 0xB4 + #define REG_CTRL_ERROR 0xB5 + #define ULPI_I2C_CONFLICT_INTEN (1 << 0) + + #define OTHER_FUNC_CTRL2 0xB8 +-#define OTHER_FUNC_CTRL2_SET 0xB9 +-#define OTHER_FUNC_CTRL2_CLR 0xBA + #define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) + + /* following registers do not have separate _clr and _set registers */ +@@ -328,13 +235,13 @@ static inline int twl4030_usb_read(struc + static inline int + twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) + { +- return twl4030_usb_write(twl, reg + 1, bits); ++ return twl4030_usb_write(twl, ULPI_SET(reg), bits); + } + + static inline int + twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) + { +- return twl4030_usb_write(twl, reg + 2, bits); ++ return twl4030_usb_write(twl, ULPI_CLR(reg), bits); + } + + /*-------------------------------------------------------------------------*/ +@@ -393,11 +300,12 @@ static void twl4030_usb_set_mode(struct + + switch (mode) { + case T2_USB_MODE_ULPI: +- twl4030_usb_clear_bits(twl, IFC_CTRL, IFC_CTRL_CARKITMODE); ++ twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, ++ ULPI_IFC_CTRL_CARKITMODE); + twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); +- twl4030_usb_clear_bits(twl, FUNC_CTRL, +- FUNC_CTRL_XCVRSELECT_MASK | +- FUNC_CTRL_OPMODE_MASK); ++ twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, ++ ULPI_FUNC_CTRL_XCVRSEL_MASK | ++ ULPI_FUNC_CTRL_OPMODE_MASK); + break; + case -1: + /* FIXME: power on defaults */ diff --git a/usb/usb-pxa27x_udc-use-four-bits-to-store-endpoint-addresses.patch b/usb/usb-pxa27x_udc-use-four-bits-to-store-endpoint-addresses.patch new file mode 100644 index 00000000000000..470af7b3db3eeb --- /dev/null +++ b/usb/usb-pxa27x_udc-use-four-bits-to-store-endpoint-addresses.patch @@ -0,0 +1,31 @@ +From mreimer@sdgsystems.com Thu Apr 29 12:48:56 2010 +From: Matt Reimer <mreimer@sdgsystems.com> +Date: Thu, 1 Apr 2010 13:44:04 -0700 +Subject: USB: pxa27x_udc: use four bits to store endpoint addresses +To: linux-usb@vger.kernel.org +Cc: Matt Reimer <mreimer@sdgsystems.com> +Message-ID: <1270154644-13011-1-git-send-email-mreimer@sdgsystems.com> + + +Endpoint addresses on pxa27x can be programmed as 1-15, but since +only three bits were being used to store the endpoint number it +was possible to overflow. + +Signed-off-by: Matt Reimer <mreimer@sdgsystems.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/pxa27x_udc.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/gadget/pxa27x_udc.h ++++ b/drivers/usb/gadget/pxa27x_udc.h +@@ -360,7 +360,7 @@ struct pxa_ep { + * Specific pxa endpoint data, needed for hardware initialization + */ + unsigned dir_in:1; +- unsigned addr:3; ++ unsigned addr:4; + unsigned config:2; + unsigned interface:3; + unsigned alternate:3; diff --git a/usb/usb-qcserial-add-support-for-qualcomm-gobi-2000-devices.patch b/usb/usb-qcserial-add-support-for-qualcomm-gobi-2000-devices.patch new file mode 100644 index 00000000000000..7878d5db875f86 --- /dev/null +++ b/usb/usb-qcserial-add-support-for-qualcomm-gobi-2000-devices.patch @@ -0,0 +1,105 @@ +From mjg@redhat.com Thu Apr 29 12:56:50 2010 +From: Matthew Garrett <mjg@redhat.com> +Date: Thu, 1 Apr 2010 12:31:10 -0400 +Subject: USB: qcserial: Add support for Qualcomm Gobi 2000 devices +To: linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Anssi Hannula <anssi.hannula@gmail.com>, Matthew Garrett <mjg@redhat.com> +Message-ID: <1270139470-24360-4-git-send-email-mjg@redhat.com> + + +From: Anssi Hannula <anssi.hannula@gmail.com> + +Add ids for Qualcomm Gobi 2000 QDL and Modem modes. Gobi 2000 has a +single altsetting in QDL mode, so adapt code to handle that. + +Firmware upload protocol is also slightly different, with an +additional firmware file. However, qcserial doesn't handle firmware +uploading. + +Tested on Lenovo Thinkpad T510. + +Signed-off-by: Anssi Hannula <anssi.hannula@gmail.com> +Signed-off-by: Matthew Garrett <mjg@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/qcserial.c | 42 +++++++++++++++++++++++++----------------- + 1 file changed, 25 insertions(+), 17 deletions(-) + +--- a/drivers/usb/serial/qcserial.c ++++ b/drivers/usb/serial/qcserial.c +@@ -78,6 +78,8 @@ static const struct usb_device_id id_tab + {USB_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ + {USB_DEVICE(0x16d8, 0x8001)}, /* CMDTech Gobi 2000 QDL device (VU922) */ + {USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */ ++ {USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */ ++ {USB_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */ + { } /* Terminating entry */ + }; + MODULE_DEVICE_TABLE(usb, id_table); +@@ -95,6 +97,7 @@ static struct usb_driver qcdriver = { + static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) + { + struct usb_wwan_intf_private *data; ++ struct usb_host_interface *intf = serial->interface->cur_altsetting; + int retval = -ENODEV; + __u8 nintf; + __u8 ifnum; +@@ -103,7 +106,7 @@ static int qcprobe(struct usb_serial *se + + nintf = serial->dev->actconfig->desc.bNumInterfaces; + dbg("Num Interfaces = %d", nintf); +- ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; ++ ifnum = intf->desc.bInterfaceNumber; + dbg("This Interface = %d", ifnum); + + data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), +@@ -116,27 +119,32 @@ static int qcprobe(struct usb_serial *se + switch (nintf) { + case 1: + /* QDL mode */ +- if (serial->interface->num_altsetting == 2) { +- struct usb_host_interface *intf; +- ++ /* Gobi 2000 has a single altsetting, older ones have two */ ++ if (serial->interface->num_altsetting == 2) + intf = &serial->interface->altsetting[1]; +- if (intf->desc.bNumEndpoints == 2) { +- if (usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) && +- usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) { +- dbg("QDL port found"); +- retval = usb_set_interface(serial->dev, ifnum, 1); +- if (retval < 0) { +- dev_err(&serial->dev->dev, +- "Could not set interface, error %d\n", +- retval); +- retval = -ENODEV; +- } +- return retval; +- } ++ else if (serial->interface->num_altsetting > 2) ++ break; ++ ++ if (intf->desc.bNumEndpoints == 2 && ++ usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) && ++ usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) { ++ dbg("QDL port found"); ++ ++ if (serial->interface->num_altsetting == 1) ++ return 0; ++ ++ retval = usb_set_interface(serial->dev, ifnum, 1); ++ if (retval < 0) { ++ dev_err(&serial->dev->dev, ++ "Could not set interface, error %d\n", ++ retval); ++ retval = -ENODEV; + } ++ return retval; + } + break; + ++ case 3: + case 4: + /* Composite mode */ + if (ifnum == 2) { diff --git a/usb/usb-qcserial-use-generic-usb-wwan-code.patch b/usb/usb-qcserial-use-generic-usb-wwan-code.patch new file mode 100644 index 00000000000000..084ce302b54e31 --- /dev/null +++ b/usb/usb-qcserial-use-generic-usb-wwan-code.patch @@ -0,0 +1,82 @@ +From mjg@redhat.com Thu Apr 29 12:56:00 2010 +From: Matthew Garrett <mjg@redhat.com> +Date: Thu, 1 Apr 2010 12:31:09 -0400 +Subject: USB: qcserial: Use generic USB wwan code +To: linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Matthew Garrett <mjg@redhat.com> +Message-ID: <1270139470-24360-3-git-send-email-mjg@redhat.com> + + +Make qcserial use the generic USB wwan code. This should result in a +performance improvement. + +Signed-off-by: Matthew Garrett <mjg@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/Kconfig | 1 + + drivers/usb/serial/qcserial.c | 22 ++++++++++++++++++++++ + 2 files changed, 23 insertions(+) + +--- a/drivers/usb/serial/Kconfig ++++ b/drivers/usb/serial/Kconfig +@@ -485,6 +485,7 @@ config USB_SERIAL_QCAUX + + config USB_SERIAL_QUALCOMM + tristate "USB Qualcomm Serial modem" ++ select USB_SERIAL_WWAN + help + Say Y here if you have a Qualcomm USB modem device. These are + usually wireless cellular modems. +--- a/drivers/usb/serial/qcserial.c ++++ b/drivers/usb/serial/qcserial.c +@@ -15,6 +15,8 @@ + #include <linux/tty_flip.h> + #include <linux/usb.h> + #include <linux/usb/serial.h> ++#include <linux/slab.h> ++#include "usb-wwan.h" + + #define DRIVER_AUTHOR "Qualcomm Inc" + #define DRIVER_DESC "Qualcomm USB Serial driver" +@@ -92,6 +94,7 @@ static struct usb_driver qcdriver = { + + static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) + { ++ struct usb_wwan_intf_private *data; + int retval = -ENODEV; + __u8 nintf; + __u8 ifnum; +@@ -103,6 +106,13 @@ static int qcprobe(struct usb_serial *se + ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; + dbg("This Interface = %d", ifnum); + ++ data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), ++ GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ spin_lock_init(&data->susp_lock); ++ + switch (nintf) { + case 1: + /* QDL mode */ +@@ -161,6 +171,18 @@ static struct usb_serial_driver qcdevice + .usb_driver = &qcdriver, + .num_ports = 1, + .probe = qcprobe, ++ .open = usb_wwan_open, ++ .close = usb_wwan_close, ++ .write = usb_wwan_write, ++ .write_room = usb_wwan_write_room, ++ .chars_in_buffer = usb_wwan_chars_in_buffer, ++ .attach = usb_wwan_startup, ++ .disconnect = usb_wwan_disconnect, ++ .release = usb_wwan_release, ++#ifdef CONFIG_PM ++ .suspend = usb_wwan_suspend, ++ .resume = usb_wwan_resume, ++#endif + }; + + static int __init qcinit(void) diff --git a/usb/usb-serial-add-generic-usb-wwan-support.patch b/usb/usb-serial-add-generic-usb-wwan-support.patch new file mode 100644 index 00000000000000..5885cf1e03f3aa --- /dev/null +++ b/usb/usb-serial-add-generic-usb-wwan-support.patch @@ -0,0 +1,784 @@ +From mjg@redhat.com Thu Apr 29 12:53:31 2010 +From: Matthew Garrett <mjg@redhat.com> +Date: Thu, 1 Apr 2010 12:31:07 -0400 +Subject: usb serial: Add generic USB wwan support +To: linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Matthew Garrett <mjg@redhat.com> +Message-ID: <1270139470-24360-1-git-send-email-mjg@redhat.com> + + +The generic USB serial code is ill-suited for high-speed USB wwan devices, +resulting in the option driver. However, other non-option devices may also +gain similar benefits from not using the generic code. Factorise out the +non-option specific code from the option driver and make it available to +other users. + +Signed-off-by: Matthew Garrett <mjg@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/Kconfig | 3 + drivers/usb/serial/Makefile | 1 + drivers/usb/serial/usb-wwan.h | 67 ++++ + drivers/usb/serial/usb_wwan.c | 664 ++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 735 insertions(+) + +--- a/drivers/usb/serial/Kconfig ++++ b/drivers/usb/serial/Kconfig +@@ -576,6 +576,9 @@ config USB_SERIAL_XIRCOM + To compile this driver as a module, choose M here: the + module will be called keyspan_pda. + ++config USB_SERIAL_WWAN ++ tristate ++ + config USB_SERIAL_OPTION + tristate "USB driver for GSM and CDMA modems" + help +--- a/drivers/usb/serial/Makefile ++++ b/drivers/usb/serial/Makefile +@@ -52,6 +52,7 @@ obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += + obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o + obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o + obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o ++obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o + obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o + obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o + obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o +--- /dev/null ++++ b/drivers/usb/serial/usb-wwan.h +@@ -0,0 +1,67 @@ ++/* ++ * Definitions for USB serial mobile broadband cards ++ */ ++ ++#ifndef __LINUX_USB_USB_WWAN ++#define __LINUX_USB_USB_WWAN ++ ++extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on); ++extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port); ++extern void usb_wwan_close(struct usb_serial_port *port); ++extern int usb_wwan_startup(struct usb_serial *serial); ++extern void usb_wwan_disconnect(struct usb_serial *serial); ++extern void usb_wwan_release(struct usb_serial *serial); ++extern int usb_wwan_write_room(struct tty_struct *tty); ++extern void usb_wwan_set_termios(struct tty_struct *tty, ++ struct usb_serial_port *port, ++ struct ktermios *old); ++extern int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file); ++extern int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file, ++ unsigned int set, unsigned int clear); ++extern int usb_wwan_send_setup(struct usb_serial_port *port); ++extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, ++ const unsigned char *buf, int count); ++extern int usb_wwan_chars_in_buffer(struct tty_struct *tty); ++#ifdef CONFIG_PM ++extern int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message); ++extern int usb_wwan_resume(struct usb_serial *serial); ++#endif ++ ++/* per port private data */ ++ ++#define N_IN_URB 4 ++#define N_OUT_URB 4 ++#define IN_BUFLEN 4096 ++#define OUT_BUFLEN 4096 ++ ++struct usb_wwan_intf_private { ++ spinlock_t susp_lock; ++ unsigned int suspended:1; ++ int in_flight; ++ int (*send_setup) (struct usb_serial_port *port); ++ void *private; ++}; ++ ++struct usb_wwan_port_private { ++ /* Input endpoints and buffer for this port */ ++ struct urb *in_urbs[N_IN_URB]; ++ u8 *in_buffer[N_IN_URB]; ++ /* Output endpoints and buffer for this port */ ++ struct urb *out_urbs[N_OUT_URB]; ++ u8 *out_buffer[N_OUT_URB]; ++ unsigned long out_busy; /* Bit vector of URBs in use */ ++ int opened; ++ struct usb_anchor delayed; ++ ++ /* Settings for the port */ ++ int rts_state; /* Handshaking pins (outputs) */ ++ int dtr_state; ++ int cts_state; /* Handshaking pins (inputs) */ ++ int dsr_state; ++ int dcd_state; ++ int ri_state; ++ ++ unsigned long tx_start_time[N_OUT_URB]; ++}; ++ ++#endif /* __LINUX_USB_USB_WWAN */ +--- /dev/null ++++ b/drivers/usb/serial/usb_wwan.c +@@ -0,0 +1,664 @@ ++/* ++ USB Driver layer for GSM modems ++ ++ Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de> ++ ++ This driver is free software; you can redistribute it and/or modify ++ it under the terms of Version 2 of the GNU General Public License as ++ published by the Free Software Foundation. ++ ++ Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org> ++ ++ History: see the git log. ++ ++ Work sponsored by: Sigos GmbH, Germany <info@sigos.de> ++ ++ This driver exists because the "normal" serial driver doesn't work too well ++ with GSM modems. Issues: ++ - data loss -- one single Receive URB is not nearly enough ++ - controlling the baud rate doesn't make sense ++*/ ++ ++#define DRIVER_VERSION "v0.7.2" ++#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>" ++#define DRIVER_DESC "USB Driver for GSM modems" ++ ++#include <linux/kernel.h> ++#include <linux/jiffies.h> ++#include <linux/errno.h> ++#include <linux/tty.h> ++#include <linux/tty_flip.h> ++#include <linux/module.h> ++#include <linux/bitops.h> ++#include <linux/usb.h> ++#include <linux/usb/serial.h> ++#include "usb-wwan.h" ++ ++static int debug; ++ ++void usb_wwan_dtr_rts(struct usb_serial_port *port, int on) ++{ ++ struct usb_serial *serial = port->serial; ++ struct usb_wwan_port_private *portdata; ++ ++ struct usb_wwan_intf_private *intfdata; ++ ++ dbg("%s", __func__); ++ ++ intfdata = port->serial->private; ++ ++ if (!intfdata->send_setup) ++ return; ++ ++ portdata = usb_get_serial_port_data(port); ++ mutex_lock(&serial->disc_mutex); ++ portdata->rts_state = on; ++ portdata->dtr_state = on; ++ if (serial->dev) ++ intfdata->send_setup(port); ++ mutex_unlock(&serial->disc_mutex); ++} ++EXPORT_SYMBOL(usb_wwan_dtr_rts); ++ ++void usb_wwan_set_termios(struct tty_struct *tty, ++ struct usb_serial_port *port, ++ struct ktermios *old_termios) ++{ ++ struct usb_wwan_intf_private *intfdata = port->serial->private; ++ ++ dbg("%s", __func__); ++ ++ /* Doesn't support option setting */ ++ tty_termios_copy_hw(tty->termios, old_termios); ++ ++ if (intfdata->send_setup) ++ intfdata->send_setup(port); ++} ++EXPORT_SYMBOL(usb_wwan_set_termios); ++ ++int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file) ++{ ++ struct usb_serial_port *port = tty->driver_data; ++ unsigned int value; ++ struct usb_wwan_port_private *portdata; ++ ++ portdata = usb_get_serial_port_data(port); ++ ++ value = ((portdata->rts_state) ? TIOCM_RTS : 0) | ++ ((portdata->dtr_state) ? TIOCM_DTR : 0) | ++ ((portdata->cts_state) ? TIOCM_CTS : 0) | ++ ((portdata->dsr_state) ? TIOCM_DSR : 0) | ++ ((portdata->dcd_state) ? TIOCM_CAR : 0) | ++ ((portdata->ri_state) ? TIOCM_RNG : 0); ++ ++ return value; ++} ++EXPORT_SYMBOL(usb_wwan_tiocmget); ++ ++int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file, ++ unsigned int set, unsigned int clear) ++{ ++ struct usb_serial_port *port = tty->driver_data; ++ struct usb_wwan_port_private *portdata; ++ struct usb_wwan_intf_private *intfdata; ++ ++ portdata = usb_get_serial_port_data(port); ++ intfdata = port->serial->private; ++ ++ if (!intfdata->send_setup) ++ return -EINVAL; ++ ++ /* FIXME: what locks portdata fields ? */ ++ if (set & TIOCM_RTS) ++ portdata->rts_state = 1; ++ if (set & TIOCM_DTR) ++ portdata->dtr_state = 1; ++ ++ if (clear & TIOCM_RTS) ++ portdata->rts_state = 0; ++ if (clear & TIOCM_DTR) ++ portdata->dtr_state = 0; ++ return intfdata->send_setup(port); ++} ++EXPORT_SYMBOL(usb_wwan_tiocmset); ++ ++/* Write */ ++int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, ++ const unsigned char *buf, int count) ++{ ++ struct usb_wwan_port_private *portdata; ++ struct usb_wwan_intf_private *intfdata; ++ int i; ++ int left, todo; ++ struct urb *this_urb = NULL; /* spurious */ ++ int err; ++ unsigned long flags; ++ ++ portdata = usb_get_serial_port_data(port); ++ intfdata = port->serial->private; ++ ++ dbg("%s: write (%d chars)", __func__, count); ++ ++ i = 0; ++ left = count; ++ for (i = 0; left > 0 && i < N_OUT_URB; i++) { ++ todo = left; ++ if (todo > OUT_BUFLEN) ++ todo = OUT_BUFLEN; ++ ++ this_urb = portdata->out_urbs[i]; ++ if (test_and_set_bit(i, &portdata->out_busy)) { ++ if (time_before(jiffies, ++ portdata->tx_start_time[i] + 10 * HZ)) ++ continue; ++ usb_unlink_urb(this_urb); ++ continue; ++ } ++ dbg("%s: endpoint %d buf %d", __func__, ++ usb_pipeendpoint(this_urb->pipe), i); ++ ++ err = usb_autopm_get_interface_async(port->serial->interface); ++ if (err < 0) ++ break; ++ ++ /* send the data */ ++ memcpy(this_urb->transfer_buffer, buf, todo); ++ this_urb->transfer_buffer_length = todo; ++ ++ spin_lock_irqsave(&intfdata->susp_lock, flags); ++ if (intfdata->suspended) { ++ usb_anchor_urb(this_urb, &portdata->delayed); ++ spin_unlock_irqrestore(&intfdata->susp_lock, flags); ++ } else { ++ intfdata->in_flight++; ++ spin_unlock_irqrestore(&intfdata->susp_lock, flags); ++ err = usb_submit_urb(this_urb, GFP_ATOMIC); ++ if (err) { ++ dbg("usb_submit_urb %p (write bulk) failed " ++ "(%d)", this_urb, err); ++ clear_bit(i, &portdata->out_busy); ++ spin_lock_irqsave(&intfdata->susp_lock, flags); ++ intfdata->in_flight--; ++ spin_unlock_irqrestore(&intfdata->susp_lock, ++ flags); ++ continue; ++ } ++ } ++ ++ portdata->tx_start_time[i] = jiffies; ++ buf += todo; ++ left -= todo; ++ } ++ ++ count -= left; ++ dbg("%s: wrote (did %d)", __func__, count); ++ return count; ++} ++EXPORT_SYMBOL(usb_wwan_write); ++ ++static void usb_wwan_indat_callback(struct urb *urb) ++{ ++ int err; ++ int endpoint; ++ struct usb_serial_port *port; ++ struct tty_struct *tty; ++ unsigned char *data = urb->transfer_buffer; ++ int status = urb->status; ++ ++ dbg("%s: %p", __func__, urb); ++ ++ endpoint = usb_pipeendpoint(urb->pipe); ++ port = urb->context; ++ ++ if (status) { ++ dbg("%s: nonzero status: %d on endpoint %02x.", ++ __func__, status, endpoint); ++ } else { ++ tty = tty_port_tty_get(&port->port); ++ if (urb->actual_length) { ++ tty_insert_flip_string(tty, data, urb->actual_length); ++ tty_flip_buffer_push(tty); ++ } else ++ dbg("%s: empty read urb received", __func__); ++ tty_kref_put(tty); ++ ++ /* Resubmit urb so we continue receiving */ ++ if (status != -ESHUTDOWN) { ++ err = usb_submit_urb(urb, GFP_ATOMIC); ++ if (err && err != -EPERM) ++ printk(KERN_ERR "%s: resubmit read urb failed. " ++ "(%d)", __func__, err); ++ else ++ usb_mark_last_busy(port->serial->dev); ++ } ++ ++ } ++ return; ++} ++ ++static void usb_wwan_outdat_callback(struct urb *urb) ++{ ++ struct usb_serial_port *port; ++ struct usb_wwan_port_private *portdata; ++ struct usb_wwan_intf_private *intfdata; ++ int i; ++ ++ dbg("%s", __func__); ++ ++ port = urb->context; ++ intfdata = port->serial->private; ++ ++ usb_serial_port_softint(port); ++ usb_autopm_put_interface_async(port->serial->interface); ++ portdata = usb_get_serial_port_data(port); ++ spin_lock(&intfdata->susp_lock); ++ intfdata->in_flight--; ++ spin_unlock(&intfdata->susp_lock); ++ ++ for (i = 0; i < N_OUT_URB; ++i) { ++ if (portdata->out_urbs[i] == urb) { ++ smp_mb__before_clear_bit(); ++ clear_bit(i, &portdata->out_busy); ++ break; ++ } ++ } ++} ++ ++int usb_wwan_write_room(struct tty_struct *tty) ++{ ++ struct usb_serial_port *port = tty->driver_data; ++ struct usb_wwan_port_private *portdata; ++ int i; ++ int data_len = 0; ++ struct urb *this_urb; ++ ++ portdata = usb_get_serial_port_data(port); ++ ++ for (i = 0; i < N_OUT_URB; i++) { ++ this_urb = portdata->out_urbs[i]; ++ if (this_urb && !test_bit(i, &portdata->out_busy)) ++ data_len += OUT_BUFLEN; ++ } ++ ++ dbg("%s: %d", __func__, data_len); ++ return data_len; ++} ++EXPORT_SYMBOL(usb_wwan_write_room); ++ ++int usb_wwan_chars_in_buffer(struct tty_struct *tty) ++{ ++ struct usb_serial_port *port = tty->driver_data; ++ struct usb_wwan_port_private *portdata; ++ int i; ++ int data_len = 0; ++ struct urb *this_urb; ++ ++ portdata = usb_get_serial_port_data(port); ++ ++ for (i = 0; i < N_OUT_URB; i++) { ++ this_urb = portdata->out_urbs[i]; ++ /* FIXME: This locking is insufficient as this_urb may ++ go unused during the test */ ++ if (this_urb && test_bit(i, &portdata->out_busy)) ++ data_len += this_urb->transfer_buffer_length; ++ } ++ dbg("%s: %d", __func__, data_len); ++ return data_len; ++} ++EXPORT_SYMBOL(usb_wwan_chars_in_buffer); ++ ++int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port) ++{ ++ struct usb_wwan_port_private *portdata; ++ struct usb_wwan_intf_private *intfdata; ++ struct usb_serial *serial = port->serial; ++ int i, err; ++ struct urb *urb; ++ ++ portdata = usb_get_serial_port_data(port); ++ intfdata = serial->private; ++ ++ dbg("%s", __func__); ++ ++ /* Start reading from the IN endpoint */ ++ for (i = 0; i < N_IN_URB; i++) { ++ urb = portdata->in_urbs[i]; ++ if (!urb) ++ continue; ++ err = usb_submit_urb(urb, GFP_KERNEL); ++ if (err) { ++ dbg("%s: submit urb %d failed (%d) %d", ++ __func__, i, err, urb->transfer_buffer_length); ++ } ++ } ++ ++ if (intfdata->send_setup) ++ intfdata->send_setup(port); ++ ++ serial->interface->needs_remote_wakeup = 1; ++ spin_lock_irq(&intfdata->susp_lock); ++ portdata->opened = 1; ++ spin_unlock_irq(&intfdata->susp_lock); ++ usb_autopm_put_interface(serial->interface); ++ ++ return 0; ++} ++EXPORT_SYMBOL(usb_wwan_open); ++ ++void usb_wwan_close(struct usb_serial_port *port) ++{ ++ int i; ++ struct usb_serial *serial = port->serial; ++ struct usb_wwan_port_private *portdata; ++ struct usb_wwan_intf_private *intfdata = port->serial->private; ++ ++ dbg("%s", __func__); ++ portdata = usb_get_serial_port_data(port); ++ ++ if (serial->dev) { ++ /* Stop reading/writing urbs */ ++ spin_lock_irq(&intfdata->susp_lock); ++ portdata->opened = 0; ++ spin_unlock_irq(&intfdata->susp_lock); ++ ++ for (i = 0; i < N_IN_URB; i++) ++ usb_kill_urb(portdata->in_urbs[i]); ++ for (i = 0; i < N_OUT_URB; i++) ++ usb_kill_urb(portdata->out_urbs[i]); ++ usb_autopm_get_interface(serial->interface); ++ serial->interface->needs_remote_wakeup = 0; ++ } ++} ++EXPORT_SYMBOL(usb_wwan_close); ++ ++/* Helper functions used by usb_wwan_setup_urbs */ ++static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint, ++ int dir, void *ctx, char *buf, int len, ++ void (*callback) (struct urb *)) ++{ ++ struct urb *urb; ++ ++ if (endpoint == -1) ++ return NULL; /* endpoint not needed */ ++ ++ urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ ++ if (urb == NULL) { ++ dbg("%s: alloc for endpoint %d failed.", __func__, endpoint); ++ return NULL; ++ } ++ ++ /* Fill URB using supplied data. */ ++ usb_fill_bulk_urb(urb, serial->dev, ++ usb_sndbulkpipe(serial->dev, endpoint) | dir, ++ buf, len, callback, ctx); ++ ++ return urb; ++} ++ ++/* Setup urbs */ ++static void usb_wwan_setup_urbs(struct usb_serial *serial) ++{ ++ int i, j; ++ struct usb_serial_port *port; ++ struct usb_wwan_port_private *portdata; ++ ++ dbg("%s", __func__); ++ ++ for (i = 0; i < serial->num_ports; i++) { ++ port = serial->port[i]; ++ portdata = usb_get_serial_port_data(port); ++ ++ /* Do indat endpoints first */ ++ for (j = 0; j < N_IN_URB; ++j) { ++ portdata->in_urbs[j] = usb_wwan_setup_urb(serial, ++ port-> ++ bulk_in_endpointAddress, ++ USB_DIR_IN, ++ port, ++ portdata-> ++ in_buffer[j], ++ IN_BUFLEN, ++ usb_wwan_indat_callback); ++ } ++ ++ /* outdat endpoints */ ++ for (j = 0; j < N_OUT_URB; ++j) { ++ portdata->out_urbs[j] = usb_wwan_setup_urb(serial, ++ port-> ++ bulk_out_endpointAddress, ++ USB_DIR_OUT, ++ port, ++ portdata-> ++ out_buffer ++ [j], ++ OUT_BUFLEN, ++ usb_wwan_outdat_callback); ++ } ++ } ++} ++ ++int usb_wwan_startup(struct usb_serial *serial) ++{ ++ int i, j, err; ++ struct usb_serial_port *port; ++ struct usb_wwan_port_private *portdata; ++ u8 *buffer; ++ ++ dbg("%s", __func__); ++ ++ /* Now setup per port private data */ ++ for (i = 0; i < serial->num_ports; i++) { ++ port = serial->port[i]; ++ portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); ++ if (!portdata) { ++ dbg("%s: kmalloc for usb_wwan_port_private (%d) failed!.", ++ __func__, i); ++ return 1; ++ } ++ init_usb_anchor(&portdata->delayed); ++ ++ for (j = 0; j < N_IN_URB; j++) { ++ buffer = (u8 *) __get_free_page(GFP_KERNEL); ++ if (!buffer) ++ goto bail_out_error; ++ portdata->in_buffer[j] = buffer; ++ } ++ ++ for (j = 0; j < N_OUT_URB; j++) { ++ buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); ++ if (!buffer) ++ goto bail_out_error2; ++ portdata->out_buffer[j] = buffer; ++ } ++ ++ usb_set_serial_port_data(port, portdata); ++ ++ if (!port->interrupt_in_urb) ++ continue; ++ err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); ++ if (err) ++ dbg("%s: submit irq_in urb failed %d", __func__, err); ++ } ++ usb_wwan_setup_urbs(serial); ++ return 0; ++ ++bail_out_error2: ++ for (j = 0; j < N_OUT_URB; j++) ++ kfree(portdata->out_buffer[j]); ++bail_out_error: ++ for (j = 0; j < N_IN_URB; j++) ++ if (portdata->in_buffer[j]) ++ free_page((unsigned long)portdata->in_buffer[j]); ++ kfree(portdata); ++ return 1; ++} ++EXPORT_SYMBOL(usb_wwan_startup); ++ ++static void stop_read_write_urbs(struct usb_serial *serial) ++{ ++ int i, j; ++ struct usb_serial_port *port; ++ struct usb_wwan_port_private *portdata; ++ ++ /* Stop reading/writing urbs */ ++ for (i = 0; i < serial->num_ports; ++i) { ++ port = serial->port[i]; ++ portdata = usb_get_serial_port_data(port); ++ for (j = 0; j < N_IN_URB; j++) ++ usb_kill_urb(portdata->in_urbs[j]); ++ for (j = 0; j < N_OUT_URB; j++) ++ usb_kill_urb(portdata->out_urbs[j]); ++ } ++} ++ ++void usb_wwan_disconnect(struct usb_serial *serial) ++{ ++ dbg("%s", __func__); ++ ++ stop_read_write_urbs(serial); ++} ++EXPORT_SYMBOL(usb_wwan_disconnect); ++ ++void usb_wwan_release(struct usb_serial *serial) ++{ ++ int i, j; ++ struct usb_serial_port *port; ++ struct usb_wwan_port_private *portdata; ++ ++ dbg("%s", __func__); ++ ++ /* Now free them */ ++ for (i = 0; i < serial->num_ports; ++i) { ++ port = serial->port[i]; ++ portdata = usb_get_serial_port_data(port); ++ ++ for (j = 0; j < N_IN_URB; j++) { ++ usb_free_urb(portdata->in_urbs[j]); ++ free_page((unsigned long) ++ portdata->in_buffer[j]); ++ portdata->in_urbs[j] = NULL; ++ } ++ for (j = 0; j < N_OUT_URB; j++) { ++ usb_free_urb(portdata->out_urbs[j]); ++ kfree(portdata->out_buffer[j]); ++ portdata->out_urbs[j] = NULL; ++ } ++ } ++ ++ /* Now free per port private data */ ++ for (i = 0; i < serial->num_ports; i++) { ++ port = serial->port[i]; ++ kfree(usb_get_serial_port_data(port)); ++ } ++} ++EXPORT_SYMBOL(usb_wwan_release); ++ ++#ifdef CONFIG_PM ++int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message) ++{ ++ struct usb_wwan_intf_private *intfdata = serial->private; ++ int b; ++ ++ dbg("%s entered", __func__); ++ ++ if (message.event & PM_EVENT_AUTO) { ++ spin_lock_irq(&intfdata->susp_lock); ++ b = intfdata->in_flight; ++ spin_unlock_irq(&intfdata->susp_lock); ++ ++ if (b) ++ return -EBUSY; ++ } ++ ++ spin_lock_irq(&intfdata->susp_lock); ++ intfdata->suspended = 1; ++ spin_unlock_irq(&intfdata->susp_lock); ++ stop_read_write_urbs(serial); ++ ++ return 0; ++} ++EXPORT_SYMBOL(usb_wwan_suspend); ++ ++static void play_delayed(struct usb_serial_port *port) ++{ ++ struct usb_wwan_intf_private *data; ++ struct usb_wwan_port_private *portdata; ++ struct urb *urb; ++ int err; ++ ++ portdata = usb_get_serial_port_data(port); ++ data = port->serial->private; ++ while ((urb = usb_get_from_anchor(&portdata->delayed))) { ++ err = usb_submit_urb(urb, GFP_ATOMIC); ++ if (!err) ++ data->in_flight++; ++ } ++} ++ ++int usb_wwan_resume(struct usb_serial *serial) ++{ ++ int i, j; ++ struct usb_serial_port *port; ++ struct usb_wwan_intf_private *intfdata = serial->private; ++ struct usb_wwan_port_private *portdata; ++ struct urb *urb; ++ int err = 0; ++ ++ dbg("%s entered", __func__); ++ /* get the interrupt URBs resubmitted unconditionally */ ++ for (i = 0; i < serial->num_ports; i++) { ++ port = serial->port[i]; ++ if (!port->interrupt_in_urb) { ++ dbg("%s: No interrupt URB for port %d", __func__, i); ++ continue; ++ } ++ err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); ++ dbg("Submitted interrupt URB for port %d (result %d)", i, err); ++ if (err < 0) { ++ err("%s: Error %d for interrupt URB of port%d", ++ __func__, err, i); ++ goto err_out; ++ } ++ } ++ ++ for (i = 0; i < serial->num_ports; i++) { ++ /* walk all ports */ ++ port = serial->port[i]; ++ portdata = usb_get_serial_port_data(port); ++ ++ /* skip closed ports */ ++ spin_lock_irq(&intfdata->susp_lock); ++ if (!portdata->opened) { ++ spin_unlock_irq(&intfdata->susp_lock); ++ continue; ++ } ++ ++ for (j = 0; j < N_IN_URB; j++) { ++ urb = portdata->in_urbs[j]; ++ err = usb_submit_urb(urb, GFP_ATOMIC); ++ if (err < 0) { ++ err("%s: Error %d for bulk URB %d", ++ __func__, err, i); ++ spin_unlock_irq(&intfdata->susp_lock); ++ goto err_out; ++ } ++ } ++ play_delayed(port); ++ spin_unlock_irq(&intfdata->susp_lock); ++ } ++ spin_lock_irq(&intfdata->susp_lock); ++ intfdata->suspended = 0; ++ spin_unlock_irq(&intfdata->susp_lock); ++err_out: ++ return err; ++} ++EXPORT_SYMBOL(usb_wwan_resume); ++#endif ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_VERSION(DRIVER_VERSION); ++MODULE_LICENSE("GPL"); ++ ++module_param(debug, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(debug, "Debug messages"); diff --git a/usb/usb-storage-remove-unneeded-sl11r-unusual_devs-entry.patch b/usb/usb-storage-remove-unneeded-sl11r-unusual_devs-entry.patch new file mode 100644 index 00000000000000..ccb4affd3e75b8 --- /dev/null +++ b/usb/usb-storage-remove-unneeded-sl11r-unusual_devs-entry.patch @@ -0,0 +1,42 @@ +From phil@ipom.com Thu Apr 29 13:00:25 2010 +From: Phil Dibowitz <phil@ipom.com> +Date: Sun, 04 Apr 2010 14:21:01 +0200 +Subject: USB: storage: Remove unneeded SL11R unusual_devs entry +To: Greg KH <greg@kroah.com> +Cc: Matthew Dharm <mdharm-usb@one-eyed-alien.net>, USB Dev <linux-usb@vger.kernel.org>, USB Storage list <usb-storage@lists.one-eyed-alien.net> +Message-ID: <4BB8842D.5090304@ipom.com> + + +It seems unlikely that this entry is needed anymore since the kernel +has logic to handle devices that poorly respond to INQUIRY. Since we +now have another entry with the same VID/PID but different flags, it's +a good time to attempt to clean this up. + +The original submitter's email no longer works, so we'll keep an eye +out for any regression reports. + +Signed-off-by: Phil Dibowitz <phil@ipom.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/storage/unusual_devs.h | 9 --------- + 1 file changed, 9 deletions(-) + +--- a/drivers/usb/storage/unusual_devs.h ++++ b/drivers/usb/storage/unusual_devs.h +@@ -365,15 +365,6 @@ UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x + "FinePix 1400Zoom", + US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY | US_FL_SINGLE_LUN), + +-/* Reported by Peter Wächtler <pwaechtler@loewe-komp.de> +- * The device needs the flags only. +- */ +-UNUSUAL_DEV( 0x04ce, 0x0002, 0x0074, 0x0074, +- "ScanLogic", +- "SL11R-IDE", +- US_SC_DEVICE, US_PR_DEVICE, NULL, +- US_FL_FIX_INQUIRY), +- + /* Reported by Ondrej Zary <linux@rainbow-software.org> + * The device reports one sector more and breaks when that sector is accessed + */ diff --git a/usb/usb-support-for-allocating-usb-3.0-streams.patch b/usb/usb-support-for-allocating-usb-3.0-streams.patch new file mode 100644 index 00000000000000..24ffbd0f1eb7f1 --- /dev/null +++ b/usb/usb-support-for-allocating-usb-3.0-streams.patch @@ -0,0 +1,248 @@ +From sarah.a.sharp@linux.intel.com Thu Apr 29 12:59:59 2010 +From: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Date: Mon, 5 Apr 2010 10:55:58 -0700 +Subject: USB: Support for allocating USB 3.0 streams. +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, Alan Stern <stern@rowland.harvard.edu>, usb-storage@lists.one-eyed-alien.net, Hrant Dalalyan <Hrant.Dalalyan@synopsys.com>, Paul Zimmerman <Paul.Zimmerman@synopsys.com>, Ashot Madatyan <Ashot.Madatyan@synopsys.com> +Message-ID: <20100405175558.GA11643@xanatos> +Content-Disposition: inline + + +Bulk endpoint streams were added in the USB 3.0 specification. Streams +allow a device driver to overload a bulk endpoint so that multiple +transfers can be queued at once. + +The device then decides which transfer it wants to work on first, and can +queue part of a transfer before it switches to a new stream. All this +switching is invisible to the device driver, which just gets a completion +for the URB. Drivers that use streams must be able to handle URBs +completing in a different order than they were submitted to the endpoint. + +This requires adding new API to set up xHCI data structures to support +multiple queues ("stream rings") per endpoint. Drivers will allocate a +number of stream IDs before enqueueing URBs to the bulk endpoints of the +device, and free the stream IDs in their disconnect function. See +Documentation/usb/bulk-streams.txt for details. + +The new mass storage device class, USB Attached SCSI Protocol (UASP), uses +these streams API. + +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/usb/bulk-streams.txt | 78 +++++++++++++++++++++++++++++++++++++ + drivers/usb/core/hcd.c | 69 ++++++++++++++++++++++++++++++++ + drivers/usb/core/hcd.h | 10 ++++ + drivers/usb/host/xhci-pci.c | 2 + include/linux/usb.h | 10 ++++ + 5 files changed, 169 insertions(+) + +--- /dev/null ++++ b/Documentation/usb/bulk-streams.txt +@@ -0,0 +1,78 @@ ++Background ++========== ++ ++Bulk endpoint streams were added in the USB 3.0 specification. Streams allow a ++device driver to overload a bulk endpoint so that multiple transfers can be ++queued at once. ++ ++Streams are defined in sections 4.4.6.4 and 8.12.1.4 of the Universal Serial Bus ++3.0 specification at http://www.usb.org/developers/docs/ The USB Attached SCSI ++Protocol, which uses streams to queue multiple SCSI commands, can be found on ++the T10 website (http://t10.org/). ++ ++ ++Device-side implications ++======================== ++ ++Once a buffer has been queued to a stream ring, the device is notified (through ++an out-of-band mechanism on another endpoint) that data is ready for that stream ++ID. The device then tells the host which "stream" it wants to start. The host ++can also initiate a transfer on a stream without the device asking, but the ++device can refuse that transfer. Devices can switch between streams at any ++time. ++ ++ ++Driver implications ++=================== ++ ++int usb_alloc_streams(struct usb_interface *interface, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ unsigned int num_streams, gfp_t mem_flags); ++ ++Device drivers will call this API to request that the host controller driver ++allocate memory so the driver can use up to num_streams stream IDs. They must ++pass an array of usb_host_endpoints that need to be setup with similar stream ++IDs. This is to ensure that a UASP driver will be able to use the same stream ++ID for the bulk IN and OUT endpoints used in a Bi-directional command sequence. ++ ++The return value is an error condition (if one of the endpoints doesn't support ++streams, or the xHCI driver ran out of memory), or the number of streams the ++host controller allocated for this endpoint. The xHCI host controller hardware ++declares how many stream IDs it can support, and each bulk endpoint on a ++SuperSpeed device will say how many stream IDs it can handle. Therefore, ++drivers should be able to deal with being allocated less stream IDs than they ++requested. ++ ++Do NOT call this function if you have URBs enqueued for any of the endpoints ++passed in as arguments. Do not call this function to request less than two ++streams. ++ ++Drivers will only be allowed to call this API once for the same endpoint ++without calling usb_free_streams(). This is a simplification for the xHCI host ++controller driver, and may change in the future. ++ ++ ++Picking new Stream IDs to use ++============================ ++ ++Stream ID 0 is reserved, and should not be used to communicate with devices. If ++usb_alloc_streams() returns with a value of N, you may use streams 1 though N. ++To queue an URB for a specific stream, set the urb->stream_id value. If the ++endpoint does not support streams, an error will be returned. ++ ++Note that new API to choose the next stream ID will have to be added if the xHCI ++driver supports secondary stream IDs. ++ ++ ++Clean up ++======== ++ ++If a driver wishes to stop using streams to communicate with the device, it ++should call ++ ++void usb_free_streams(struct usb_interface *interface, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ gfp_t mem_flags); ++ ++All stream IDs will be deallocated when the driver releases the interface, to ++ensure that drivers that don't support streams will be able to use the endpoint. +--- a/drivers/usb/core/hcd.c ++++ b/drivers/usb/core/hcd.c +@@ -1815,6 +1815,75 @@ void usb_hcd_reset_endpoint(struct usb_d + } + } + ++/** ++ * usb_alloc_streams - allocate bulk endpoint stream IDs. ++ * @interface: alternate setting that includes all endpoints. ++ * @eps: array of endpoints that need streams. ++ * @num_eps: number of endpoints in the array. ++ * @num_streams: number of streams to allocate. ++ * @mem_flags: flags hcd should use to allocate memory. ++ * ++ * Sets up a group of bulk endpoints to have num_streams stream IDs available. ++ * Drivers may queue multiple transfers to different stream IDs, which may ++ * complete in a different order than they were queued. ++ */ ++int usb_alloc_streams(struct usb_interface *interface, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ unsigned int num_streams, gfp_t mem_flags) ++{ ++ struct usb_hcd *hcd; ++ struct usb_device *dev; ++ int i; ++ ++ dev = interface_to_usbdev(interface); ++ hcd = bus_to_hcd(dev->bus); ++ if (!hcd->driver->alloc_streams || !hcd->driver->free_streams) ++ return -EINVAL; ++ if (dev->speed != USB_SPEED_SUPER) ++ return -EINVAL; ++ ++ /* Streams only apply to bulk endpoints. */ ++ for (i = 0; i < num_eps; i++) ++ if (!usb_endpoint_xfer_bulk(&eps[i]->desc)) ++ return -EINVAL; ++ ++ return hcd->driver->alloc_streams(hcd, dev, eps, num_eps, ++ num_streams, mem_flags); ++} ++EXPORT_SYMBOL_GPL(usb_alloc_streams); ++ ++/** ++ * usb_free_streams - free bulk endpoint stream IDs. ++ * @interface: alternate setting that includes all endpoints. ++ * @eps: array of endpoints to remove streams from. ++ * @num_eps: number of endpoints in the array. ++ * @mem_flags: flags hcd should use to allocate memory. ++ * ++ * Reverts a group of bulk endpoints back to not using stream IDs. ++ * Can fail if we are given bad arguments, or HCD is broken. ++ */ ++void usb_free_streams(struct usb_interface *interface, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ gfp_t mem_flags) ++{ ++ struct usb_hcd *hcd; ++ struct usb_device *dev; ++ int i; ++ ++ dev = interface_to_usbdev(interface); ++ hcd = bus_to_hcd(dev->bus); ++ if (dev->speed != USB_SPEED_SUPER) ++ return; ++ ++ /* Streams only apply to bulk endpoints. */ ++ for (i = 0; i < num_eps; i++) ++ if (!usb_endpoint_xfer_bulk(&eps[i]->desc)) ++ return; ++ ++ hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags); ++} ++EXPORT_SYMBOL_GPL(usb_free_streams); ++ + /* Protect against drivers that try to unlink URBs after the device + * is gone, by waiting until all unlinks for @udev are finished. + * Since we don't currently track URBs by device, simply wait until +--- a/drivers/usb/core/hcd.h ++++ b/drivers/usb/core/hcd.h +@@ -250,6 +250,16 @@ struct hc_driver { + int (*alloc_dev)(struct usb_hcd *, struct usb_device *); + /* Called by usb_disconnect to free HC device structures */ + void (*free_dev)(struct usb_hcd *, struct usb_device *); ++ /* Change a group of bulk endpoints to support multiple stream IDs */ ++ int (*alloc_streams)(struct usb_hcd *hcd, struct usb_device *udev, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ unsigned int num_streams, gfp_t mem_flags); ++ /* Reverts a group of bulk endpoints back to not using stream IDs. ++ * Can fail if we run out of memory. ++ */ ++ int (*free_streams)(struct usb_hcd *hcd, struct usb_device *udev, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ gfp_t mem_flags); + + /* Bandwidth computation functions */ + /* Note that add_endpoint() can only be called once per endpoint before +--- a/drivers/usb/host/xhci-pci.c ++++ b/drivers/usb/host/xhci-pci.c +@@ -132,6 +132,8 @@ static const struct hc_driver xhci_pci_h + .urb_dequeue = xhci_urb_dequeue, + .alloc_dev = xhci_alloc_dev, + .free_dev = xhci_free_dev, ++ .alloc_streams = xhci_alloc_streams, ++ .free_streams = xhci_free_streams, + .add_endpoint = xhci_add_endpoint, + .drop_endpoint = xhci_drop_endpoint, + .endpoint_reset = xhci_endpoint_reset, +--- a/include/linux/usb.h ++++ b/include/linux/usb.h +@@ -570,6 +570,16 @@ static inline void usb_mark_last_busy(st + /* for drivers using iso endpoints */ + extern int usb_get_current_frame_number(struct usb_device *usb_dev); + ++/* Sets up a group of bulk endpoints to support multiple stream IDs. */ ++extern int usb_alloc_streams(struct usb_interface *interface, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ unsigned int num_streams, gfp_t mem_flags); ++ ++/* Reverts a group of bulk endpoints back to not using stream IDs. */ ++extern void usb_free_streams(struct usb_interface *interface, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ gfp_t mem_flags); ++ + /* used these for multi-interface device registration */ + extern int usb_driver_claim_interface(struct usb_driver *driver, + struct usb_interface *iface, void *priv); diff --git a/usb/usb-ueagle-fix-coding-styles.patch b/usb/usb-ueagle-fix-coding-styles.patch new file mode 100644 index 00000000000000..a2cf57c1548093 --- /dev/null +++ b/usb/usb-ueagle-fix-coding-styles.patch @@ -0,0 +1,745 @@ +From jblanco@neurowork.net Thu Apr 29 13:08:59 2010 +From: "Javier Blanco de Torres (Neurowork)" <jblanco@neurowork.net> +Date: Mon, 12 Apr 2010 09:21:13 +0200 +Subject: USB: ueagle: fix Coding Styles +To: "Greg KH" <gregkh@suse.de> +Message-ID: <92b3d0e7b27bfa13ae50672e2ea5473d.squirrel@neurowork.net> + + +Fixed coding styles in the ueagle usb driver. + +Signed-off-by: Javier Blanco de Torres <jblanco@neurowork.net> +Signed-off-by: Alejandro S�nchez Acosta <asanchez@neurowork.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/atm/ueagle-atm.c | 347 +++++++++++++++++++++++++------------------ + 1 file changed, 207 insertions(+), 140 deletions(-) + +--- a/drivers/usb/atm/ueagle-atm.c ++++ b/drivers/usb/atm/ueagle-atm.c +@@ -94,19 +94,19 @@ + } while (0) + + #define uea_enters(usb_dev) \ +- uea_vdbg(usb_dev, "entering %s\n", __func__) ++ uea_vdbg(usb_dev, "entering %s\n" , __func__) + + #define uea_leaves(usb_dev) \ +- uea_vdbg(usb_dev, "leaving %s\n", __func__) ++ uea_vdbg(usb_dev, "leaving %s\n" , __func__) + +-#define uea_err(usb_dev, format,args...) \ +- dev_err(&(usb_dev)->dev ,"[UEAGLE-ATM] " format , ##args) ++#define uea_err(usb_dev, format, args...) \ ++ dev_err(&(usb_dev)->dev , "[UEAGLE-ATM] " format , ##args) + +-#define uea_warn(usb_dev, format,args...) \ +- dev_warn(&(usb_dev)->dev ,"[Ueagle-atm] " format, ##args) ++#define uea_warn(usb_dev, format, args...) \ ++ dev_warn(&(usb_dev)->dev , "[Ueagle-atm] " format, ##args) + +-#define uea_info(usb_dev, format,args...) \ +- dev_info(&(usb_dev)->dev ,"[ueagle-atm] " format, ##args) ++#define uea_info(usb_dev, format, args...) \ ++ dev_info(&(usb_dev)->dev , "[ueagle-atm] " format, ##args) + + struct intr_pkt; + +@@ -289,7 +289,7 @@ enum { + #define IS_ISDN(x) \ + ((x)->annex & ANNEXB) + +-#define INS_TO_USBDEV(ins) ins->usb_dev ++#define INS_TO_USBDEV(ins) (ins->usb_dev) + + #define GET_STATUS(data) \ + ((data >> 8) & 0xf) +@@ -304,7 +304,7 @@ enum { + * The FW_GET_BYTE() macro is provided only for consistency. + */ + +-#define FW_GET_BYTE(p) *((__u8 *) (p)) ++#define FW_GET_BYTE(p) (*((__u8 *) (p))) + + #define FW_DIR "ueagle-atm/" + #define UEA_FW_NAME_MAX 30 +@@ -315,7 +315,7 @@ enum { + + #define ACK_TIMEOUT msecs_to_jiffies(3000) + +-#define UEA_INTR_IFACE_NO 0 ++#define UEA_INTR_IFACE_NO 0 + #define UEA_US_IFACE_NO 1 + #define UEA_DS_IFACE_NO 2 + +@@ -326,9 +326,9 @@ enum { + #define UEA_INTR_PIPE 0x04 + #define UEA_ISO_DATA_PIPE 0x08 + +-#define UEA_E1_SET_BLOCK 0x0001 ++#define UEA_E1_SET_BLOCK 0x0001 + #define UEA_E4_SET_BLOCK 0x002c +-#define UEA_SET_MODE 0x0003 ++#define UEA_SET_MODE 0x0003 + #define UEA_SET_2183_DATA 0x0004 + #define UEA_SET_TIMEOUT 0x0011 + +@@ -366,7 +366,7 @@ struct l1_code { + u8 string_header[E4_L1_STRING_HEADER]; + u8 page_number_to_block_index[E4_MAX_PAGE_NUMBER]; + struct block_index page_header[E4_NO_SWAPPAGE_HEADERS]; +- u8 code [0]; ++ u8 code[0]; + } __attribute__ ((packed)); + + /* structures describing a block within a DSP page */ +@@ -428,7 +428,8 @@ struct block_info_e4 { + #define E4_MODEMREADY 0x1 + + #define E1_MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf)) +-#define E4_MAKEFUNCTION(t, st, s) (((t) & 0xf) << 8 | ((st) & 0xf) << 4 | ((s) & 0xf)) ++#define E4_MAKEFUNCTION(t, st, s) (((t) & 0xf) << 8 | \ ++ ((st) & 0xf) << 4 | ((s) & 0xf)) + + #define E1_MAKESA(a, b, c, d) \ + (((c) & 0xff) << 24 | \ +@@ -473,7 +474,7 @@ struct cmv_e4 { + __be16 wFunction; + __be16 wOffset; + __be16 wAddress; +- __be32 dwData [6]; ++ __be32 dwData[6]; + } __attribute__ ((packed)); + + /* structures representing swap information */ +@@ -534,11 +535,13 @@ struct intr_pkt { + + static struct usb_driver uea_driver; + static DEFINE_MUTEX(uea_mutex); +-static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III", "Eagle IV"}; ++static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III", ++ "Eagle IV"}; + + static int modem_index; + static unsigned int debug; +-static unsigned int altsetting[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = FASTEST_ISO_INTF}; ++static unsigned int altsetting[NB_MODEM] = { ++ [0 ... (NB_MODEM - 1)] = FASTEST_ISO_INTF}; + static int sync_wait[NB_MODEM]; + static char *cmv_file[NB_MODEM]; + static int annex[NB_MODEM]; +@@ -555,7 +558,7 @@ MODULE_PARM_DESC(cmv_file, + "file name with configuration and management variables"); + module_param_array(annex, uint, NULL, 0644); + MODULE_PARM_DESC(annex, +- "manually set annex a/b (0=auto, 1=annex a, 2=annex b)"); ++ "manually set annex a/b (0=auto, 1=annex a, 2=annex b)"); + + #define uea_wait(sc, cond, timeo) \ + ({ \ +@@ -602,7 +605,8 @@ static int uea_send_modem_cmd(struct usb + return (ret == size) ? 0 : -EIO; + } + +-static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *context) ++static void uea_upload_pre_firmware(const struct firmware *fw_entry, ++ void *context) + { + struct usb_device *usb = context; + const u8 *pfw; +@@ -707,7 +711,8 @@ static int uea_load_firmware(struct usb_ + } + + ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, +- GFP_KERNEL, usb, uea_upload_pre_firmware); ++ GFP_KERNEL, usb, ++ uea_upload_pre_firmware); + if (ret) + uea_err(usb, "firmware %s is not available\n", fw_name); + else +@@ -876,7 +881,7 @@ static int request_dsp(struct uea_softc + if (ret < 0) { + uea_err(INS_TO_USBDEV(sc), + "requesting firmware %s failed with error %d\n", +- dsp_name, ret); ++ dsp_name, ret); + return ret; + } + +@@ -994,14 +999,17 @@ static void __uea_load_page_e4(struct ue + + blockidx = &p->page_header[blockno]; + blocksize = E4_PAGE_BYTES(blockidx->PageSize); +- blockoffset = sc->dsp_firm->data + le32_to_cpu(blockidx->PageOffset); ++ blockoffset = sc->dsp_firm->data + le32_to_cpu( ++ blockidx->PageOffset); + + bi.dwSize = cpu_to_be32(blocksize); + bi.dwAddress = cpu_to_be32(le32_to_cpu(blockidx->PageAddress)); + + uea_dbg(INS_TO_USBDEV(sc), +- "sending block %u for DSP page %u size %u address %x\n", +- blockno, pageno, blocksize, le32_to_cpu(blockidx->PageAddress)); ++ "sending block %u for DSP page " ++ "%u size %u address %x\n", ++ blockno, pageno, blocksize, ++ le32_to_cpu(blockidx->PageAddress)); + + /* send block info through the IDMA pipe */ + if (uea_idma_write(sc, &bi, E4_BLOCK_INFO_SIZE)) +@@ -1042,7 +1050,8 @@ static void uea_load_page_e4(struct work + + p = (struct l1_code *) sc->dsp_firm->data; + if (pageno >= le16_to_cpu(p->page_header[0].PageNumber)) { +- uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n", pageno); ++ uea_err(INS_TO_USBDEV(sc), "invalid DSP " ++ "page %u requested\n", pageno); + return; + } + +@@ -1059,7 +1068,7 @@ static void uea_load_page_e4(struct work + __uea_load_page_e4(sc, i, 1); + } + +- uea_dbg(INS_TO_USBDEV(sc),"sending start bi\n"); ++ uea_dbg(INS_TO_USBDEV(sc) , "sending start bi\n"); + + bi.wHdr = cpu_to_be16(UEA_BIHDR); + bi.bBootPage = 0; +@@ -1139,8 +1148,10 @@ static int uea_cmv_e1(struct uea_softc * + uea_enters(INS_TO_USBDEV(sc)); + uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, " + "offset : 0x%04x, data : 0x%08x\n", +- E1_FUNCTION_TYPE(function), E1_FUNCTION_SUBTYPE(function), +- E1_GETSA1(address), E1_GETSA2(address), E1_GETSA3(address), ++ E1_FUNCTION_TYPE(function), ++ E1_FUNCTION_SUBTYPE(function), ++ E1_GETSA1(address), E1_GETSA2(address), ++ E1_GETSA3(address), + E1_GETSA4(address), offset, data); + + /* we send a request, but we expect a reply */ +@@ -1157,7 +1168,8 @@ static int uea_cmv_e1(struct uea_softc * + cmv.wOffsetAddress = cpu_to_le16(offset); + put_unaligned_le32(data >> 16 | data << 16, &cmv.dwData); + +- ret = uea_request(sc, UEA_E1_SET_BLOCK, UEA_MPTX_START, sizeof(cmv), &cmv); ++ ret = uea_request(sc, UEA_E1_SET_BLOCK, UEA_MPTX_START, ++ sizeof(cmv), &cmv); + if (ret < 0) + return ret; + ret = wait_cmv_ack(sc); +@@ -1191,7 +1203,8 @@ static int uea_cmv_e4(struct uea_softc * + cmv.wOffset = cpu_to_be16(offset); + cmv.dwData[0] = cpu_to_be32(data); + +- ret = uea_request(sc, UEA_E4_SET_BLOCK, UEA_MPTX_START, sizeof(cmv), &cmv); ++ ret = uea_request(sc, UEA_E4_SET_BLOCK, UEA_MPTX_START, ++ sizeof(cmv), &cmv); + if (ret < 0) + return ret; + ret = wait_cmv_ack(sc); +@@ -1208,7 +1221,7 @@ static inline int uea_read_cmv_e1(struct + uea_err(INS_TO_USBDEV(sc), + "reading cmv failed with error %d\n", ret); + else +- *data = sc->data; ++ *data = sc->data; + + return ret; + } +@@ -1216,13 +1229,14 @@ static inline int uea_read_cmv_e1(struct + static inline int uea_read_cmv_e4(struct uea_softc *sc, + u8 size, u16 group, u16 address, u16 offset, u32 *data) + { +- int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, E4_REQUESTREAD, size), ++ int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, ++ E4_REQUESTREAD, size), + group, address, offset, 0); + if (ret < 0) + uea_err(INS_TO_USBDEV(sc), + "reading cmv failed with error %d\n", ret); + else { +- *data = sc->data; ++ *data = sc->data; + /* size is in 16-bit word quantities */ + if (size > 2) + *(data + 1) = sc->data1; +@@ -1245,7 +1259,8 @@ static inline int uea_write_cmv_e1(struc + static inline int uea_write_cmv_e4(struct uea_softc *sc, + u8 size, u16 group, u16 address, u16 offset, u32 data) + { +- int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, E4_REQUESTWRITE, size), ++ int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, ++ E4_REQUESTWRITE, size), + group, address, offset, data); + if (ret < 0) + uea_err(INS_TO_USBDEV(sc), +@@ -1442,27 +1457,29 @@ static int uea_stat_e4(struct uea_softc + return ret; + + switch (sc->stats.phy.state) { +- case 0x0: /* not yet synchronized */ +- case 0x1: +- case 0x3: +- case 0x4: +- uea_dbg(INS_TO_USBDEV(sc), "modem not yet synchronized\n"); +- return 0; +- case 0x5: /* initialization */ +- case 0x6: +- case 0x9: +- case 0xa: +- uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n"); +- return 0; +- case 0x2: /* fail ... */ +- uea_info(INS_TO_USBDEV(sc), "modem synchronization failed" +- " (may be try other cmv/dsp)\n"); +- return -EAGAIN; +- case 0x7: /* operational */ +- break; +- default: +- uea_warn(INS_TO_USBDEV(sc), "unknown state: %x\n", sc->stats.phy.state); +- return 0; ++ case 0x0: /* not yet synchronized */ ++ case 0x1: ++ case 0x3: ++ case 0x4: ++ uea_dbg(INS_TO_USBDEV(sc), "modem not yet " ++ "synchronized\n"); ++ return 0; ++ case 0x5: /* initialization */ ++ case 0x6: ++ case 0x9: ++ case 0xa: ++ uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n"); ++ return 0; ++ case 0x2: /* fail ... */ ++ uea_info(INS_TO_USBDEV(sc), "modem synchronization " ++ "failed (may be try other cmv/dsp)\n"); ++ return -EAGAIN; ++ case 0x7: /* operational */ ++ break; ++ default: ++ uea_warn(INS_TO_USBDEV(sc), "unknown state: %x\n", ++ sc->stats.phy.state); ++ return 0; + } + + if (data != 7) { +@@ -1502,9 +1519,9 @@ static int uea_stat_e4(struct uea_softc + if (sc->stats.phy.flags) { + uea_dbg(INS_TO_USBDEV(sc), "Stat flag = 0x%x\n", + sc->stats.phy.flags); +- if (sc->stats.phy.flags & 1) //delineation LOSS ++ if (sc->stats.phy.flags & 1) /* delineation LOSS */ + return -EAGAIN; +- if (sc->stats.phy.flags & 0x4000) //Reset Flag ++ if (sc->stats.phy.flags & 0x4000) /* Reset Flag */ + return -EAGAIN; + return 0; + } +@@ -1618,7 +1635,8 @@ static int request_cmvs(struct uea_softc + if (ret < 0) { + /* if caller can handle old version, try to provide it */ + if (*ver == 1) { +- uea_warn(INS_TO_USBDEV(sc), "requesting firmware %s failed, " ++ uea_warn(INS_TO_USBDEV(sc), "requesting " ++ "firmware %s failed, " + "try to get older cmvs\n", cmv_name); + return request_cmvs_old(sc, cmvs, fw); + } +@@ -1632,8 +1650,8 @@ static int request_cmvs(struct uea_softc + data = (u8 *) (*fw)->data; + if (size < 4 || strncmp(data, "cmv2", 4) != 0) { + if (*ver == 1) { +- uea_warn(INS_TO_USBDEV(sc), "firmware %s is corrupted, " +- "try to get older cmvs\n", cmv_name); ++ uea_warn(INS_TO_USBDEV(sc), "firmware %s is corrupted," ++ " try to get older cmvs\n", cmv_name); + release_firmware(*fw); + return request_cmvs_old(sc, cmvs, fw); + } +@@ -1670,7 +1688,7 @@ static int uea_send_cmvs_e1(struct uea_s + int i, ret, len; + void *cmvs_ptr; + const struct firmware *cmvs_fw; +- int ver = 1; // we can handle v1 cmv firmware version; ++ int ver = 1; /* we can handle v1 cmv firmware version; */ + + /* Enter in R-IDLE (cmv) until instructed otherwise */ + ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 1); +@@ -1685,7 +1703,7 @@ static int uea_send_cmvs_e1(struct uea_s + sc->stats.phy.firmid); + + /* get options */ +- ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver); ++ ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver); + if (ret < 0) + return ret; + +@@ -1697,9 +1715,10 @@ static int uea_send_cmvs_e1(struct uea_s + "please update your firmware\n"); + + for (i = 0; i < len; i++) { +- ret = uea_write_cmv_e1(sc, get_unaligned_le32(&cmvs_v1[i].address), +- get_unaligned_le16(&cmvs_v1[i].offset), +- get_unaligned_le32(&cmvs_v1[i].data)); ++ ret = uea_write_cmv_e1(sc, ++ get_unaligned_le32(&cmvs_v1[i].address), ++ get_unaligned_le16(&cmvs_v1[i].offset), ++ get_unaligned_le32(&cmvs_v1[i].data)); + if (ret < 0) + goto out; + } +@@ -1707,9 +1726,10 @@ static int uea_send_cmvs_e1(struct uea_s + struct uea_cmvs_v2 *cmvs_v2 = cmvs_ptr; + + for (i = 0; i < len; i++) { +- ret = uea_write_cmv_e1(sc, get_unaligned_le32(&cmvs_v2[i].address), +- (u16) get_unaligned_le32(&cmvs_v2[i].offset), +- get_unaligned_le32(&cmvs_v2[i].data)); ++ ret = uea_write_cmv_e1(sc, ++ get_unaligned_le32(&cmvs_v2[i].address), ++ (u16) get_unaligned_le32(&cmvs_v2[i].offset), ++ get_unaligned_le32(&cmvs_v2[i].data)); + if (ret < 0) + goto out; + } +@@ -1722,7 +1742,8 @@ static int uea_send_cmvs_e1(struct uea_s + /* Enter in R-ACT-REQ */ + ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 2); + uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n"); +- uea_info(INS_TO_USBDEV(sc), "modem started, waiting synchronization...\n"); ++ uea_info(INS_TO_USBDEV(sc), "modem started, waiting " ++ "synchronization...\n"); + out: + release_firmware(cmvs_fw); + return ret; +@@ -1733,7 +1754,7 @@ static int uea_send_cmvs_e4(struct uea_s + int i, ret, len; + void *cmvs_ptr; + const struct firmware *cmvs_fw; +- int ver = 2; // we can only handle v2 cmv firmware version; ++ int ver = 2; /* we can only handle v2 cmv firmware version; */ + + /* Enter in R-IDLE (cmv) until instructed otherwise */ + ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 1); +@@ -1750,7 +1771,7 @@ static int uea_send_cmvs_e4(struct uea_s + + + /* get options */ +- ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver); ++ ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver); + if (ret < 0) + return ret; + +@@ -1760,10 +1781,10 @@ static int uea_send_cmvs_e4(struct uea_s + + for (i = 0; i < len; i++) { + ret = uea_write_cmv_e4(sc, 1, +- get_unaligned_le32(&cmvs_v2[i].group), +- get_unaligned_le32(&cmvs_v2[i].address), +- get_unaligned_le32(&cmvs_v2[i].offset), +- get_unaligned_le32(&cmvs_v2[i].data)); ++ get_unaligned_le32(&cmvs_v2[i].group), ++ get_unaligned_le32(&cmvs_v2[i].address), ++ get_unaligned_le32(&cmvs_v2[i].offset), ++ get_unaligned_le32(&cmvs_v2[i].data)); + if (ret < 0) + goto out; + } +@@ -1776,7 +1797,8 @@ static int uea_send_cmvs_e4(struct uea_s + /* Enter in R-ACT-REQ */ + ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 2); + uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n"); +- uea_info(INS_TO_USBDEV(sc), "modem started, waiting synchronization...\n"); ++ uea_info(INS_TO_USBDEV(sc), "modem started, waiting " ++ "synchronization...\n"); + out: + release_firmware(cmvs_fw); + return ret; +@@ -1812,7 +1834,7 @@ static int uea_start_reset(struct uea_so + uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL); + uea_request(sc, UEA_SET_MODE, UEA_BOOT_IDMA, 0, NULL); + +- /* enter reset mode */ ++ /* enter reset mode */ + uea_request(sc, UEA_SET_MODE, UEA_START_RESET, 0, NULL); + + /* original driver use 200ms, but windows driver use 100ms */ +@@ -1824,7 +1846,7 @@ static int uea_start_reset(struct uea_so + uea_request(sc, UEA_SET_MODE, UEA_END_RESET, 0, NULL); + + if (UEA_CHIP_VERSION(sc) != EAGLE_IV) { +- /* clear tx and rx mailboxes */ ++ /* clear tx and rx mailboxes */ + uea_request(sc, UEA_SET_2183_DATA, UEA_MPTX_MAILBOX, 2, &zero); + uea_request(sc, UEA_SET_2183_DATA, UEA_MPRX_MAILBOX, 2, &zero); + uea_request(sc, UEA_SET_2183_DATA, UEA_SWAP_MAILBOX, 2, &zero); +@@ -1835,9 +1857,11 @@ static int uea_start_reset(struct uea_so + return ret; + + if (UEA_CHIP_VERSION(sc) == EAGLE_IV) +- sc->cmv_dsc.e4.function = E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1); ++ sc->cmv_dsc.e4.function = E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, ++ E4_MODEMREADY, 1); + else +- sc->cmv_dsc.e1.function = E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY); ++ sc->cmv_dsc.e1.function = E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, ++ E1_MODEMREADY); + + /* demask interrupt */ + sc->booting = 0; +@@ -1937,7 +1961,8 @@ static int load_XILINX_firmware(struct u + value = 0; + ret = uea_send_modem_cmd(sc->usb_dev, 0xe, 1, &value); + if (ret < 0) +- uea_err(sc->usb_dev, "elsa de-assert failed with error %d\n", ret); ++ uea_err(sc->usb_dev, "elsa de-assert failed with error" ++ " %d\n", ret); + + err1: + release_firmware(fw_entry); +@@ -1966,13 +1991,15 @@ static void uea_dispatch_cmv_e1(struct u + if (UEA_CHIP_VERSION(sc) == ADI930 + && cmv->bFunction == E1_MAKEFUNCTION(2, 2)) { + cmv->wIndex = cpu_to_le16(dsc->idx); +- put_unaligned_le32(dsc->address, &cmv->dwSymbolicAddress); ++ put_unaligned_le32(dsc->address, ++ &cmv->dwSymbolicAddress); + cmv->wOffsetAddress = cpu_to_le16(dsc->offset); + } else + goto bad2; + } + +- if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY)) { ++ if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, ++ E1_MODEMREADY)) { + wake_up_cmv_ack(sc); + uea_leaves(INS_TO_USBDEV(sc)); + return; +@@ -2021,7 +2048,8 @@ static void uea_dispatch_cmv_e4(struct u + if (be16_to_cpu(cmv->wFunction) != dsc->function) + goto bad2; + +- if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1)) { ++ if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, ++ E4_MODEMREADY, 1)) { + wake_up_cmv_ack(sc); + uea_leaves(INS_TO_USBDEV(sc)); + return; +@@ -2048,14 +2076,16 @@ bad2: + return; + } + +-static void uea_schedule_load_page_e1(struct uea_softc *sc, struct intr_pkt *intr) ++static void uea_schedule_load_page_e1(struct uea_softc *sc, ++ struct intr_pkt *intr) + { + sc->pageno = intr->e1_bSwapPageNo; + sc->ovl = intr->e1_bOvl >> 4 | intr->e1_bOvl << 4; + queue_work(sc->work_q, &sc->task); + } + +-static void uea_schedule_load_page_e4(struct uea_softc *sc, struct intr_pkt *intr) ++static void uea_schedule_load_page_e4(struct uea_softc *sc, ++ struct intr_pkt *intr) + { + sc->pageno = intr->e4_bSwapPageNo; + queue_work(sc->work_q, &sc->task); +@@ -2263,8 +2293,8 @@ out: + + static DEVICE_ATTR(stat_status, S_IWUGO | S_IRUGO, read_status, reboot); + +-static ssize_t read_human_status(struct device *dev, struct device_attribute *attr, +- char *buf) ++static ssize_t read_human_status(struct device *dev, ++ struct device_attribute *attr, char *buf) + { + int ret = -ENODEV; + int modem_state; +@@ -2289,7 +2319,7 @@ static ssize_t read_human_status(struct + case 0xa: + modem_state = 1; + break; +- case 0x7: /* operational */ ++ case 0x7: /* operational */ + modem_state = 2; + break; + case 0x2: /* fail ... */ +@@ -2324,7 +2354,8 @@ out: + return ret; + } + +-static DEVICE_ATTR(stat_human_status, S_IWUGO | S_IRUGO, read_human_status, NULL); ++static DEVICE_ATTR(stat_human_status, S_IWUGO | S_IRUGO, ++ read_human_status, NULL); + + static ssize_t read_delin(struct device *dev, struct device_attribute *attr, + char *buf) +@@ -2358,25 +2389,25 @@ out: + + static DEVICE_ATTR(stat_delin, S_IWUGO | S_IRUGO, read_delin, NULL); + +-#define UEA_ATTR(name, reset) \ ++#define UEA_ATTR(name, reset) \ + \ +-static ssize_t read_##name(struct device *dev, \ ++static ssize_t read_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +-{ \ +- int ret = -ENODEV; \ +- struct uea_softc *sc; \ +- \ +- mutex_lock(&uea_mutex); \ ++{ \ ++ int ret = -ENODEV; \ ++ struct uea_softc *sc; \ ++ \ ++ mutex_lock(&uea_mutex); \ + sc = dev_to_uea(dev); \ +- if (!sc) \ +- goto out; \ ++ if (!sc) \ ++ goto out; \ + ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.name); \ + if (reset) \ + sc->stats.phy.name = 0; \ +-out: \ +- mutex_unlock(&uea_mutex); \ +- return ret; \ +-} \ ++out: \ ++ mutex_unlock(&uea_mutex); \ ++ return ret; \ ++} \ + \ + static DEVICE_ATTR(stat_##name, S_IRUGO, read_##name, NULL) + +@@ -2527,12 +2558,14 @@ static int uea_bind(struct usbatm_data * + else if (sc->driver_info & AUTO_ANNEX_B) + sc->annex = ANNEXB; + else +- sc->annex = (le16_to_cpu(sc->usb_dev->descriptor.bcdDevice) & 0x80)?ANNEXB:ANNEXA; ++ sc->annex = (le16_to_cpu ++ (sc->usb_dev->descriptor.bcdDevice) & 0x80) ? ANNEXB : ANNEXA; + + alt = altsetting[sc->modem_index]; + /* ADI930 don't support iso */ + if (UEA_CHIP_VERSION(id) != ADI930 && alt > 0) { +- if (alt <= 8 && usb_set_interface(usb, UEA_DS_IFACE_NO, alt) == 0) { ++ if (alt <= 8 && ++ usb_set_interface(usb, UEA_DS_IFACE_NO, alt) == 0) { + uea_dbg(usb, "set alternate %u for 2 interface\n", alt); + uea_info(usb, "using iso mode\n"); + usbatm->flags |= UDSL_USE_ISOC | UDSL_IGNORE_EILSEQ; +@@ -2621,40 +2654,74 @@ static void uea_disconnect(struct usb_in + * List of supported VID/PID + */ + static const struct usb_device_id uea_ids[] = { +- {USB_DEVICE(ANALOG_VID, ADI930_PID_PREFIRM), .driver_info = ADI930 | PREFIRM}, +- {USB_DEVICE(ANALOG_VID, ADI930_PID_PSTFIRM), .driver_info = ADI930 | PSTFIRM}, +- {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM}, +- {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM}, +- {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM}, +- {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM}, +- {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM}, +- {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM}, +- {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PREFIRM), .driver_info = EAGLE_III | PREFIRM}, +- {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PSTFIRM), .driver_info = EAGLE_III | PSTFIRM}, +- {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PREFIRM), .driver_info = EAGLE_IV | PREFIRM}, +- {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PSTFIRM), .driver_info = EAGLE_IV | PSTFIRM}, +- {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM}, +- {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A}, +- {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM}, +- {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B}, +- {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM}, +- {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_A}, +- {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM}, +- {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_B}, +- {USB_DEVICE(ELSA_VID, ELSA_PID_PREFIRM), .driver_info = ADI930 | PREFIRM}, +- {USB_DEVICE(ELSA_VID, ELSA_PID_PSTFIRM), .driver_info = ADI930 | PSTFIRM}, +- {USB_DEVICE(ELSA_VID, ELSA_PID_A_PREFIRM), .driver_info = ADI930 | PREFIRM}, +- {USB_DEVICE(ELSA_VID, ELSA_PID_A_PSTFIRM), .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_A}, +- {USB_DEVICE(ELSA_VID, ELSA_PID_B_PREFIRM), .driver_info = ADI930 | PREFIRM}, +- {USB_DEVICE(ELSA_VID, ELSA_PID_B_PSTFIRM), .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_B}, +- {USB_DEVICE(USR_VID, MILLER_A_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM}, +- {USB_DEVICE(USR_VID, MILLER_A_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A}, +- {USB_DEVICE(USR_VID, MILLER_B_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM}, +- {USB_DEVICE(USR_VID, MILLER_B_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B}, +- {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM}, +- {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A}, +- {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM}, +- {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B}, ++ {USB_DEVICE(ANALOG_VID, ADI930_PID_PREFIRM), ++ .driver_info = ADI930 | PREFIRM}, ++ {USB_DEVICE(ANALOG_VID, ADI930_PID_PSTFIRM), ++ .driver_info = ADI930 | PSTFIRM}, ++ {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PREFIRM), ++ .driver_info = EAGLE_I | PREFIRM}, ++ {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PSTFIRM), ++ .driver_info = EAGLE_I | PSTFIRM}, ++ {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PREFIRM), ++ .driver_info = EAGLE_II | PREFIRM}, ++ {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PSTFIRM), ++ .driver_info = EAGLE_II | PSTFIRM}, ++ {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PREFIRM), ++ .driver_info = EAGLE_II | PREFIRM}, ++ {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PSTFIRM), ++ .driver_info = EAGLE_II | PSTFIRM}, ++ {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PREFIRM), ++ .driver_info = EAGLE_III | PREFIRM}, ++ {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PSTFIRM), ++ .driver_info = EAGLE_III | PSTFIRM}, ++ {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PREFIRM), ++ .driver_info = EAGLE_IV | PREFIRM}, ++ {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PSTFIRM), ++ .driver_info = EAGLE_IV | PSTFIRM}, ++ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PREFIRM), ++ .driver_info = EAGLE_I | PREFIRM}, ++ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PSTFIRM), ++ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A}, ++ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PREFIRM), ++ .driver_info = EAGLE_I | PREFIRM}, ++ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PSTFIRM), ++ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B}, ++ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PREFIRM), ++ .driver_info = EAGLE_II | PREFIRM}, ++ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PSTFIRM), ++ .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_A}, ++ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PREFIRM), ++ .driver_info = EAGLE_II | PREFIRM}, ++ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PSTFIRM), ++ .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_B}, ++ {USB_DEVICE(ELSA_VID, ELSA_PID_PREFIRM), ++ .driver_info = ADI930 | PREFIRM}, ++ {USB_DEVICE(ELSA_VID, ELSA_PID_PSTFIRM), ++ .driver_info = ADI930 | PSTFIRM}, ++ {USB_DEVICE(ELSA_VID, ELSA_PID_A_PREFIRM), ++ .driver_info = ADI930 | PREFIRM}, ++ {USB_DEVICE(ELSA_VID, ELSA_PID_A_PSTFIRM), ++ .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_A}, ++ {USB_DEVICE(ELSA_VID, ELSA_PID_B_PREFIRM), ++ .driver_info = ADI930 | PREFIRM}, ++ {USB_DEVICE(ELSA_VID, ELSA_PID_B_PSTFIRM), ++ .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_B}, ++ {USB_DEVICE(USR_VID, MILLER_A_PID_PREFIRM), ++ .driver_info = EAGLE_I | PREFIRM}, ++ {USB_DEVICE(USR_VID, MILLER_A_PID_PSTFIRM), ++ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A}, ++ {USB_DEVICE(USR_VID, MILLER_B_PID_PREFIRM), ++ .driver_info = EAGLE_I | PREFIRM}, ++ {USB_DEVICE(USR_VID, MILLER_B_PID_PSTFIRM), ++ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B}, ++ {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PREFIRM), ++ .driver_info = EAGLE_I | PREFIRM}, ++ {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PSTFIRM), ++ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A}, ++ {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PREFIRM), ++ .driver_info = EAGLE_I | PREFIRM}, ++ {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PSTFIRM), ++ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B}, + {} + }; + diff --git a/usb/usb-use-pm-core-routines-to-enable-disable-autosuspend.patch b/usb/usb-use-pm-core-routines-to-enable-disable-autosuspend.patch new file mode 100644 index 00000000000000..13593136a77ce6 --- /dev/null +++ b/usb/usb-use-pm-core-routines-to-enable-disable-autosuspend.patch @@ -0,0 +1,138 @@ +From stern@rowland.harvard.edu Thu Apr 29 12:51:52 2010 +From: Alan Stern <stern@rowland.harvard.edu> +Date: Fri, 2 Apr 2010 13:22:09 -0400 (EDT) +Subject: USB: use PM core routines to enable/disable autosuspend +To: Greg KH <greg@kroah.com> +Message-ID: <Pine.LNX.4.44L0.1004021312150.1324-100000@iolanthe.rowland.org> + + +This patch (as1366) replaces the private routines +usb_enable_autosuspend() and usb_disable_autosuspend() with calls to +the standard pm_runtime_allow() and pm_runtime_forbid() functions in +the runtime PM framework. They do the same thing. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/core/driver.c | 21 +++++---------------- + drivers/usb/core/sysfs.c | 10 +++++----- + include/linux/usb.h | 6 ++---- + 3 files changed, 12 insertions(+), 25 deletions(-) + +--- a/drivers/usb/core/driver.c ++++ b/drivers/usb/core/driver.c +@@ -1355,13 +1355,9 @@ int usb_resume(struct device *dev, pm_me + * + * The caller must hold @udev's device lock. + */ +-int usb_enable_autosuspend(struct usb_device *udev) ++void usb_enable_autosuspend(struct usb_device *udev) + { +- if (udev->autosuspend_disabled) { +- udev->autosuspend_disabled = 0; +- usb_autosuspend_device(udev); +- } +- return 0; ++ pm_runtime_allow(&udev->dev); + } + EXPORT_SYMBOL_GPL(usb_enable_autosuspend); + +@@ -1374,16 +1370,9 @@ EXPORT_SYMBOL_GPL(usb_enable_autosuspend + * + * The caller must hold @udev's device lock. + */ +-int usb_disable_autosuspend(struct usb_device *udev) ++void usb_disable_autosuspend(struct usb_device *udev) + { +- int rc = 0; +- +- if (!udev->autosuspend_disabled) { +- rc = usb_autoresume_device(udev); +- if (rc == 0) +- udev->autosuspend_disabled = 1; +- } +- return rc; ++ pm_runtime_forbid(&udev->dev); + } + EXPORT_SYMBOL_GPL(usb_disable_autosuspend); + +@@ -1527,7 +1516,7 @@ void usb_autopm_put_interface_async(stru + atomic_dec(&intf->pm_usage_cnt); + pm_runtime_put_noidle(&intf->dev); + +- if (!udev->autosuspend_disabled) { ++ if (udev->dev.power.runtime_auto) { + /* Optimization: Don't schedule a delayed autosuspend if + * the timer is already running and the expiration time + * wouldn't change. +--- a/drivers/usb/core/sysfs.c ++++ b/drivers/usb/core/sysfs.c +@@ -389,7 +389,7 @@ show_level(struct device *dev, struct de + struct usb_device *udev = to_usb_device(dev); + const char *p = auto_string; + +- if (udev->state != USB_STATE_SUSPENDED && udev->autosuspend_disabled) ++ if (udev->state != USB_STATE_SUSPENDED && !udev->dev.power.runtime_auto) + p = on_string; + return sprintf(buf, "%s\n", p); + } +@@ -401,7 +401,7 @@ set_level(struct device *dev, struct dev + struct usb_device *udev = to_usb_device(dev); + int len = count; + char *cp; +- int rc; ++ int rc = count; + + cp = memchr(buf, '\n', count); + if (cp) +@@ -411,17 +411,17 @@ set_level(struct device *dev, struct dev + + if (len == sizeof on_string - 1 && + strncmp(buf, on_string, len) == 0) +- rc = usb_disable_autosuspend(udev); ++ usb_disable_autosuspend(udev); + + else if (len == sizeof auto_string - 1 && + strncmp(buf, auto_string, len) == 0) +- rc = usb_enable_autosuspend(udev); ++ usb_enable_autosuspend(udev); + + else + rc = -EINVAL; + + usb_unlock_device(udev); +- return (rc < 0 ? rc : count); ++ return rc; + } + + static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); +--- a/include/linux/usb.h ++++ b/include/linux/usb.h +@@ -425,7 +425,6 @@ struct usb_tt; + * @connect_time: time device was first connected + * @do_remote_wakeup: remote wakeup should be enabled + * @reset_resume: needs reset instead of resume +- * @autosuspend_disabled: autosuspend disabled by the user + * @wusb_dev: if this is a Wireless USB device, link to the WUSB + * specific data for the device. + * @slot_id: Slot ID assigned by xHCI +@@ -501,7 +500,6 @@ struct usb_device { + + unsigned do_remote_wakeup:1; + unsigned reset_resume:1; +- unsigned autosuspend_disabled:1; + #endif + struct wusb_dev *wusb_dev; + int slot_id; +@@ -526,8 +524,8 @@ extern struct usb_device *usb_find_devic + + /* USB autosuspend and autoresume */ + #ifdef CONFIG_USB_SUSPEND +-extern int usb_enable_autosuspend(struct usb_device *udev); +-extern int usb_disable_autosuspend(struct usb_device *udev); ++extern void usb_enable_autosuspend(struct usb_device *udev); ++extern void usb_disable_autosuspend(struct usb_device *udev); + + extern int usb_autopm_get_interface(struct usb_interface *intf); + extern void usb_autopm_put_interface(struct usb_interface *intf); diff --git a/usb/usb-xhci-correct-assumptions-about-number-of-rings-per-endpoint.patch b/usb/usb-xhci-correct-assumptions-about-number-of-rings-per-endpoint.patch new file mode 100644 index 00000000000000..4d105b56152851 --- /dev/null +++ b/usb/usb-xhci-correct-assumptions-about-number-of-rings-per-endpoint.patch @@ -0,0 +1,802 @@ +From sarah.a.sharp@linux.intel.com Thu Apr 29 12:59:23 2010 +From: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Date: Fri, 2 Apr 2010 15:34:43 -0700 +Subject: USB: xhci: Correct assumptions about number of rings per endpoint. +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, usb-storage@lists.one-eyed-alien.net, Hrant Dalalyan <Hrant.Dalalyan@synopsys.com>, Alan Stern <stern@rowland.harvard.edu>, Paul Zimmerman <Paul.Zimmerman@synopsys.com>, Ashot Madatyan <Ashot.Madatyan@synopsys.com> +Message-ID: <20100402223443.GA1354@xanatos> +Content-Disposition: inline + + +Much of the xHCI driver code assumes that endpoints only have one ring. +Now an endpoint can have one ring per enabled stream ID, so correct that +assumption. Use functions that translate the stream_id field in the URB +or the DMA address of a TRB into the correct stream ring. + +Correct the polling loop to print out all enabled stream rings. Make the +URB cancellation routine find the correct stream ring if the URB has +stream_id set. Make sure the URB enqueueing routine does the same. Also +correct the code that handles stalled/halted endpoints. + +Check that commands and registers that can take stream IDs handle them +properly. That includes ringing an endpoint doorbell, resetting a +stalled/halted endpoint, and setting a transfer ring dequeue pointer +(since that command can set the dequeue pointer in a stream context or an +endpoint context). + +Correct the transfer event handler to translate a TRB DMA address into the +stream ring it was enqueued to. Make the code to allocate and prepare TD +structures adds the TD to the right td_list for the stream ring. Make +sure the code to give the first TRB in a TD to the hardware manipulates +the correct stream ring. + +When an endpoint stalls, store the stream ID of the stream ring that +stalled in the xhci_virt_ep structure. Use that instead of the stream ID +in the URB, since an URB may be re-used after it is given back after a +non-control endpoint stall. + +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-dbg.c | 24 +++++ + drivers/usb/host/xhci-mem.c | 74 ++++++++++++++++ + drivers/usb/host/xhci-ring.c | 192 ++++++++++++++++++++++++++++++++----------- + drivers/usb/host/xhci.c | 19 ++-- + drivers/usb/host/xhci.h | 26 +++++ + 5 files changed, 280 insertions(+), 55 deletions(-) + +--- a/drivers/usb/host/xhci-dbg.c ++++ b/drivers/usb/host/xhci-dbg.c +@@ -364,6 +364,30 @@ void xhci_debug_ring(struct xhci_hcd *xh + xhci_debug_segment(xhci, seg); + } + ++void xhci_dbg_ep_rings(struct xhci_hcd *xhci, ++ unsigned int slot_id, unsigned int ep_index, ++ struct xhci_virt_ep *ep) ++{ ++ int i; ++ struct xhci_ring *ring; ++ ++ if (ep->ep_state & EP_HAS_STREAMS) { ++ for (i = 1; i < ep->stream_info->num_streams; i++) { ++ ring = ep->stream_info->stream_rings[i]; ++ xhci_dbg(xhci, "Dev %d endpoint %d stream ID %d:\n", ++ slot_id, ep_index, i); ++ xhci_debug_segment(xhci, ring->deq_seg); ++ } ++ } else { ++ ring = ep->ring; ++ if (!ring) ++ return; ++ xhci_dbg(xhci, "Dev %d endpoint ring %d:\n", ++ slot_id, ep_index); ++ xhci_debug_segment(xhci, ring->deq_seg); ++ } ++} ++ + void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) + { + u32 addr = (u32) erst->erst_dma_addr; +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -353,8 +353,19 @@ struct xhci_stream_ctx *xhci_alloc_strea + mem_flags, dma); + } + ++struct xhci_ring *xhci_dma_to_transfer_ring( ++ struct xhci_virt_ep *ep, ++ u64 address) ++{ ++ if (ep->ep_state & EP_HAS_STREAMS) ++ return radix_tree_lookup(&ep->stream_info->trb_address_map, ++ address >> SEGMENT_SHIFT); ++ return ep->ring; ++} ++ ++/* Only use this when you know stream_info is valid */ + #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING +-struct xhci_ring *dma_to_stream_ring( ++static struct xhci_ring *dma_to_stream_ring( + struct xhci_stream_info *stream_info, + u64 address) + { +@@ -363,6 +374,66 @@ struct xhci_ring *dma_to_stream_ring( + } + #endif /* CONFIG_USB_XHCI_HCD_DEBUGGING */ + ++struct xhci_ring *xhci_stream_id_to_ring( ++ struct xhci_virt_device *dev, ++ unsigned int ep_index, ++ unsigned int stream_id) ++{ ++ struct xhci_virt_ep *ep = &dev->eps[ep_index]; ++ ++ if (stream_id == 0) ++ return ep->ring; ++ if (!ep->stream_info) ++ return NULL; ++ ++ if (stream_id > ep->stream_info->num_streams) ++ return NULL; ++ return ep->stream_info->stream_rings[stream_id]; ++} ++ ++struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci, ++ unsigned int slot_id, unsigned int ep_index, ++ unsigned int stream_id) ++{ ++ struct xhci_virt_ep *ep; ++ ++ ep = &xhci->devs[slot_id]->eps[ep_index]; ++ /* Common case: no streams */ ++ if (!(ep->ep_state & EP_HAS_STREAMS)) ++ return ep->ring; ++ ++ if (stream_id == 0) { ++ xhci_warn(xhci, ++ "WARN: Slot ID %u, ep index %u has streams, " ++ "but URB has no stream ID.\n", ++ slot_id, ep_index); ++ return NULL; ++ } ++ ++ if (stream_id < ep->stream_info->num_streams) ++ return ep->stream_info->stream_rings[stream_id]; ++ ++ xhci_warn(xhci, ++ "WARN: Slot ID %u, ep index %u has " ++ "stream IDs 1 to %u allocated, " ++ "but stream ID %u is requested.\n", ++ slot_id, ep_index, ++ ep->stream_info->num_streams - 1, ++ stream_id); ++ return NULL; ++} ++ ++/* Get the right ring for the given URB. ++ * If the endpoint supports streams, boundary check the URB's stream ID. ++ * If the endpoint doesn't support streams, return the singular endpoint ring. ++ */ ++struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci, ++ struct urb *urb) ++{ ++ return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id, ++ xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id); ++} ++ + #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING + static int xhci_test_radix_tree(struct xhci_hcd *xhci, + unsigned int num_streams, +@@ -515,6 +586,7 @@ struct xhci_stream_info *xhci_alloc_stre + cur_ring = stream_info->stream_rings[cur_stream]; + if (!cur_ring) + goto cleanup_rings; ++ cur_ring->stream_id = cur_stream; + /* Set deq ptr, cycle bit, and stream context type */ + addr = cur_ring->first_seg->dma | + SCT_FOR_CTX(SCT_PRI_TR) | +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -295,7 +295,8 @@ void xhci_ring_cmd_db(struct xhci_hcd *x + + static void ring_ep_doorbell(struct xhci_hcd *xhci, + unsigned int slot_id, +- unsigned int ep_index) ++ unsigned int ep_index, ++ unsigned int stream_id) + { + struct xhci_virt_ep *ep; + unsigned int ep_state; +@@ -314,7 +315,8 @@ static void ring_ep_doorbell(struct xhci + if (!(ep_state & EP_HALT_PENDING) && !(ep_state & SET_DEQ_PENDING) + && !(ep_state & EP_HALTED)) { + field = xhci_readl(xhci, db_addr) & DB_MASK; +- xhci_writel(xhci, field | EPI_TO_DB(ep_index), db_addr); ++ field |= EPI_TO_DB(ep_index) | STREAM_ID_TO_DB(stream_id); ++ xhci_writel(xhci, field, db_addr); + /* Flush PCI posted writes - FIXME Matthew Wilcox says this + * isn't time-critical and we shouldn't make the CPU wait for + * the flush. +@@ -323,6 +325,31 @@ static void ring_ep_doorbell(struct xhci + } + } + ++/* Ring the doorbell for any rings with pending URBs */ ++static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, ++ unsigned int slot_id, ++ unsigned int ep_index) ++{ ++ unsigned int stream_id; ++ struct xhci_virt_ep *ep; ++ ++ ep = &xhci->devs[slot_id]->eps[ep_index]; ++ ++ /* A ring has pending URBs if its TD list is not empty */ ++ if (!(ep->ep_state & EP_HAS_STREAMS)) { ++ if (!(list_empty(&ep->ring->td_list))) ++ ring_ep_doorbell(xhci, slot_id, ep_index, 0); ++ return; ++ } ++ ++ for (stream_id = 1; stream_id < ep->stream_info->num_streams; ++ stream_id++) { ++ struct xhci_stream_info *stream_info = ep->stream_info; ++ if (!list_empty(&stream_info->stream_rings[stream_id]->td_list)) ++ ring_ep_doorbell(xhci, slot_id, ep_index, stream_id); ++ } ++} ++ + /* + * Find the segment that trb is in. Start searching in start_seg. + * If we must move past a segment that has a link TRB with a toggle cycle state +@@ -365,14 +392,23 @@ static struct xhci_segment *find_trb_seg + */ + void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, + unsigned int slot_id, unsigned int ep_index, +- struct xhci_td *cur_td, struct xhci_dequeue_state *state) ++ unsigned int stream_id, struct xhci_td *cur_td, ++ struct xhci_dequeue_state *state) + { + struct xhci_virt_device *dev = xhci->devs[slot_id]; +- struct xhci_ring *ep_ring = dev->eps[ep_index].ring; ++ struct xhci_ring *ep_ring; + struct xhci_generic_trb *trb; + struct xhci_ep_ctx *ep_ctx; + dma_addr_t addr; + ++ ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id, ++ ep_index, stream_id); ++ if (!ep_ring) { ++ xhci_warn(xhci, "WARN can't find new dequeue state " ++ "for invalid stream ID %u.\n", ++ stream_id); ++ return; ++ } + state->new_cycle_state = 0; + xhci_dbg(xhci, "Finding segment containing stopped TRB.\n"); + state->new_deq_seg = find_trb_seg(cur_td->start_seg, +@@ -452,11 +488,13 @@ static void td_to_noop(struct xhci_hcd * + } + + static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, +- unsigned int ep_index, struct xhci_segment *deq_seg, ++ unsigned int ep_index, unsigned int stream_id, ++ struct xhci_segment *deq_seg, + union xhci_trb *deq_ptr, u32 cycle_state); + + void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, + unsigned int slot_id, unsigned int ep_index, ++ unsigned int stream_id, + struct xhci_dequeue_state *deq_state) + { + struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; +@@ -468,7 +506,7 @@ void xhci_queue_new_dequeue_state(struct + deq_state->new_deq_ptr, + (unsigned long long)xhci_trb_virt_to_dma(deq_state->new_deq_seg, deq_state->new_deq_ptr), + deq_state->new_cycle_state); +- queue_set_tr_deq(xhci, slot_id, ep_index, ++ queue_set_tr_deq(xhci, slot_id, ep_index, stream_id, + deq_state->new_deq_seg, + deq_state->new_deq_ptr, + (u32) deq_state->new_cycle_state); +@@ -536,11 +574,10 @@ static void handle_stopped_endpoint(stru + slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); + ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); + ep = &xhci->devs[slot_id]->eps[ep_index]; +- ep_ring = ep->ring; + + if (list_empty(&ep->cancelled_td_list)) { + xhci_stop_watchdog_timer_in_irq(xhci, ep); +- ring_ep_doorbell(xhci, slot_id, ep_index); ++ ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + return; + } + +@@ -554,15 +591,36 @@ static void handle_stopped_endpoint(stru + xhci_dbg(xhci, "Cancelling TD starting at %p, 0x%llx (dma).\n", + cur_td->first_trb, + (unsigned long long)xhci_trb_virt_to_dma(cur_td->start_seg, cur_td->first_trb)); ++ ep_ring = xhci_urb_to_transfer_ring(xhci, cur_td->urb); ++ if (!ep_ring) { ++ /* This shouldn't happen unless a driver is mucking ++ * with the stream ID after submission. This will ++ * leave the TD on the hardware ring, and the hardware ++ * will try to execute it, and may access a buffer ++ * that has already been freed. In the best case, the ++ * hardware will execute it, and the event handler will ++ * ignore the completion event for that TD, since it was ++ * removed from the td_list for that endpoint. In ++ * short, don't muck with the stream ID after ++ * submission. ++ */ ++ xhci_warn(xhci, "WARN Cancelled URB %p " ++ "has invalid stream ID %u.\n", ++ cur_td->urb, ++ cur_td->urb->stream_id); ++ goto remove_finished_td; ++ } + /* + * If we stopped on the TD we need to cancel, then we have to + * move the xHC endpoint ring dequeue pointer past this TD. + */ + if (cur_td == ep->stopped_td) +- xhci_find_new_dequeue_state(xhci, slot_id, ep_index, cur_td, +- &deq_state); ++ xhci_find_new_dequeue_state(xhci, slot_id, ep_index, ++ cur_td->urb->stream_id, ++ cur_td, &deq_state); + else + td_to_noop(xhci, ep_ring, cur_td); ++remove_finished_td: + /* + * The event handler won't see a completion for this TD anymore, + * so remove it from the endpoint ring's TD list. Keep it in +@@ -576,11 +634,13 @@ static void handle_stopped_endpoint(stru + /* If necessary, queue a Set Transfer Ring Dequeue Pointer command */ + if (deq_state.new_deq_ptr && deq_state.new_deq_seg) { + xhci_queue_new_dequeue_state(xhci, +- slot_id, ep_index, &deq_state); ++ slot_id, ep_index, ++ ep->stopped_td->urb->stream_id, ++ &deq_state); + xhci_ring_cmd_db(xhci); + } else { +- /* Otherwise just ring the doorbell to restart the ring */ +- ring_ep_doorbell(xhci, slot_id, ep_index); ++ /* Otherwise ring the doorbell(s) to restart queued transfers */ ++ ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + } + + /* +@@ -738,6 +798,7 @@ static void handle_set_deq_completion(st + { + unsigned int slot_id; + unsigned int ep_index; ++ unsigned int stream_id; + struct xhci_ring *ep_ring; + struct xhci_virt_device *dev; + struct xhci_ep_ctx *ep_ctx; +@@ -745,8 +806,19 @@ static void handle_set_deq_completion(st + + slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); + ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); ++ stream_id = TRB_TO_STREAM_ID(trb->generic.field[2]); + dev = xhci->devs[slot_id]; +- ep_ring = dev->eps[ep_index].ring; ++ ++ ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id); ++ if (!ep_ring) { ++ xhci_warn(xhci, "WARN Set TR deq ptr command for " ++ "freed stream ID %u\n", ++ stream_id); ++ /* XXX: Harmless??? */ ++ dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING; ++ return; ++ } ++ + ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); + slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx); + +@@ -791,7 +863,8 @@ static void handle_set_deq_completion(st + } + + dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING; +- ring_ep_doorbell(xhci, slot_id, ep_index); ++ /* Restart any rings with pending URBs */ ++ ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + } + + static void handle_reset_ep_completion(struct xhci_hcd *xhci, +@@ -800,11 +873,9 @@ static void handle_reset_ep_completion(s + { + int slot_id; + unsigned int ep_index; +- struct xhci_ring *ep_ring; + + slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); + ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); +- ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; + /* This command will only fail if the endpoint wasn't halted, + * but we don't care. + */ +@@ -822,9 +893,9 @@ static void handle_reset_ep_completion(s + false); + xhci_ring_cmd_db(xhci); + } else { +- /* Clear our internal halted state and restart the ring */ ++ /* Clear our internal halted state and restart the ring(s) */ + xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED; +- ring_ep_doorbell(xhci, slot_id, ep_index); ++ ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + } + } + +@@ -910,8 +981,10 @@ static void handle_cmd_completion(struct + /* Input ctx add_flags are the endpoint index plus one */ + ep_index = xhci_last_valid_endpoint(ctrl_ctx->add_flags) - 1; + /* A usb_set_interface() call directly after clearing a halted +- * condition may race on this quirky hardware. +- * Not worth worrying about, since this is prototype hardware. ++ * condition may race on this quirky hardware. Not worth ++ * worrying about, since this is prototype hardware. Not sure ++ * if this will work for streams, but streams support was ++ * untested on this prototype. + */ + if (xhci->quirks & XHCI_RESET_EP_QUIRK && + ep_index != (unsigned int) -1 && +@@ -924,10 +997,10 @@ static void handle_cmd_completion(struct + xhci_dbg(xhci, "Completed config ep cmd - " + "last ep index = %d, state = %d\n", + ep_index, ep_state); +- /* Clear our internal halted state and restart ring */ ++ /* Clear internal halted state and restart ring(s) */ + xhci->devs[slot_id]->eps[ep_index].ep_state &= + ~EP_HALTED; +- ring_ep_doorbell(xhci, slot_id, ep_index); ++ ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + break; + } + bandwidth_change: +@@ -1060,12 +1133,14 @@ struct xhci_segment *trb_in_td(struct xh + + static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci, + unsigned int slot_id, unsigned int ep_index, ++ unsigned int stream_id, + struct xhci_td *td, union xhci_trb *event_trb) + { + struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; + ep->ep_state |= EP_HALTED; + ep->stopped_td = td; + ep->stopped_trb = event_trb; ++ ep->stopped_stream = stream_id; + xhci_queue_reset_ep(xhci, slot_id, ep_index); + xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index); + xhci_ring_cmd_db(xhci); +@@ -1145,10 +1220,11 @@ static int handle_tx_event(struct xhci_h + ep_index = TRB_TO_EP_ID(event->flags) - 1; + xhci_dbg(xhci, "%s - ep index = %d\n", __func__, ep_index); + ep = &xdev->eps[ep_index]; +- ep_ring = ep->ring; ++ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); + if (!ep_ring || (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) { +- xhci_err(xhci, "ERROR Transfer event pointed to disabled endpoint\n"); ++ xhci_err(xhci, "ERROR Transfer event for disabled endpoint " ++ "or incorrect stream ring\n"); + return -ENODEV; + } + +@@ -1279,7 +1355,7 @@ static int handle_tx_event(struct xhci_h + td->urb->actual_length = 0; + + xhci_cleanup_halted_endpoint(xhci, +- slot_id, ep_index, td, event_trb); ++ slot_id, ep_index, 0, td, event_trb); + goto td_cleanup; + } + /* +@@ -1428,6 +1504,7 @@ static int handle_tx_event(struct xhci_h + */ + ep->stopped_td = td; + ep->stopped_trb = event_trb; ++ ep->stopped_stream = ep_ring->stream_id; + } else if (xhci_requires_manual_halt_cleanup(xhci, + ep_ctx, trb_comp_code)) { + /* Other types of errors halt the endpoint, but the +@@ -1436,7 +1513,7 @@ static int handle_tx_event(struct xhci_h + * xHCI hardware manually. + */ + xhci_cleanup_halted_endpoint(xhci, +- slot_id, ep_index, td, event_trb); ++ slot_id, ep_index, ep_ring->stream_id, td, event_trb); + } else { + /* Update ring dequeue pointer */ + while (ep_ring->dequeue != td->last_trb) +@@ -1632,14 +1709,24 @@ static int prepare_ring(struct xhci_hcd + static int prepare_transfer(struct xhci_hcd *xhci, + struct xhci_virt_device *xdev, + unsigned int ep_index, ++ unsigned int stream_id, + unsigned int num_trbs, + struct urb *urb, + struct xhci_td **td, + gfp_t mem_flags) + { + int ret; ++ struct xhci_ring *ep_ring; + struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); +- ret = prepare_ring(xhci, xdev->eps[ep_index].ring, ++ ++ ep_ring = xhci_stream_id_to_ring(xdev, ep_index, stream_id); ++ if (!ep_ring) { ++ xhci_dbg(xhci, "Can't prepare ring for bad stream ID %u\n", ++ stream_id); ++ return -EINVAL; ++ } ++ ++ ret = prepare_ring(xhci, ep_ring, + ep_ctx->ep_info & EP_STATE_MASK, + num_trbs, mem_flags); + if (ret) +@@ -1659,9 +1746,9 @@ static int prepare_transfer(struct xhci_ + (*td)->urb = urb; + urb->hcpriv = (void *) (*td); + /* Add this TD to the tail of the endpoint ring's TD list */ +- list_add_tail(&(*td)->td_list, &xdev->eps[ep_index].ring->td_list); +- (*td)->start_seg = xdev->eps[ep_index].ring->enq_seg; +- (*td)->first_trb = xdev->eps[ep_index].ring->enqueue; ++ list_add_tail(&(*td)->td_list, &ep_ring->td_list); ++ (*td)->start_seg = ep_ring->enq_seg; ++ (*td)->first_trb = ep_ring->enqueue; + + return 0; + } +@@ -1727,7 +1814,7 @@ static void check_trb_math(struct urb *u + } + + static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id, +- unsigned int ep_index, int start_cycle, ++ unsigned int ep_index, unsigned int stream_id, int start_cycle, + struct xhci_generic_trb *start_trb, struct xhci_td *td) + { + /* +@@ -1736,7 +1823,7 @@ static void giveback_first_trb(struct xh + */ + wmb(); + start_trb->field[3] |= start_cycle; +- ring_ep_doorbell(xhci, slot_id, ep_index); ++ ring_ep_doorbell(xhci, slot_id, ep_index, stream_id); + } + + /* +@@ -1810,12 +1897,16 @@ static int queue_bulk_sg_tx(struct xhci_ + struct xhci_generic_trb *start_trb; + int start_cycle; + +- ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; ++ ep_ring = xhci_urb_to_transfer_ring(xhci, urb); ++ if (!ep_ring) ++ return -EINVAL; ++ + num_trbs = count_sg_trbs_needed(xhci, urb); + num_sgs = urb->num_sgs; + + trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id], +- ep_index, num_trbs, urb, &td, mem_flags); ++ ep_index, urb->stream_id, ++ num_trbs, urb, &td, mem_flags); + if (trb_buff_len < 0) + return trb_buff_len; + /* +@@ -1924,7 +2015,8 @@ static int queue_bulk_sg_tx(struct xhci_ + } while (running_total < urb->transfer_buffer_length); + + check_trb_math(urb, num_trbs, running_total); +- giveback_first_trb(xhci, slot_id, ep_index, start_cycle, start_trb, td); ++ giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, ++ start_cycle, start_trb, td); + return 0; + } + +@@ -1946,7 +2038,9 @@ int xhci_queue_bulk_tx(struct xhci_hcd * + if (urb->num_sgs) + return queue_bulk_sg_tx(xhci, mem_flags, urb, slot_id, ep_index); + +- ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; ++ ep_ring = xhci_urb_to_transfer_ring(xhci, urb); ++ if (!ep_ring) ++ return -EINVAL; + + num_trbs = 0; + /* How much data is (potentially) left before the 64KB boundary? */ +@@ -1973,7 +2067,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd * + (unsigned long long)urb->transfer_dma, + num_trbs); + +- ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, ++ ret = prepare_transfer(xhci, xhci->devs[slot_id], ++ ep_index, urb->stream_id, + num_trbs, urb, &td, mem_flags); + if (ret < 0) + return ret; +@@ -2043,7 +2138,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd * + } while (running_total < urb->transfer_buffer_length); + + check_trb_math(urb, num_trbs, running_total); +- giveback_first_trb(xhci, slot_id, ep_index, start_cycle, start_trb, td); ++ giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, ++ start_cycle, start_trb, td); + return 0; + } + +@@ -2060,7 +2156,9 @@ int xhci_queue_ctrl_tx(struct xhci_hcd * + u32 field, length_field; + struct xhci_td *td; + +- ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; ++ ep_ring = xhci_urb_to_transfer_ring(xhci, urb); ++ if (!ep_ring) ++ return -EINVAL; + + /* + * Need to copy setup packet into setup TRB, so we can't use the setup +@@ -2081,8 +2179,9 @@ int xhci_queue_ctrl_tx(struct xhci_hcd * + */ + if (urb->transfer_buffer_length > 0) + num_trbs++; +- ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, num_trbs, +- urb, &td, mem_flags); ++ ret = prepare_transfer(xhci, xhci->devs[slot_id], ++ ep_index, urb->stream_id, ++ num_trbs, urb, &td, mem_flags); + if (ret < 0) + return ret; + +@@ -2137,7 +2236,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd * + /* Event on completion */ + field | TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state); + +- giveback_first_trb(xhci, slot_id, ep_index, start_cycle, start_trb, td); ++ giveback_first_trb(xhci, slot_id, ep_index, 0, ++ start_cycle, start_trb, td); + return 0; + } + +@@ -2249,12 +2349,14 @@ int xhci_queue_stop_endpoint(struct xhci + * This should not be used for endpoints that have streams enabled. + */ + static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, +- unsigned int ep_index, struct xhci_segment *deq_seg, ++ unsigned int ep_index, unsigned int stream_id, ++ struct xhci_segment *deq_seg, + union xhci_trb *deq_ptr, u32 cycle_state) + { + dma_addr_t addr; + u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id); + u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); ++ u32 trb_stream_id = STREAM_ID_FOR_TRB(stream_id); + u32 type = TRB_TYPE(TRB_SET_DEQ); + + addr = xhci_trb_virt_to_dma(deq_seg, deq_ptr); +@@ -2265,7 +2367,7 @@ static int queue_set_tr_deq(struct xhci_ + return 0; + } + return queue_command(xhci, lower_32_bits(addr) | cycle_state, +- upper_32_bits(addr), 0, ++ upper_32_bits(addr), trb_stream_id, + trb_slot_id | trb_ep_index | type, false); + } + +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -353,11 +353,7 @@ void xhci_event_ring_work(unsigned long + if (!xhci->devs[i]) + continue; + for (j = 0; j < 31; ++j) { +- struct xhci_ring *ring = xhci->devs[i]->eps[j].ring; +- if (!ring) +- continue; +- xhci_dbg(xhci, "Dev %d endpoint ring %d:\n", i, j); +- xhci_debug_segment(xhci, ring->deq_seg); ++ xhci_dbg_ep_rings(xhci, i, j, &xhci->devs[i]->eps[j]); + } + } + +@@ -839,7 +835,12 @@ int xhci_urb_dequeue(struct usb_hcd *hcd + xhci_debug_ring(xhci, xhci->event_ring); + ep_index = xhci_get_endpoint_index(&urb->ep->desc); + ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index]; +- ep_ring = ep->ring; ++ ep_ring = xhci_urb_to_transfer_ring(xhci, urb); ++ if (!ep_ring) { ++ ret = -EINVAL; ++ goto done; ++ } ++ + xhci_dbg(xhci, "Endpoint ring:\n"); + xhci_debug_ring(xhci, ep_ring); + td = (struct xhci_td *) urb->hcpriv; +@@ -1383,7 +1384,7 @@ void xhci_cleanup_stalled_ring(struct xh + * or it will attempt to resend it on the next doorbell ring. + */ + xhci_find_new_dequeue_state(xhci, udev->slot_id, +- ep_index, ep->stopped_td, ++ ep_index, ep->stopped_stream, ep->stopped_td, + &deq_state); + + /* HW with the reset endpoint quirk will use the saved dequeue state to +@@ -1392,10 +1393,12 @@ void xhci_cleanup_stalled_ring(struct xh + if (!(xhci->quirks & XHCI_RESET_EP_QUIRK)) { + xhci_dbg(xhci, "Queueing new dequeue state\n"); + xhci_queue_new_dequeue_state(xhci, udev->slot_id, +- ep_index, &deq_state); ++ ep_index, ep->stopped_stream, &deq_state); + } else { + /* Better hope no one uses the input context between now and the + * reset endpoint completion! ++ * XXX: No idea how this hardware will react when stream rings ++ * are enabled. + */ + xhci_dbg(xhci, "Setting up input context for " + "configure endpoint command\n"); +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -444,6 +444,7 @@ struct xhci_doorbell_array { + + /* Endpoint Target - bits 0:7 */ + #define EPI_TO_DB(p) (((p) + 1) & 0xff) ++#define STREAM_ID_TO_DB(p) (((p) & 0xffff) << 16) + + + /** +@@ -714,6 +715,7 @@ struct xhci_virt_ep { + /* The TRB that was last reported in a stopped endpoint ring */ + union xhci_trb *stopped_trb; + struct xhci_td *stopped_td; ++ unsigned int stopped_stream; + /* Watchdog timer for stop endpoint command to cancel URBs */ + struct timer_list stop_cmd_timer; + int stop_cmds_pending; +@@ -871,6 +873,10 @@ struct xhci_event_cmd { + #define TRB_TO_EP_INDEX(p) ((((p) & (0x1f << 16)) >> 16) - 1) + #define EP_ID_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16) + ++/* Set TR Dequeue Pointer command TRB fields */ ++#define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16)) ++#define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16) ++ + + /* Port Status Change Event TRB fields */ + /* Port ID - bits 31:24 */ +@@ -1040,6 +1046,7 @@ struct xhci_ring { + * if we own the TRB (if we are the consumer). See section 4.9.1. + */ + u32 cycle_state; ++ unsigned int stream_id; + }; + + struct xhci_erst_entry { +@@ -1265,6 +1272,9 @@ void xhci_dbg_ring_ptrs(struct xhci_hcd + void xhci_dbg_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int last_ep); + char *xhci_get_slot_state(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx); ++void xhci_dbg_ep_rings(struct xhci_hcd *xhci, ++ unsigned int slot_id, unsigned int ep_index, ++ struct xhci_virt_ep *ep); + + /* xHCI memory management */ + void xhci_mem_cleanup(struct xhci_hcd *xhci); +@@ -1302,6 +1312,18 @@ void xhci_setup_streams_ep_input_ctx(str + void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci, + struct xhci_ep_ctx *ep_ctx, + struct xhci_virt_ep *ep); ++struct xhci_ring *xhci_dma_to_transfer_ring( ++ struct xhci_virt_ep *ep, ++ u64 address); ++struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci, ++ struct urb *urb); ++struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci, ++ unsigned int slot_id, unsigned int ep_index, ++ unsigned int stream_id); ++struct xhci_ring *xhci_stream_id_to_ring( ++ struct xhci_virt_device *dev, ++ unsigned int ep_index, ++ unsigned int stream_id); + struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, + bool allocate_in_ctx, bool allocate_completion, + gfp_t mem_flags); +@@ -1374,9 +1396,11 @@ int xhci_queue_reset_ep(struct xhci_hcd + int xhci_queue_reset_device(struct xhci_hcd *xhci, u32 slot_id); + void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, + unsigned int slot_id, unsigned int ep_index, +- struct xhci_td *cur_td, struct xhci_dequeue_state *state); ++ unsigned int stream_id, struct xhci_td *cur_td, ++ struct xhci_dequeue_state *state); + void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, + unsigned int slot_id, unsigned int ep_index, ++ unsigned int stream_id, + struct xhci_dequeue_state *deq_state); + void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, + struct usb_device *udev, unsigned int ep_index); diff --git a/usb/xhci-add-memory-allocation-for-usb3-bulk-streams.patch b/usb/xhci-add-memory-allocation-for-usb3-bulk-streams.patch new file mode 100644 index 00000000000000..ff63b742ffc749 --- /dev/null +++ b/usb/xhci-add-memory-allocation-for-usb3-bulk-streams.patch @@ -0,0 +1,1086 @@ +From sarah.a.sharp@linux.intel.com Thu Apr 29 12:59:13 2010 +From: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Date: Fri, 2 Apr 2010 15:34:16 -0700 +Subject: USB: xhci: Add memory allocation for USB3 bulk streams. +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, usb-storage@lists.one-eyed-alien.net, Hrant Dalalyan <Hrant.Dalalyan@synopsys.com>, Alan Stern <stern@rowland.harvard.edu>, Paul Zimmerman <Paul.Zimmerman@synopsys.com>, Ashot Madatyan <Ashot.Madatyan@synopsys.com> +Message-ID: <20100402223416.GA1318@xanatos> +Content-Disposition: inline + + +Add support for allocating streams for USB 3.0 bulk endpoints. See +Documentation/usb/bulk-streams.txt for more information about how and why +you would use streams. + +When an endpoint has streams enabled, instead of having one ring where all +transfers are enqueued to the hardware, it has several rings. The ring +dequeue pointer in the endpoint context is changed to point to a "Stream +Context Array". This is basically an array of pointers to transfer rings, +one for each stream ID that the driver wants to use. + +The Stream Context Array size must be a power of two, and host controllers +can place a limit on the size of the array (4 to 2^16 entries). These +two facts make calculating the size of the Stream Context Array and the +number of entries actually used by the driver a bit tricky. + +Besides the Stream Context Array and rings for all the stream IDs, we need +one more data structure. The xHCI hardware will not tell us which stream +ID a transfer event was for, but it will give us the slot ID, endpoint +index, and physical address for the TRB that caused the event. For every +endpoint on a device, add a radix tree to map physical TRB addresses to +virtual segments within a stream ring. + +Keep track of whether an endpoint is transitioning to using streams, and +don't enqueue any URBs while that's taking place. Refuse to transition an +endpoint to streams if there are already URBs enqueued for that endpoint. + +We need to make sure that freeing streams does not fail, since a driver's +disconnect() function may attempt to do this, and it cannot fail. +Pre-allocate the command structure used to issue the Configure Endpoint +command, and reserve space on the command ring for each stream endpoint. +This may be a bit overkill, but it is permissible for the driver to +allocate all streams in one call and free them in multiple calls. (It is +not advised, however, since it is a waste of resources and time.) + +Even with the memory and ring room pre-allocated, freeing streams can +still fail because the xHC rejects the configure endpoint command. It is +valid (by the xHCI 0.96 spec) to return a "Bandwidth Error" or a "Resource +Error" for a configure endpoint command. We should never see a Bandwidth +Error, since bulk endpoints do not effect the reserved bandwidth. The +host controller can still return a Resource Error, but it's improbable +since the xHC would be going from a more resource-intensive configuration +(streams) to a less resource-intensive configuration (no streams). + +If the xHC returns a Resource Error, the endpoint will be stuck with +streams and will be unusable for drivers. It's an unavoidable consequence +of broken host controller hardware. + +Includes bug fixes from the original patch, contributed by +John Youn <John.Youn@synopsys.com> and Andy Green <AGreen@PLXTech.com> + +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-mem.c | 379 ++++++++++++++++++++++++++++++++++++++++ + drivers/usb/host/xhci-ring.c | 9 + drivers/usb/host/xhci.c | 399 ++++++++++++++++++++++++++++++++++++++++++- + drivers/usb/host/xhci.h | 84 ++++++++- + 4 files changed, 857 insertions(+), 14 deletions(-) + +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -304,6 +304,350 @@ struct xhci_ep_ctx *xhci_get_ep_ctx(stru + (ctx->bytes + (ep_index * CTX_SIZE(xhci->hcc_params))); + } + ++ ++/***************** Streams structures manipulation *************************/ ++ ++void xhci_free_stream_ctx(struct xhci_hcd *xhci, ++ unsigned int num_stream_ctxs, ++ struct xhci_stream_ctx *stream_ctx, dma_addr_t dma) ++{ ++ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); ++ ++ if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) ++ pci_free_consistent(pdev, ++ sizeof(struct xhci_stream_ctx)*num_stream_ctxs, ++ stream_ctx, dma); ++ else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) ++ return dma_pool_free(xhci->small_streams_pool, ++ stream_ctx, dma); ++ else ++ return dma_pool_free(xhci->medium_streams_pool, ++ stream_ctx, dma); ++} ++ ++/* ++ * The stream context array for each endpoint with bulk streams enabled can ++ * vary in size, based on: ++ * - how many streams the endpoint supports, ++ * - the maximum primary stream array size the host controller supports, ++ * - and how many streams the device driver asks for. ++ * ++ * The stream context array must be a power of 2, and can be as small as ++ * 64 bytes or as large as 1MB. ++ */ ++struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci, ++ unsigned int num_stream_ctxs, dma_addr_t *dma, ++ gfp_t mem_flags) ++{ ++ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); ++ ++ if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) ++ return pci_alloc_consistent(pdev, ++ sizeof(struct xhci_stream_ctx)*num_stream_ctxs, ++ dma); ++ else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) ++ return dma_pool_alloc(xhci->small_streams_pool, ++ mem_flags, dma); ++ else ++ return dma_pool_alloc(xhci->medium_streams_pool, ++ mem_flags, dma); ++} ++ ++#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING ++struct xhci_ring *dma_to_stream_ring( ++ struct xhci_stream_info *stream_info, ++ u64 address) ++{ ++ return radix_tree_lookup(&stream_info->trb_address_map, ++ address >> SEGMENT_SHIFT); ++} ++#endif /* CONFIG_USB_XHCI_HCD_DEBUGGING */ ++ ++#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING ++static int xhci_test_radix_tree(struct xhci_hcd *xhci, ++ unsigned int num_streams, ++ struct xhci_stream_info *stream_info) ++{ ++ u32 cur_stream; ++ struct xhci_ring *cur_ring; ++ u64 addr; ++ ++ for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { ++ struct xhci_ring *mapped_ring; ++ int trb_size = sizeof(union xhci_trb); ++ ++ cur_ring = stream_info->stream_rings[cur_stream]; ++ for (addr = cur_ring->first_seg->dma; ++ addr < cur_ring->first_seg->dma + SEGMENT_SIZE; ++ addr += trb_size) { ++ mapped_ring = dma_to_stream_ring(stream_info, addr); ++ if (cur_ring != mapped_ring) { ++ xhci_warn(xhci, "WARN: DMA address 0x%08llx " ++ "didn't map to stream ID %u; " ++ "mapped to ring %p\n", ++ (unsigned long long) addr, ++ cur_stream, ++ mapped_ring); ++ return -EINVAL; ++ } ++ } ++ /* One TRB after the end of the ring segment shouldn't return a ++ * pointer to the current ring (although it may be a part of a ++ * different ring). ++ */ ++ mapped_ring = dma_to_stream_ring(stream_info, addr); ++ if (mapped_ring != cur_ring) { ++ /* One TRB before should also fail */ ++ addr = cur_ring->first_seg->dma - trb_size; ++ mapped_ring = dma_to_stream_ring(stream_info, addr); ++ } ++ if (mapped_ring == cur_ring) { ++ xhci_warn(xhci, "WARN: Bad DMA address 0x%08llx " ++ "mapped to valid stream ID %u; " ++ "mapped ring = %p\n", ++ (unsigned long long) addr, ++ cur_stream, ++ mapped_ring); ++ return -EINVAL; ++ } ++ } ++ return 0; ++} ++#endif /* CONFIG_USB_XHCI_HCD_DEBUGGING */ ++ ++/* ++ * Change an endpoint's internal structure so it supports stream IDs. The ++ * number of requested streams includes stream 0, which cannot be used by device ++ * drivers. ++ * ++ * The number of stream contexts in the stream context array may be bigger than ++ * the number of streams the driver wants to use. This is because the number of ++ * stream context array entries must be a power of two. ++ * ++ * We need a radix tree for mapping physical addresses of TRBs to which stream ++ * ID they belong to. We need to do this because the host controller won't tell ++ * us which stream ring the TRB came from. We could store the stream ID in an ++ * event data TRB, but that doesn't help us for the cancellation case, since the ++ * endpoint may stop before it reaches that event data TRB. ++ * ++ * The radix tree maps the upper portion of the TRB DMA address to a ring ++ * segment that has the same upper portion of DMA addresses. For example, say I ++ * have segments of size 1KB, that are always 64-byte aligned. A segment may ++ * start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the ++ * key to the stream ID is 0x43244. I can use the DMA address of the TRB to ++ * pass the radix tree a key to get the right stream ID: ++ * ++ * 0x10c90fff >> 10 = 0x43243 ++ * 0x10c912c0 >> 10 = 0x43244 ++ * 0x10c91400 >> 10 = 0x43245 ++ * ++ * Obviously, only those TRBs with DMA addresses that are within the segment ++ * will make the radix tree return the stream ID for that ring. ++ * ++ * Caveats for the radix tree: ++ * ++ * The radix tree uses an unsigned long as a key pair. On 32-bit systems, an ++ * unsigned long will be 32-bits; on a 64-bit system an unsigned long will be ++ * 64-bits. Since we only request 32-bit DMA addresses, we can use that as the ++ * key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit ++ * PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit ++ * extended systems (where the DMA address can be bigger than 32-bits), ++ * if we allow the PCI dma mask to be bigger than 32-bits. So don't do that. ++ */ ++struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, ++ unsigned int num_stream_ctxs, ++ unsigned int num_streams, gfp_t mem_flags) ++{ ++ struct xhci_stream_info *stream_info; ++ u32 cur_stream; ++ struct xhci_ring *cur_ring; ++ unsigned long key; ++ u64 addr; ++ int ret; ++ ++ xhci_dbg(xhci, "Allocating %u streams and %u " ++ "stream context array entries.\n", ++ num_streams, num_stream_ctxs); ++ if (xhci->cmd_ring_reserved_trbs == MAX_RSVD_CMD_TRBS) { ++ xhci_dbg(xhci, "Command ring has no reserved TRBs available\n"); ++ return NULL; ++ } ++ xhci->cmd_ring_reserved_trbs++; ++ ++ stream_info = kzalloc(sizeof(struct xhci_stream_info), mem_flags); ++ if (!stream_info) ++ goto cleanup_trbs; ++ ++ stream_info->num_streams = num_streams; ++ stream_info->num_stream_ctxs = num_stream_ctxs; ++ ++ /* Initialize the array of virtual pointers to stream rings. */ ++ stream_info->stream_rings = kzalloc( ++ sizeof(struct xhci_ring *)*num_streams, ++ mem_flags); ++ if (!stream_info->stream_rings) ++ goto cleanup_info; ++ ++ /* Initialize the array of DMA addresses for stream rings for the HW. */ ++ stream_info->stream_ctx_array = xhci_alloc_stream_ctx(xhci, ++ num_stream_ctxs, &stream_info->ctx_array_dma, ++ mem_flags); ++ if (!stream_info->stream_ctx_array) ++ goto cleanup_ctx; ++ memset(stream_info->stream_ctx_array, 0, ++ sizeof(struct xhci_stream_ctx)*num_stream_ctxs); ++ ++ /* Allocate everything needed to free the stream rings later */ ++ stream_info->free_streams_command = ++ xhci_alloc_command(xhci, true, true, mem_flags); ++ if (!stream_info->free_streams_command) ++ goto cleanup_ctx; ++ ++ INIT_RADIX_TREE(&stream_info->trb_address_map, GFP_ATOMIC); ++ ++ /* Allocate rings for all the streams that the driver will use, ++ * and add their segment DMA addresses to the radix tree. ++ * Stream 0 is reserved. ++ */ ++ for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { ++ stream_info->stream_rings[cur_stream] = ++ xhci_ring_alloc(xhci, 1, true, mem_flags); ++ cur_ring = stream_info->stream_rings[cur_stream]; ++ if (!cur_ring) ++ goto cleanup_rings; ++ /* Set deq ptr, cycle bit, and stream context type */ ++ addr = cur_ring->first_seg->dma | ++ SCT_FOR_CTX(SCT_PRI_TR) | ++ cur_ring->cycle_state; ++ stream_info->stream_ctx_array[cur_stream].stream_ring = addr; ++ xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n", ++ cur_stream, (unsigned long long) addr); ++ ++ key = (unsigned long) ++ (cur_ring->first_seg->dma >> SEGMENT_SHIFT); ++ ret = radix_tree_insert(&stream_info->trb_address_map, ++ key, cur_ring); ++ if (ret) { ++ xhci_ring_free(xhci, cur_ring); ++ stream_info->stream_rings[cur_stream] = NULL; ++ goto cleanup_rings; ++ } ++ } ++ /* Leave the other unused stream ring pointers in the stream context ++ * array initialized to zero. This will cause the xHC to give us an ++ * error if the device asks for a stream ID we don't have setup (if it ++ * was any other way, the host controller would assume the ring is ++ * "empty" and wait forever for data to be queued to that stream ID). ++ */ ++#if XHCI_DEBUG ++ /* Do a little test on the radix tree to make sure it returns the ++ * correct values. ++ */ ++ if (xhci_test_radix_tree(xhci, num_streams, stream_info)) ++ goto cleanup_rings; ++#endif ++ ++ return stream_info; ++ ++cleanup_rings: ++ for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { ++ cur_ring = stream_info->stream_rings[cur_stream]; ++ if (cur_ring) { ++ addr = cur_ring->first_seg->dma; ++ radix_tree_delete(&stream_info->trb_address_map, ++ addr >> SEGMENT_SHIFT); ++ xhci_ring_free(xhci, cur_ring); ++ stream_info->stream_rings[cur_stream] = NULL; ++ } ++ } ++ xhci_free_command(xhci, stream_info->free_streams_command); ++cleanup_ctx: ++ kfree(stream_info->stream_rings); ++cleanup_info: ++ kfree(stream_info); ++cleanup_trbs: ++ xhci->cmd_ring_reserved_trbs--; ++ return NULL; ++} ++/* ++ * Sets the MaxPStreams field and the Linear Stream Array field. ++ * Sets the dequeue pointer to the stream context array. ++ */ ++void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci, ++ struct xhci_ep_ctx *ep_ctx, ++ struct xhci_stream_info *stream_info) ++{ ++ u32 max_primary_streams; ++ /* MaxPStreams is the number of stream context array entries, not the ++ * number we're actually using. Must be in 2^(MaxPstreams + 1) format. ++ * fls(0) = 0, fls(0x1) = 1, fls(0x10) = 2, fls(0x100) = 3, etc. ++ */ ++ max_primary_streams = fls(stream_info->num_stream_ctxs) - 2; ++ xhci_dbg(xhci, "Setting number of stream ctx array entries to %u\n", ++ 1 << (max_primary_streams + 1)); ++ ep_ctx->ep_info &= ~EP_MAXPSTREAMS_MASK; ++ ep_ctx->ep_info |= EP_MAXPSTREAMS(max_primary_streams); ++ ep_ctx->ep_info |= EP_HAS_LSA; ++ ep_ctx->deq = stream_info->ctx_array_dma; ++} ++ ++/* ++ * Sets the MaxPStreams field and the Linear Stream Array field to 0. ++ * Reinstalls the "normal" endpoint ring (at its previous dequeue mark, ++ * not at the beginning of the ring). ++ */ ++void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci, ++ struct xhci_ep_ctx *ep_ctx, ++ struct xhci_virt_ep *ep) ++{ ++ dma_addr_t addr; ++ ep_ctx->ep_info &= ~EP_MAXPSTREAMS_MASK; ++ ep_ctx->ep_info &= ~EP_HAS_LSA; ++ addr = xhci_trb_virt_to_dma(ep->ring->deq_seg, ep->ring->dequeue); ++ ep_ctx->deq = addr | ep->ring->cycle_state; ++} ++ ++/* Frees all stream contexts associated with the endpoint, ++ * ++ * Caller should fix the endpoint context streams fields. ++ */ ++void xhci_free_stream_info(struct xhci_hcd *xhci, ++ struct xhci_stream_info *stream_info) ++{ ++ int cur_stream; ++ struct xhci_ring *cur_ring; ++ dma_addr_t addr; ++ ++ if (!stream_info) ++ return; ++ ++ for (cur_stream = 1; cur_stream < stream_info->num_streams; ++ cur_stream++) { ++ cur_ring = stream_info->stream_rings[cur_stream]; ++ if (cur_ring) { ++ addr = cur_ring->first_seg->dma; ++ radix_tree_delete(&stream_info->trb_address_map, ++ addr >> SEGMENT_SHIFT); ++ xhci_ring_free(xhci, cur_ring); ++ stream_info->stream_rings[cur_stream] = NULL; ++ } ++ } ++ xhci_free_command(xhci, stream_info->free_streams_command); ++ xhci->cmd_ring_reserved_trbs--; ++ if (stream_info->stream_ctx_array) ++ xhci_free_stream_ctx(xhci, ++ stream_info->num_stream_ctxs, ++ stream_info->stream_ctx_array, ++ stream_info->ctx_array_dma); ++ ++ if (stream_info) ++ kfree(stream_info->stream_rings); ++ kfree(stream_info); ++} ++ ++ ++/***************** Device context manipulation *************************/ ++ + static void xhci_init_endpoint_timer(struct xhci_hcd *xhci, + struct xhci_virt_ep *ep) + { +@@ -328,9 +672,13 @@ void xhci_free_virt_device(struct xhci_h + if (!dev) + return; + +- for (i = 0; i < 31; ++i) ++ for (i = 0; i < 31; ++i) { + if (dev->eps[i].ring) + xhci_ring_free(xhci, dev->eps[i].ring); ++ if (dev->eps[i].stream_info) ++ xhci_free_stream_info(xhci, ++ dev->eps[i].stream_info); ++ } + + if (dev->ring_cache) { + for (i = 0; i < dev->num_rings_cached; i++) +@@ -655,6 +1003,9 @@ static inline u32 xhci_get_max_esit_payl + return max_packet * (max_burst + 1); + } + ++/* Set up an endpoint with one ring segment. Do not allocate stream rings. ++ * Drivers will have to call usb_alloc_streams() to do that. ++ */ + int xhci_endpoint_init(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + struct usb_device *udev, +@@ -1003,6 +1354,16 @@ void xhci_mem_cleanup(struct xhci_hcd *x + xhci->device_pool = NULL; + xhci_dbg(xhci, "Freed device context pool\n"); + ++ if (xhci->small_streams_pool) ++ dma_pool_destroy(xhci->small_streams_pool); ++ xhci->small_streams_pool = NULL; ++ xhci_dbg(xhci, "Freed small stream array pool\n"); ++ ++ if (xhci->medium_streams_pool) ++ dma_pool_destroy(xhci->medium_streams_pool); ++ xhci->medium_streams_pool = NULL; ++ xhci_dbg(xhci, "Freed medium stream array pool\n"); ++ + xhci_write_64(xhci, 0, &xhci->op_regs->dcbaa_ptr); + if (xhci->dcbaa) + pci_free_consistent(pdev, sizeof(*xhci->dcbaa), +@@ -1239,6 +1600,22 @@ int xhci_mem_init(struct xhci_hcd *xhci, + if (!xhci->segment_pool || !xhci->device_pool) + goto fail; + ++ /* Linear stream context arrays don't have any boundary restrictions, ++ * and only need to be 16-byte aligned. ++ */ ++ xhci->small_streams_pool = ++ dma_pool_create("xHCI 256 byte stream ctx arrays", ++ dev, SMALL_STREAM_ARRAY_SIZE, 16, 0); ++ xhci->medium_streams_pool = ++ dma_pool_create("xHCI 1KB stream ctx arrays", ++ dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0); ++ /* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE ++ * will be allocated with pci_alloc_consistent() ++ */ ++ ++ if (!xhci->small_streams_pool || !xhci->medium_streams_pool) ++ goto fail; ++ + /* Set up the command ring to have one segments for now. */ + xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, flags); + if (!xhci->cmd_ring) +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -306,6 +306,10 @@ static void ring_ep_doorbell(struct xhci + ep_state = ep->ep_state; + /* Don't ring the doorbell for this endpoint if there are pending + * cancellations because the we don't want to interrupt processing. ++ * We don't want to restart any stream rings if there's a set dequeue ++ * pointer command pending because the device can choose to start any ++ * stream once the endpoint is on the HW schedule. ++ * FIXME - check all the stream rings for pending cancellations. + */ + if (!(ep_state & EP_HALT_PENDING) && !(ep_state & SET_DEQ_PENDING) + && !(ep_state & EP_HALTED)) { +@@ -897,8 +901,9 @@ static void handle_cmd_completion(struct + * Configure endpoint commands can come from the USB core + * configuration or alt setting changes, or because the HW + * needed an extra configure endpoint command after a reset +- * endpoint command. In the latter case, the xHCI driver is +- * not waiting on the configure endpoint command. ++ * endpoint command or streams were being configured. ++ * If the command was for a halted endpoint, the xHCI driver ++ * is not waiting on the configure endpoint command. + */ + ctrl_ctx = xhci_get_input_control_ctx(xhci, + virt_dev->in_ctx); +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -21,6 +21,7 @@ + */ + + #include <linux/irq.h> ++#include <linux/log2.h> + #include <linux/module.h> + #include <linux/moduleparam.h> + #include <linux/slab.h> +@@ -726,8 +727,21 @@ int xhci_urb_enqueue(struct usb_hcd *hcd + spin_lock_irqsave(&xhci->lock, flags); + if (xhci->xhc_state & XHCI_STATE_DYING) + goto dying; +- ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, +- slot_id, ep_index); ++ if (xhci->devs[slot_id]->eps[ep_index].ep_state & ++ EP_GETTING_STREAMS) { ++ xhci_warn(xhci, "WARN: Can't enqueue URB while bulk ep " ++ "is transitioning to using streams.\n"); ++ ret = -EINVAL; ++ } else if (xhci->devs[slot_id]->eps[ep_index].ep_state & ++ EP_GETTING_NO_STREAMS) { ++ xhci_warn(xhci, "WARN: Can't enqueue URB while bulk ep " ++ "is transitioning to " ++ "not having streams.\n"); ++ ret = -EINVAL; ++ } else { ++ ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, ++ slot_id, ep_index); ++ } + spin_unlock_irqrestore(&xhci->lock, flags); + } else if (usb_endpoint_xfer_int(&urb->ep->desc)) { + spin_lock_irqsave(&xhci->lock, flags); +@@ -1444,6 +1458,387 @@ void xhci_endpoint_reset(struct usb_hcd + xhci_warn(xhci, "FIXME allocate a new ring segment\n"); + } + ++static int xhci_check_streams_endpoint(struct xhci_hcd *xhci, ++ struct usb_device *udev, struct usb_host_endpoint *ep, ++ unsigned int slot_id) ++{ ++ int ret; ++ unsigned int ep_index; ++ unsigned int ep_state; ++ ++ if (!ep) ++ return -EINVAL; ++ ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, __func__); ++ if (ret <= 0) ++ return -EINVAL; ++ if (!ep->ss_ep_comp) { ++ xhci_warn(xhci, "WARN: No SuperSpeed Endpoint Companion" ++ " descriptor for ep 0x%x\n", ++ ep->desc.bEndpointAddress); ++ return -EINVAL; ++ } ++ if (ep->ss_ep_comp->desc.bmAttributes == 0) { ++ xhci_warn(xhci, "WARN: SuperSpeed Endpoint Companion" ++ " descriptor for ep 0x%x does not support streams\n", ++ ep->desc.bEndpointAddress); ++ return -EINVAL; ++ } ++ ++ ep_index = xhci_get_endpoint_index(&ep->desc); ++ ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state; ++ if (ep_state & EP_HAS_STREAMS || ++ ep_state & EP_GETTING_STREAMS) { ++ xhci_warn(xhci, "WARN: SuperSpeed bulk endpoint 0x%x " ++ "already has streams set up.\n", ++ ep->desc.bEndpointAddress); ++ xhci_warn(xhci, "Send email to xHCI maintainer and ask for " ++ "dynamic stream context array reallocation.\n"); ++ return -EINVAL; ++ } ++ if (!list_empty(&xhci->devs[slot_id]->eps[ep_index].ring->td_list)) { ++ xhci_warn(xhci, "Cannot setup streams for SuperSpeed bulk " ++ "endpoint 0x%x; URBs are pending.\n", ++ ep->desc.bEndpointAddress); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void xhci_calculate_streams_entries(struct xhci_hcd *xhci, ++ unsigned int *num_streams, unsigned int *num_stream_ctxs) ++{ ++ unsigned int max_streams; ++ ++ /* The stream context array size must be a power of two */ ++ *num_stream_ctxs = roundup_pow_of_two(*num_streams); ++ /* ++ * Find out how many primary stream array entries the host controller ++ * supports. Later we may use secondary stream arrays (similar to 2nd ++ * level page entries), but that's an optional feature for xHCI host ++ * controllers. xHCs must support at least 4 stream IDs. ++ */ ++ max_streams = HCC_MAX_PSA(xhci->hcc_params); ++ if (*num_stream_ctxs > max_streams) { ++ xhci_dbg(xhci, "xHCI HW only supports %u stream ctx entries.\n", ++ max_streams); ++ *num_stream_ctxs = max_streams; ++ *num_streams = max_streams; ++ } ++} ++ ++/* Returns an error code if one of the endpoint already has streams. ++ * This does not change any data structures, it only checks and gathers ++ * information. ++ */ ++static int xhci_calculate_streams_and_bitmask(struct xhci_hcd *xhci, ++ struct usb_device *udev, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ unsigned int *num_streams, u32 *changed_ep_bitmask) ++{ ++ struct usb_host_ss_ep_comp *ss_ep_comp; ++ unsigned int max_streams; ++ unsigned int endpoint_flag; ++ int i; ++ int ret; ++ ++ for (i = 0; i < num_eps; i++) { ++ ret = xhci_check_streams_endpoint(xhci, udev, ++ eps[i], udev->slot_id); ++ if (ret < 0) ++ return ret; ++ ++ ss_ep_comp = eps[i]->ss_ep_comp; ++ max_streams = USB_SS_MAX_STREAMS(ss_ep_comp->desc.bmAttributes); ++ if (max_streams < (*num_streams - 1)) { ++ xhci_dbg(xhci, "Ep 0x%x only supports %u stream IDs.\n", ++ eps[i]->desc.bEndpointAddress, ++ max_streams); ++ *num_streams = max_streams+1; ++ } ++ ++ endpoint_flag = xhci_get_endpoint_flag(&eps[i]->desc); ++ if (*changed_ep_bitmask & endpoint_flag) ++ return -EINVAL; ++ *changed_ep_bitmask |= endpoint_flag; ++ } ++ return 0; ++} ++ ++static u32 xhci_calculate_no_streams_bitmask(struct xhci_hcd *xhci, ++ struct usb_device *udev, ++ struct usb_host_endpoint **eps, unsigned int num_eps) ++{ ++ u32 changed_ep_bitmask = 0; ++ unsigned int slot_id; ++ unsigned int ep_index; ++ unsigned int ep_state; ++ int i; ++ ++ slot_id = udev->slot_id; ++ if (!xhci->devs[slot_id]) ++ return 0; ++ ++ for (i = 0; i < num_eps; i++) { ++ ep_index = xhci_get_endpoint_index(&eps[i]->desc); ++ ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state; ++ /* Are streams already being freed for the endpoint? */ ++ if (ep_state & EP_GETTING_NO_STREAMS) { ++ xhci_warn(xhci, "WARN Can't disable streams for " ++ "endpoint 0x%x\n, " ++ "streams are being disabled already.", ++ eps[i]->desc.bEndpointAddress); ++ return 0; ++ } ++ /* Are there actually any streams to free? */ ++ if (!(ep_state & EP_HAS_STREAMS) && ++ !(ep_state & EP_GETTING_STREAMS)) { ++ xhci_warn(xhci, "WARN Can't disable streams for " ++ "endpoint 0x%x\n, " ++ "streams are already disabled!", ++ eps[i]->desc.bEndpointAddress); ++ xhci_warn(xhci, "WARN xhci_free_streams() called " ++ "with non-streams endpoint\n"); ++ return 0; ++ } ++ changed_ep_bitmask |= xhci_get_endpoint_flag(&eps[i]->desc); ++ } ++ return changed_ep_bitmask; ++} ++ ++/* ++ * The USB device drivers use this function (though the HCD interface in USB ++ * core) to prepare a set of bulk endpoints to use streams. Streams are used to ++ * coordinate mass storage command queueing across multiple endpoints (basically ++ * a stream ID == a task ID). ++ * ++ * Setting up streams involves allocating the same size stream context array ++ * for each endpoint and issuing a configure endpoint command for all endpoints. ++ * ++ * Don't allow the call to succeed if one endpoint only supports one stream ++ * (which means it doesn't support streams at all). ++ * ++ * Drivers may get less stream IDs than they asked for, if the host controller ++ * hardware or endpoints claim they can't support the number of requested ++ * stream IDs. ++ */ ++int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ unsigned int num_streams, gfp_t mem_flags) ++{ ++ int i, ret; ++ struct xhci_hcd *xhci; ++ struct xhci_virt_device *vdev; ++ struct xhci_command *config_cmd; ++ unsigned int ep_index; ++ unsigned int num_stream_ctxs; ++ unsigned long flags; ++ u32 changed_ep_bitmask = 0; ++ ++ if (!eps) ++ return -EINVAL; ++ ++ /* Add one to the number of streams requested to account for ++ * stream 0 that is reserved for xHCI usage. ++ */ ++ num_streams += 1; ++ xhci = hcd_to_xhci(hcd); ++ xhci_dbg(xhci, "Driver wants %u stream IDs (including stream 0).\n", ++ num_streams); ++ ++ config_cmd = xhci_alloc_command(xhci, true, true, mem_flags); ++ if (!config_cmd) { ++ xhci_dbg(xhci, "Could not allocate xHCI command structure.\n"); ++ return -ENOMEM; ++ } ++ ++ /* Check to make sure all endpoints are not already configured for ++ * streams. While we're at it, find the maximum number of streams that ++ * all the endpoints will support and check for duplicate endpoints. ++ */ ++ spin_lock_irqsave(&xhci->lock, flags); ++ ret = xhci_calculate_streams_and_bitmask(xhci, udev, eps, ++ num_eps, &num_streams, &changed_ep_bitmask); ++ if (ret < 0) { ++ xhci_free_command(xhci, config_cmd); ++ spin_unlock_irqrestore(&xhci->lock, flags); ++ return ret; ++ } ++ if (num_streams <= 1) { ++ xhci_warn(xhci, "WARN: endpoints can't handle " ++ "more than one stream.\n"); ++ xhci_free_command(xhci, config_cmd); ++ spin_unlock_irqrestore(&xhci->lock, flags); ++ return -EINVAL; ++ } ++ vdev = xhci->devs[udev->slot_id]; ++ /* Mark each endpoint as being in transistion, so ++ * xhci_urb_enqueue() will reject all URBs. ++ */ ++ for (i = 0; i < num_eps; i++) { ++ ep_index = xhci_get_endpoint_index(&eps[i]->desc); ++ vdev->eps[ep_index].ep_state |= EP_GETTING_STREAMS; ++ } ++ spin_unlock_irqrestore(&xhci->lock, flags); ++ ++ /* Setup internal data structures and allocate HW data structures for ++ * streams (but don't install the HW structures in the input context ++ * until we're sure all memory allocation succeeded). ++ */ ++ xhci_calculate_streams_entries(xhci, &num_streams, &num_stream_ctxs); ++ xhci_dbg(xhci, "Need %u stream ctx entries for %u stream IDs.\n", ++ num_stream_ctxs, num_streams); ++ ++ for (i = 0; i < num_eps; i++) { ++ ep_index = xhci_get_endpoint_index(&eps[i]->desc); ++ vdev->eps[ep_index].stream_info = xhci_alloc_stream_info(xhci, ++ num_stream_ctxs, ++ num_streams, mem_flags); ++ if (!vdev->eps[ep_index].stream_info) ++ goto cleanup; ++ /* Set maxPstreams in endpoint context and update deq ptr to ++ * point to stream context array. FIXME ++ */ ++ } ++ ++ /* Set up the input context for a configure endpoint command. */ ++ for (i = 0; i < num_eps; i++) { ++ struct xhci_ep_ctx *ep_ctx; ++ ++ ep_index = xhci_get_endpoint_index(&eps[i]->desc); ++ ep_ctx = xhci_get_ep_ctx(xhci, config_cmd->in_ctx, ep_index); ++ ++ xhci_endpoint_copy(xhci, config_cmd->in_ctx, ++ vdev->out_ctx, ep_index); ++ xhci_setup_streams_ep_input_ctx(xhci, ep_ctx, ++ vdev->eps[ep_index].stream_info); ++ } ++ /* Tell the HW to drop its old copy of the endpoint context info ++ * and add the updated copy from the input context. ++ */ ++ xhci_setup_input_ctx_for_config_ep(xhci, config_cmd->in_ctx, ++ vdev->out_ctx, changed_ep_bitmask, changed_ep_bitmask); ++ ++ /* Issue and wait for the configure endpoint command */ ++ ret = xhci_configure_endpoint(xhci, udev, config_cmd, ++ false, false); ++ ++ /* xHC rejected the configure endpoint command for some reason, so we ++ * leave the old ring intact and free our internal streams data ++ * structure. ++ */ ++ if (ret < 0) ++ goto cleanup; ++ ++ spin_lock_irqsave(&xhci->lock, flags); ++ for (i = 0; i < num_eps; i++) { ++ ep_index = xhci_get_endpoint_index(&eps[i]->desc); ++ vdev->eps[ep_index].ep_state &= ~EP_GETTING_STREAMS; ++ xhci_dbg(xhci, "Slot %u ep ctx %u now has streams.\n", ++ udev->slot_id, ep_index); ++ vdev->eps[ep_index].ep_state |= EP_HAS_STREAMS; ++ } ++ xhci_free_command(xhci, config_cmd); ++ spin_unlock_irqrestore(&xhci->lock, flags); ++ ++ /* Subtract 1 for stream 0, which drivers can't use */ ++ return num_streams - 1; ++ ++cleanup: ++ /* If it didn't work, free the streams! */ ++ for (i = 0; i < num_eps; i++) { ++ ep_index = xhci_get_endpoint_index(&eps[i]->desc); ++ xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info); ++ /* FIXME Unset maxPstreams in endpoint context and ++ * update deq ptr to point to normal string ring. ++ */ ++ vdev->eps[ep_index].ep_state &= ~EP_GETTING_STREAMS; ++ vdev->eps[ep_index].ep_state &= ~EP_HAS_STREAMS; ++ xhci_endpoint_zero(xhci, vdev, eps[i]); ++ } ++ xhci_free_command(xhci, config_cmd); ++ return -ENOMEM; ++} ++ ++/* Transition the endpoint from using streams to being a "normal" endpoint ++ * without streams. ++ * ++ * Modify the endpoint context state, submit a configure endpoint command, ++ * and free all endpoint rings for streams if that completes successfully. ++ */ ++int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ gfp_t mem_flags) ++{ ++ int i, ret; ++ struct xhci_hcd *xhci; ++ struct xhci_virt_device *vdev; ++ struct xhci_command *command; ++ unsigned int ep_index; ++ unsigned long flags; ++ u32 changed_ep_bitmask; ++ ++ xhci = hcd_to_xhci(hcd); ++ vdev = xhci->devs[udev->slot_id]; ++ ++ /* Set up a configure endpoint command to remove the streams rings */ ++ spin_lock_irqsave(&xhci->lock, flags); ++ changed_ep_bitmask = xhci_calculate_no_streams_bitmask(xhci, ++ udev, eps, num_eps); ++ if (changed_ep_bitmask == 0) { ++ spin_unlock_irqrestore(&xhci->lock, flags); ++ return -EINVAL; ++ } ++ ++ /* Use the xhci_command structure from the first endpoint. We may have ++ * allocated too many, but the driver may call xhci_free_streams() for ++ * each endpoint it grouped into one call to xhci_alloc_streams(). ++ */ ++ ep_index = xhci_get_endpoint_index(&eps[0]->desc); ++ command = vdev->eps[ep_index].stream_info->free_streams_command; ++ for (i = 0; i < num_eps; i++) { ++ struct xhci_ep_ctx *ep_ctx; ++ ++ ep_index = xhci_get_endpoint_index(&eps[i]->desc); ++ ep_ctx = xhci_get_ep_ctx(xhci, command->in_ctx, ep_index); ++ xhci->devs[udev->slot_id]->eps[ep_index].ep_state |= ++ EP_GETTING_NO_STREAMS; ++ ++ xhci_endpoint_copy(xhci, command->in_ctx, ++ vdev->out_ctx, ep_index); ++ xhci_setup_no_streams_ep_input_ctx(xhci, ep_ctx, ++ &vdev->eps[ep_index]); ++ } ++ xhci_setup_input_ctx_for_config_ep(xhci, command->in_ctx, ++ vdev->out_ctx, changed_ep_bitmask, changed_ep_bitmask); ++ spin_unlock_irqrestore(&xhci->lock, flags); ++ ++ /* Issue and wait for the configure endpoint command, ++ * which must succeed. ++ */ ++ ret = xhci_configure_endpoint(xhci, udev, command, ++ false, true); ++ ++ /* xHC rejected the configure endpoint command for some reason, so we ++ * leave the streams rings intact. ++ */ ++ if (ret < 0) ++ return ret; ++ ++ spin_lock_irqsave(&xhci->lock, flags); ++ for (i = 0; i < num_eps; i++) { ++ ep_index = xhci_get_endpoint_index(&eps[i]->desc); ++ xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info); ++ /* FIXME Unset maxPstreams in endpoint context and ++ * update deq ptr to point to normal string ring. ++ */ ++ vdev->eps[ep_index].ep_state &= ~EP_GETTING_NO_STREAMS; ++ vdev->eps[ep_index].ep_state &= ~EP_HAS_STREAMS; ++ } ++ spin_unlock_irqrestore(&xhci->lock, flags); ++ ++ return 0; ++} ++ + /* + * This submits a Reset Device Command, which will set the device state to 0, + * set the device address to 0, and disable all the endpoints except the default +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -117,7 +117,7 @@ struct xhci_cap_regs { + /* true: no secondary Stream ID Support */ + #define HCC_NSS(p) ((p) & (1 << 7)) + /* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */ +-#define HCC_MAX_PSA (1 << ((((p) >> 12) & 0xf) + 1)) ++#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) + /* Extended Capabilities pointer from PCI base - section 5.3.6 */ + #define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p) + +@@ -585,6 +585,10 @@ struct xhci_ep_ctx { + /* Interval - period between requests to an endpoint - 125u increments. */ + #define EP_INTERVAL(p) ((p & 0xff) << 16) + #define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff)) ++#define EP_MAXPSTREAMS_MASK (0x1f << 10) ++#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) ++/* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ ++#define EP_HAS_LSA (1 << 15) + + /* ep_info2 bitmasks */ + /* +@@ -648,8 +652,50 @@ struct xhci_command { + /* add context bitmasks */ + #define ADD_EP(x) (0x1 << x) + ++struct xhci_stream_ctx { ++ /* 64-bit stream ring address, cycle state, and stream type */ ++ u64 stream_ring; ++ /* offset 0x14 - 0x1f reserved for HC internal use */ ++ u32 reserved[2]; ++}; ++ ++/* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */ ++#define SCT_FOR_CTX(p) (((p) << 1) & 0x7) ++/* Secondary stream array type, dequeue pointer is to a transfer ring */ ++#define SCT_SEC_TR 0 ++/* Primary stream array type, dequeue pointer is to a transfer ring */ ++#define SCT_PRI_TR 1 ++/* Dequeue pointer is for a secondary stream array (SSA) with 8 entries */ ++#define SCT_SSA_8 2 ++#define SCT_SSA_16 3 ++#define SCT_SSA_32 4 ++#define SCT_SSA_64 5 ++#define SCT_SSA_128 6 ++#define SCT_SSA_256 7 ++ ++/* Assume no secondary streams for now */ ++struct xhci_stream_info { ++ struct xhci_ring **stream_rings; ++ /* Number of streams, including stream 0 (which drivers can't use) */ ++ unsigned int num_streams; ++ /* The stream context array may be bigger than ++ * the number of streams the driver asked for ++ */ ++ struct xhci_stream_ctx *stream_ctx_array; ++ unsigned int num_stream_ctxs; ++ dma_addr_t ctx_array_dma; ++ /* For mapping physical TRB addresses to segments in stream rings */ ++ struct radix_tree_root trb_address_map; ++ struct xhci_command *free_streams_command; ++}; ++ ++#define SMALL_STREAM_ARRAY_SIZE 256 ++#define MEDIUM_STREAM_ARRAY_SIZE 1024 ++ + struct xhci_virt_ep { + struct xhci_ring *ring; ++ /* Related to endpoints that are configured to use stream IDs only */ ++ struct xhci_stream_info *stream_info; + /* Temporary storage in case the configure endpoint command fails and we + * have to restore the device state to the previous state + */ +@@ -658,6 +704,11 @@ struct xhci_virt_ep { + #define SET_DEQ_PENDING (1 << 0) + #define EP_HALTED (1 << 1) /* For stall handling */ + #define EP_HALT_PENDING (1 << 2) /* For URB cancellation */ ++/* Transitioning the endpoint to using streams, don't enqueue URBs */ ++#define EP_GETTING_STREAMS (1 << 3) ++#define EP_HAS_STREAMS (1 << 4) ++/* Transitioning the endpoint to not using streams, don't enqueue URBs */ ++#define EP_GETTING_NO_STREAMS (1 << 5) + /* ---- Related to URB cancellation ---- */ + struct list_head cancelled_td_list; + /* The TRB that was last reported in a stopped endpoint ring */ +@@ -710,14 +761,6 @@ struct xhci_device_context_array { + */ + + +-struct xhci_stream_ctx { +- /* 64-bit stream ring address, cycle state, and stream type */ +- u64 stream_ring; +- /* offset 0x14 - 0x1f reserved for HC internal use */ +- u32 reserved[2]; +-}; +- +- + struct xhci_transfer_event { + /* 64-bit buffer address, or immediate data */ + u64 buffer; +@@ -952,6 +995,10 @@ union xhci_trb { + /* Allow two commands + a link TRB, along with any reserved command TRBs */ + #define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3) + #define SEGMENT_SIZE (TRBS_PER_SEGMENT*16) ++/* SEGMENT_SHIFT should be log2(SEGMENT_SIZE). ++ * Change this if you change TRBS_PER_SEGMENT! ++ */ ++#define SEGMENT_SHIFT 10 + /* TRB buffer pointers can't cross 64KB boundaries */ + #define TRB_MAX_BUFF_SHIFT 16 + #define TRB_MAX_BUFF_SIZE (1 << TRB_MAX_BUFF_SHIFT) +@@ -1088,6 +1135,8 @@ struct xhci_hcd { + /* DMA pools */ + struct dma_pool *device_pool; + struct dma_pool *segment_pool; ++ struct dma_pool *small_streams_pool; ++ struct dma_pool *medium_streams_pool; + + #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING + /* Poll the rings - for debugging */ +@@ -1242,6 +1291,17 @@ void xhci_ring_free(struct xhci_hcd *xhc + void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + unsigned int ep_index); ++struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, ++ unsigned int num_stream_ctxs, ++ unsigned int num_streams, gfp_t flags); ++void xhci_free_stream_info(struct xhci_hcd *xhci, ++ struct xhci_stream_info *stream_info); ++void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci, ++ struct xhci_ep_ctx *ep_ctx, ++ struct xhci_stream_info *stream_info); ++void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci, ++ struct xhci_ep_ctx *ep_ctx, ++ struct xhci_virt_ep *ep); + struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, + bool allocate_in_ctx, bool allocate_completion, + gfp_t mem_flags); +@@ -1266,6 +1326,12 @@ int xhci_get_frame(struct usb_hcd *hcd); + irqreturn_t xhci_irq(struct usb_hcd *hcd); + int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev); + void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev); ++int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ unsigned int num_streams, gfp_t mem_flags); ++int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, ++ struct usb_host_endpoint **eps, unsigned int num_eps, ++ gfp_t mem_flags); + int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev); + int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags); |
