diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2009-11-20 13:19:37 -0800 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-11-20 13:19:37 -0800 |
| commit | 7206339a2d7c087c9286f715091433c7b4ebc64b (patch) | |
| tree | 2e18e1c51359cae4501121e5aa8f5f290b7dd56b | |
| parent | 09fee4a605baa586dbe2939dadbf6e7ee5c394ca (diff) | |
| download | patches-7206339a2d7c087c9286f715091433c7b4ebc64b.tar.gz | |
lots o usb patches
and some sysfs ones as well.
41 files changed, 8845 insertions, 0 deletions
diff --git a/driver-core/sysfs-rename-sysfs_d_iput-to-sysfs_dentry_iput.patch b/driver-core/sysfs-rename-sysfs_d_iput-to-sysfs_dentry_iput.patch new file mode 100644 index 00000000000000..ff09ba7d33b986 --- /dev/null +++ b/driver-core/sysfs-rename-sysfs_d_iput-to-sysfs_dentry_iput.patch @@ -0,0 +1,44 @@ +From ebiederm@xmission.com Fri Nov 20 12:06:44 2009 +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Sat, 7 Nov 2009 23:27:00 -0800 +Subject: sysfs: Rename sysfs_d_iput to sysfs_dentry_iput +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Kay Sievers <kay.sievers@vrfy.org>, Greg KH <greg@kroah.com>, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, Eric Dumazet <eric.dumazet@gmail.com>, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, "Eric W. Biederman" <ebiederm@xmission.com>, "Eric W. Biederman" <ebiederm@aristanetworks.com> +Message-ID: <1257665233-12468-2-git-send-email-ebiederm@xmission.com> + + +From: Eric W. Biederman <ebiederm@xmission.com> + +Using dentry instead of d in the function name is what +several other filesystems are doing and it seems to be +a more readable convention. + +Acked-by: Tejun Heo <tj@kernel.org> +Acked-by: Serge Hallyn <serue@us.ibm.com> +Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + fs/sysfs/dir.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/fs/sysfs/dir.c ++++ b/fs/sysfs/dir.c +@@ -298,7 +298,7 @@ void release_sysfs_dirent(struct sysfs_d + goto repeat; + } + +-static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) ++static void sysfs_dentry_iput(struct dentry * dentry, struct inode * inode) + { + struct sysfs_dirent * sd = dentry->d_fsdata; + +@@ -307,7 +307,7 @@ static void sysfs_d_iput(struct dentry * + } + + static const struct dentry_operations sysfs_dentry_ops = { +- .d_iput = sysfs_d_iput, ++ .d_iput = sysfs_dentry_iput, + }; + + struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) diff --git a/driver-core/sysfs-simplify-iattr-time-assignments.patch b/driver-core/sysfs-simplify-iattr-time-assignments.patch new file mode 100644 index 00000000000000..67907d2dc07d1a --- /dev/null +++ b/driver-core/sysfs-simplify-iattr-time-assignments.patch @@ -0,0 +1,45 @@ +From ebiederm@xmission.com Fri Nov 20 12:10:29 2009 +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Sat, 7 Nov 2009 23:27:03 -0800 +Subject: sysfs: Simplify iattr time assignments +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Kay Sievers <kay.sievers@vrfy.org>, Greg KH <greg@kroah.com>, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, Eric Dumazet <eric.dumazet@gmail.com>, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, "Eric W. Biederman" <ebiederm@xmission.com>, "Eric W. Biederman" <ebiederm@aristanetworks.com> +Message-ID: <1257665233-12468-5-git-send-email-ebiederm@xmission.com> + + +From: Eric W. Biederman <ebiederm@xmission.com> + +The granularity of sysfs time when we keep it is 1 ns. Which +when passed to timestamp_trunc results in a nop. So remove +the unnecessary function call making sysfs_setattr slightly +easier to read. + +Acked-by: Tejun Heo <tj@kernel.org> +Acked-by: Serge Hallyn <serue@us.ibm.com> +Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + fs/sysfs/inode.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +--- a/fs/sysfs/inode.c ++++ b/fs/sysfs/inode.c +@@ -104,14 +104,11 @@ int sysfs_setattr(struct dentry * dentry + if (ia_valid & ATTR_GID) + iattrs->ia_gid = iattr->ia_gid; + if (ia_valid & ATTR_ATIME) +- iattrs->ia_atime = timespec_trunc(iattr->ia_atime, +- inode->i_sb->s_time_gran); ++ iattrs->ia_atime = iattr->ia_atime; + if (ia_valid & ATTR_MTIME) +- iattrs->ia_mtime = timespec_trunc(iattr->ia_mtime, +- inode->i_sb->s_time_gran); ++ iattrs->ia_mtime = iattr->ia_mtime; + if (ia_valid & ATTR_CTIME) +- iattrs->ia_ctime = timespec_trunc(iattr->ia_ctime, +- inode->i_sb->s_time_gran); ++ iattrs->ia_ctime = iattr->ia_ctime; + if (ia_valid & ATTR_MODE) { + umode_t mode = iattr->ia_mode; + diff --git a/driver-core/sysfs-simplify-sysfs_chmod_file-semantics.patch b/driver-core/sysfs-simplify-sysfs_chmod_file-semantics.patch new file mode 100644 index 00000000000000..cb975324022d6b --- /dev/null +++ b/driver-core/sysfs-simplify-sysfs_chmod_file-semantics.patch @@ -0,0 +1,51 @@ +From ebiederm@xmission.com Fri Nov 20 12:10:03 2009 +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Sat, 7 Nov 2009 23:27:02 -0800 +Subject: sysfs: Simplify sysfs_chmod_file semantics +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Kay Sievers <kay.sievers@vrfy.org>, Greg KH <greg@kroah.com>, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, Eric Dumazet <eric.dumazet@gmail.com>, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, "Eric W. Biederman" <ebiederm@xmission.com>, "Eric W. Biederman" <ebiederm@aristanetworks.com> +Message-ID: <1257665233-12468-4-git-send-email-ebiederm@xmission.com> + + +From: Eric W. Biederman <ebiederm@xmission.com> + +Currently every caller of sysfs_chmod_file happens at either +file creation time to set a non-default mode or in response +to a specific user requested space change in policy. Making +timestamps of when the chmod happens and notification of +a file changing mode uninteresting. + +Remove the unnecessary time stamp and filesystem change +notification, and removes the last of the explicit inotify +and donitfy support from sysfs. + +Acked-by: Tejun Heo <tj@kernel.org> +Acked-by: Serge Hallyn <serue@us.ibm.com> +Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + fs/sysfs/file.c | 10 +--------- + 1 file changed, 1 insertion(+), 9 deletions(-) + +--- a/fs/sysfs/file.c ++++ b/fs/sysfs/file.c +@@ -604,17 +604,9 @@ int sysfs_chmod_file(struct kobject *kob + mutex_lock(&inode->i_mutex); + + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); +- newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; +- newattrs.ia_ctime = current_fs_time(inode->i_sb); ++ newattrs.ia_valid = ATTR_MODE; + rc = sysfs_setattr(victim, &newattrs); + +- if (rc == 0) { +- fsnotify_change(victim, newattrs.ia_valid); +- mutex_lock(&sysfs_mutex); +- victim_sd->s_mode = newattrs.ia_mode; +- mutex_unlock(&sysfs_mutex); +- } +- + mutex_unlock(&inode->i_mutex); + out: + dput(victim); diff --git a/driver-core/sysfs-update-sysfs_setxattr-so-it-updates-secdata-under-the-sysfs_mutex.patch b/driver-core/sysfs-update-sysfs_setxattr-so-it-updates-secdata-under-the-sysfs_mutex.patch new file mode 100644 index 00000000000000..10d3bf42349f12 --- /dev/null +++ b/driver-core/sysfs-update-sysfs_setxattr-so-it-updates-secdata-under-the-sysfs_mutex.patch @@ -0,0 +1,92 @@ +From ebiederm@xmission.com Fri Nov 20 12:06:08 2009 +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Sat, 7 Nov 2009 23:26:59 -0800 +Subject: sysfs: Update sysfs_setxattr so it updates secdata under the sysfs_mutex +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Kay Sievers <kay.sievers@vrfy.org>, Greg KH <greg@kroah.com>, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, Eric Dumazet <eric.dumazet@gmail.com>, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, "Eric W. Biederman" <ebiederm@maxwell.aristanetworks.com>, "Eric W. Biederman" <ebiederm@aristanetworks.com> +Message-ID: <1257665233-12468-1-git-send-email-ebiederm@xmission.com> + + +From: Eric W. Biederman <ebiederm@maxwell.aristanetworks.com> + +The sysfs_mutex is required to ensure updates are and will remain +atomic with respect to other inode iattr updates, that do not happen +through the filesystem. + +Acked-by: Serge Hallyn <serue@us.ibm.com> +Acked-by: Tejun Heo <tj@kernel.org> +Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + fs/sysfs/inode.c | 41 +++++++++++++++++++++++++++++------------ + 1 file changed, 29 insertions(+), 12 deletions(-) + +--- a/fs/sysfs/inode.c ++++ b/fs/sysfs/inode.c +@@ -123,23 +123,39 @@ int sysfs_setattr(struct dentry * dentry + return error; + } + ++static int sysfs_sd_setsecdata(struct sysfs_dirent *sd, void **secdata, u32 *secdata_len) ++{ ++ struct sysfs_inode_attrs *iattrs; ++ void *old_secdata; ++ size_t old_secdata_len; ++ ++ iattrs = sd->s_iattr; ++ if (!iattrs) ++ iattrs = sysfs_init_inode_attrs(sd); ++ if (!iattrs) ++ return -ENOMEM; ++ ++ old_secdata = iattrs->ia_secdata; ++ old_secdata_len = iattrs->ia_secdata_len; ++ ++ iattrs->ia_secdata = *secdata; ++ iattrs->ia_secdata_len = *secdata_len; ++ ++ *secdata = old_secdata; ++ *secdata_len = old_secdata_len; ++ return 0; ++} ++ + int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, + size_t size, int flags) + { + struct sysfs_dirent *sd = dentry->d_fsdata; +- struct sysfs_inode_attrs *iattrs; + void *secdata; + int error; + u32 secdata_len = 0; + + if (!sd) + return -EINVAL; +- if (!sd->s_iattr) +- sd->s_iattr = sysfs_init_inode_attrs(sd); +- if (!sd->s_iattr) +- return -ENOMEM; +- +- iattrs = sd->s_iattr; + + if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) { + const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; +@@ -151,12 +167,13 @@ int sysfs_setxattr(struct dentry *dentry + &secdata, &secdata_len); + if (error) + goto out; +- if (iattrs->ia_secdata) +- security_release_secctx(iattrs->ia_secdata, +- iattrs->ia_secdata_len); +- iattrs->ia_secdata = secdata; +- iattrs->ia_secdata_len = secdata_len; + ++ mutex_lock(&sysfs_mutex); ++ error = sysfs_sd_setsecdata(sd, &secdata, &secdata_len); ++ mutex_unlock(&sysfs_mutex); ++ ++ if (secdata) ++ security_release_secctx(secdata, secdata_len); + } else + return -EINVAL; + out: diff --git a/driver-core/sysfs-use-dentry_ops-instead-of-directly-playing-with-the-dcache.patch b/driver-core/sysfs-use-dentry_ops-instead-of-directly-playing-with-the-dcache.patch new file mode 100644 index 00000000000000..61c3e447baed63 --- /dev/null +++ b/driver-core/sysfs-use-dentry_ops-instead-of-directly-playing-with-the-dcache.patch @@ -0,0 +1,140 @@ +From ebiederm@xmission.com Fri Nov 20 12:07:21 2009 +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Sat, 7 Nov 2009 23:27:01 -0800 +Subject: sysfs: Use dentry_ops instead of directly playing with the dcache +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Kay Sievers <kay.sievers@vrfy.org>, Greg KH <greg@kroah.com>, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, Eric Dumazet <eric.dumazet@gmail.com>, Benjamin LaHaise <bcrl@lhnet.ca>, Serge Hallyn <serue@us.ibm.com>, "Eric W. Biederman" <ebiederm@xmission.com>, "Eric W. Biederman" <ebiederm@aristanetworks.com> +Message-ID: <1257665233-12468-3-git-send-email-ebiederm@xmission.com> + + +From: Eric W. Biederman <ebiederm@xmission.com> + +Calling d_drop unconditionally when a sysfs_dirent is deleted has +the potential to leak mounts, so instead implement dentry delete +and revalidate operations that cause sysfs dentries to be removed +at the appropriate time. + +Acked-by: Tejun Heo <tj@kernel.org> +Acked-by: Serge Hallyn <serue@us.ibm.com> +Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + fs/sysfs/dir.c | 73 +++++++++++++++++++++++++++++++++++---------------------- + 1 file changed, 46 insertions(+), 27 deletions(-) + +--- a/fs/sysfs/dir.c ++++ b/fs/sysfs/dir.c +@@ -298,6 +298,46 @@ void release_sysfs_dirent(struct sysfs_d + goto repeat; + } + ++static int sysfs_dentry_delete(struct dentry *dentry) ++{ ++ struct sysfs_dirent *sd = dentry->d_fsdata; ++ return !!(sd->s_flags & SYSFS_FLAG_REMOVED); ++} ++ ++static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd) ++{ ++ struct sysfs_dirent *sd = dentry->d_fsdata; ++ int is_dir; ++ ++ mutex_lock(&sysfs_mutex); ++ ++ /* The sysfs dirent has been deleted */ ++ if (sd->s_flags & SYSFS_FLAG_REMOVED) ++ goto out_bad; ++ ++ mutex_unlock(&sysfs_mutex); ++out_valid: ++ return 1; ++out_bad: ++ /* Remove the dentry from the dcache hashes. ++ * If this is a deleted dentry we use d_drop instead of d_delete ++ * so sysfs doesn't need to cope with negative dentries. ++ */ ++ is_dir = (sysfs_type(sd) == SYSFS_DIR); ++ mutex_unlock(&sysfs_mutex); ++ if (is_dir) { ++ /* If we have submounts we must allow the vfs caches ++ * to lie about the state of the filesystem to prevent ++ * leaks and other nasty things. ++ */ ++ if (have_submounts(dentry)) ++ goto out_valid; ++ shrink_dcache_parent(dentry); ++ } ++ d_drop(dentry); ++ return 0; ++} ++ + static void sysfs_dentry_iput(struct dentry * dentry, struct inode * inode) + { + struct sysfs_dirent * sd = dentry->d_fsdata; +@@ -307,6 +347,8 @@ static void sysfs_dentry_iput(struct den + } + + static const struct dentry_operations sysfs_dentry_ops = { ++ .d_revalidate = sysfs_dentry_revalidate, ++ .d_delete = sysfs_dentry_delete, + .d_iput = sysfs_dentry_iput, + }; + +@@ -527,44 +569,21 @@ void sysfs_remove_one(struct sysfs_addrm + } + + /** +- * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent ++ * sysfs_dec_nlink - Decrement link count for the specified sysfs_dirent + * @sd: target sysfs_dirent + * +- * Drop dentry for @sd. @sd must have been unlinked from its ++ * Decrement nlink for @sd. @sd must have been unlinked from its + * parent on entry to this function such that it can't be looked + * up anymore. + */ +-static void sysfs_drop_dentry(struct sysfs_dirent *sd) ++static void sysfs_dec_nlink(struct sysfs_dirent *sd) + { + struct inode *inode; +- struct dentry *dentry; + + inode = ilookup(sysfs_sb, sd->s_ino); + if (!inode) + return; + +- /* Drop any existing dentries associated with sd. +- * +- * For the dentry to be properly freed we need to grab a +- * reference to the dentry under the dcache lock, unhash it, +- * and then put it. The playing with the dentry count allows +- * dput to immediately free the dentry if it is not in use. +- */ +-repeat: +- spin_lock(&dcache_lock); +- list_for_each_entry(dentry, &inode->i_dentry, d_alias) { +- if (d_unhashed(dentry)) +- continue; +- dget_locked(dentry); +- spin_lock(&dentry->d_lock); +- __d_drop(dentry); +- spin_unlock(&dentry->d_lock); +- spin_unlock(&dcache_lock); +- dput(dentry); +- goto repeat; +- } +- spin_unlock(&dcache_lock); +- + /* adjust nlink and update timestamp */ + mutex_lock(&inode->i_mutex); + +@@ -611,7 +630,7 @@ void sysfs_addrm_finish(struct sysfs_add + acxt->removed = sd->s_sibling; + sd->s_sibling = NULL; + +- sysfs_drop_dentry(sd); ++ sysfs_dec_nlink(sd); + sysfs_deactivate(sd); + unmap_bin_file(sd); + sysfs_put(sd); @@ -54,6 +54,11 @@ driver-core/driver-core-devtmpfs-set-root-directory-mode-to-0755.patch driver-core/firmware_class-make-request_firmware_nowait-more-useful.patch driver-core/driver-core-don-t-remove-kobjects-in-device_shutdown.patch driver-core/debugfs-fix-create-mutex-racy-fops-and-private-data.patch +driver-core/sysfs-update-sysfs_setxattr-so-it-updates-secdata-under-the-sysfs_mutex.patch +driver-core/sysfs-rename-sysfs_d_iput-to-sysfs_dentry_iput.patch +driver-core/sysfs-use-dentry_ops-instead-of-directly-playing-with-the-dcache.patch +driver-core/sysfs-simplify-sysfs_chmod_file-semantics.patch +driver-core/sysfs-simplify-iattr-time-assignments.patch ################################# @@ -153,6 +158,41 @@ usb/usb-ark3116-callbacks-for-interrupt-and-bulk-read.patch usb/usb-ark3116-cleanup-of-now-unneeded-functions.patch usb/usb-option.c-add-support-for-d-link-dwm-162-u5.patch usb/usb-hcd.c-quiet-null-pointer-sparse-noise.patch +usb/usb-remove-the-auto_pm-flag.patch +usb/usb-r8a66597-clean-up.-remove-unneeded-null-checks.patch +usb/usb-fix-possible-null-deref-in-init_usb_class.patch +usb/usbtest-make-module-param-pattern-writeable.patch +usb/usb-xhci-add-tests-for-trb-address-translation.patch +usb/usb-g_mass_storage-mass-storage-function-created.patch +usb/usb-g_mass_storage-fsg_config-added-module-params-handlig-changed.patch +usb/usb-g_mass_storage-lun_name_format-and-thread_name-added.patch +usb/usb-g_mass_storage-code-cleaned-up-and-comments-updated.patch +usb/usb-g_mass_storage-most-data-moved-to-fsg_common.patch +usb/usb-composite-usb_composite_unregister-no-longer-__exit.patch +usb/usb-g_mass_storage-thread_exits-callback-added.patch +usb/usb-g_multi-multifunction-composite-gadget-added.patch +usb/usb-xhci-set-transfer-descriptor-size-field-correctly.patch +usb/usb-xhci-return-eproto-on-a-split-transaction-error.patch +usb/usb-xhci-return-success-for-vendor-specific-info-codes.patch +usb/usb-xhci-handle-errors-that-cause-endpoint-halts.patch +usb/usb-musb-tweak-musb_read_fifo-to-avoid-unused-warnings.patch +usb/usb-musb-kill-compile-warning-for-blackfin-systems.patch +usb/usb-musb-kill-some-useless-comments-in-blackfin-driver.patch +usb/usb-musb-update-blackfin-processor-dependency.patch +usb/usb-musb-add-notes-for-blackfin-anomalies.patch +usb/usb-musb-add-work-around-for-blackfin-anomaly-05000456.patch +usb/usb-musb-fix-musb_platform_set_mode-definition.patch +usb/usb-musb-clear-the-blackfin-interrupt-pending-bits-early-in-the-isr.patch +usb/usb-musb-error-out-when-anomaly-05000380-is-applicable.patch +usb/usb-musb-blackfin-code-needs-nop_usb_xceiv-too.patch +usb/usb-musb-fix-printf-warning-in-debug-code.patch +usb/usb-musb-save-hardware-revision-at-init.patch +usb/usb-musb_gadget_ep0-fix-unhandled-endpoint-0-irqs-again.patch +usb/usb-musb_gadget-implement-set_wedge-method.patch +usb/usb-musb_gadget_ep0-stop-abusing-musb_gadget_set_halt.patch +usb/usb-musb_gadget-remove-pointless-loop.patch +usb/usb-usbtmc-repeat-usb_bulk_msg-until-whole-message-is-transfered.patch +usb/usb-twl4030-enable-usb-regulators-before-enabling-usb-charging.patch ################################# diff --git a/usb/usb-composite-usb_composite_unregister-no-longer-__exit.patch b/usb/usb-composite-usb_composite_unregister-no-longer-__exit.patch new file mode 100644 index 00000000000000..e7d75f861f9f6e --- /dev/null +++ b/usb/usb-composite-usb_composite_unregister-no-longer-__exit.patch @@ -0,0 +1,45 @@ +From m.nazarewicz@samsung.com Fri Nov 20 12:28:47 2009 +From: Michal Nazarewicz <m.nazarewicz@samsung.com> +Date: Mon, 09 Nov 2009 14:15:25 +0100 +Subject: USB: composite: usb_composite_unregister() no longer __exit +To: Greg KH <greg@kroah.com> +Cc: Michal Nazarewicz <m.nazarewicz@samsung.com> +Message-ID: <1257772527-870-7-git-send-email-m.nazarewicz@samsung.com> + + +From: Michal Nazarewicz <m.nazarewicz@samsung.com> + +Changed definition of usb_composite_unregister() function +removing __exit declaration. This way, the function is +included even if the whole code was not compiled as module. +This is required if a compiled-in code would like to +unregister a composite gadget. + +Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/composite.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/usb/gadget/composite.c ++++ b/drivers/usb/gadget/composite.c +@@ -1092,7 +1092,8 @@ static struct usb_gadget_driver composit + .speed = USB_SPEED_HIGH, + + .bind = composite_bind, +- .unbind = __exit_p(composite_unbind), ++ /* .unbind = __exit_p(composite_unbind), */ ++ .unbind = composite_unbind, + + .setup = composite_setup, + .disconnect = composite_disconnect, +@@ -1141,7 +1142,7 @@ int __init usb_composite_register(struct + * This function is used to unregister drivers using the composite + * driver framework. + */ +-void __exit usb_composite_unregister(struct usb_composite_driver *driver) ++void /* __exit */ usb_composite_unregister(struct usb_composite_driver *driver) + { + if (composite != driver) + return; diff --git a/usb/usb-fix-possible-null-deref-in-init_usb_class.patch b/usb/usb-fix-possible-null-deref-in-init_usb_class.patch new file mode 100644 index 00000000000000..e369251ae2bc8e --- /dev/null +++ b/usb/usb-fix-possible-null-deref-in-init_usb_class.patch @@ -0,0 +1,28 @@ +From error27@gmail.com Fri Nov 20 11:55:00 2009 +From: Dan Carpenter <error27@gmail.com> +Date: Tue, 10 Nov 2009 11:02:08 +0200 (SAST) +Subject: USB: fix possible null deref in init_usb_class() +Cc: gregkh@suse.de +Message-ID: <alpine.DEB.2.00.0911071737130.19646@bicker> + +Add a missing goto. We dereference usb_class on the next line. + +Found by smatch static checker. + +Signed-off-by: Dan Carpenter <error27@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/core/file.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/usb/core/file.c ++++ b/drivers/usb/core/file.c +@@ -99,6 +99,7 @@ static int init_usb_class(void) + printk(KERN_ERR "class_create failed for usb devices\n"); + kfree(usb_class); + usb_class = NULL; ++ goto exit; + } + usb_class->class->devnode = usb_devnode; + diff --git a/usb/usb-g_mass_storage-code-cleaned-up-and-comments-updated.patch b/usb/usb-g_mass_storage-code-cleaned-up-and-comments-updated.patch new file mode 100644 index 00000000000000..4b418039dad910 --- /dev/null +++ b/usb/usb-g_mass_storage-code-cleaned-up-and-comments-updated.patch @@ -0,0 +1,1453 @@ +From m.nazarewicz@samsung.com Fri Nov 20 12:28:24 2009 +From: Michal Nazarewicz <m.nazarewicz@samsung.com> +Date: Mon, 09 Nov 2009 14:15:23 +0100 +Subject: USB: g_mass_storage: code cleaned up and comments updated +To: Greg KH <greg@kroah.com> +Cc: Michal Nazarewicz <m.nazarewicz@samsung.com> +Message-ID: <1257772527-870-5-git-send-email-m.nazarewicz@samsung.com> + + +From: Michal Nazarewicz <m.nazarewicz@samsung.com> + +Fixed most of the errors and warnings in f_mass_storage.c and +storage_common.c reported by checkpatch.pl as well as updated +comments. + +Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/f_mass_storage.c | 575 ++++++++++++++++++++---------------- + drivers/usb/gadget/mass_storage.c | 6 + drivers/usb/gadget/storage_common.c | 108 +++--- + 3 files changed, 391 insertions(+), 298 deletions(-) + +--- a/drivers/usb/gadget/f_mass_storage.c ++++ b/drivers/usb/gadget/f_mass_storage.c +@@ -1,7 +1,9 @@ + /* +- * file_storage.c -- File-backed USB Storage Gadget, for USB development ++ * f_mass_storage.c -- Mass Storage USB Composite Function + * + * Copyright (C) 2003-2008 Alan Stern ++ * Copyright (C) 2009 Samsung Electronics ++ * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without +@@ -37,37 +39,112 @@ + + + /* +- * The File-backed Storage Gadget acts as a USB Mass Storage device, +- * appearing to the host as a disk drive or as a CD-ROM drive. In addition +- * to providing an example of a genuinely useful gadget driver for a USB +- * device, it also illustrates a technique of double-buffering for increased +- * throughput. Last but not least, it gives an easy way to probe the +- * behavior of the Mass Storage drivers in a USB host. +- * +- * Backing storage is provided by a regular file or a block device, specified +- * by the "file" module parameter. Access can be limited to read-only by +- * setting the optional "ro" module parameter. (For CD-ROM emulation, +- * access is always read-only.) The gadget will indicate that it has +- * removable media if the optional "removable" module parameter is set. +- * +- * There is support for multiple logical units (LUNs), each of which has +- * its own backing file. The number of LUNs can be set using the optional +- * "luns" module parameter (anywhere from 1 to 8), and the corresponding +- * files are specified using comma-separated lists for "file" and "ro". +- * The default number of LUNs is taken from the number of "file" elements; +- * it is 1 if "file" is not given. If "removable" is not set then a backing +- * file must be specified for each LUN. If it is set, then an unspecified +- * or empty backing filename means the LUN's medium is not loaded. Ideally +- * each LUN would be settable independently as a disk drive or a CD-ROM +- * drive, but currently all LUNs have to be the same type. The CD-ROM +- * emulation includes a single data track and no audio tracks; hence there +- * need be only one backing file per LUN. Note also that the CD-ROM block +- * length is set to 512 rather than the more common value 2048. ++ * The Mass Storage Function acts as a USB Mass Storage device, ++ * appearing to the host as a disk drive or as a CD-ROM drive. In ++ * addition to providing an example of a genuinely useful composite ++ * function for a USB device, it also illustrates a technique of ++ * double-buffering for increased throughput. ++ * ++ * Function supports multiple logical units (LUNs). Backing storage ++ * for each LUN is provided by a regular file or a block device. ++ * Access for each LUN can be limited to read-only. Moreover, the ++ * function can indicate that LUN is removable and/or CD-ROM. (The ++ * later implies read-only access.) ++ * ++ * MSF is configured by specifying a fsg_config structure. It has the ++ * following fields: ++ * ++ * nluns Number of LUNs function have (anywhere from 1 ++ * to FSG_MAX_LUNS which is 8). ++ * luns An array of LUN configuration values. This ++ * should be filled for each LUN that ++ * function will include (ie. for "nluns" ++ * LUNs). Each element of the array has ++ * the following fields: ++ * ->filename The path to the backing file for the LUN. ++ * Required if LUN is not marked as ++ * removable. ++ * ->ro Flag specifying access to the LUN shall be ++ * read-only. This is implied if CD-ROM ++ * emulation is enabled as well as when ++ * it was impossible to open "filename" ++ * in R/W mode. ++ * ->removable Flag specifying that LUN shall be indicated as ++ * being removable. ++ * ->cdrom Flag specifying that LUN shall be reported as ++ * being a CD-ROM. ++ * ++ * lun_name_format A printf-like format for names of the LUN ++ * devices. This determines how the ++ * directory in sysfs will be named. ++ * Unless you are using several MSFs in ++ * a single gadget (as opposed to single ++ * MSF in many configurations) you may ++ * leave it as NULL (in which case ++ * "lun%d" will be used). In the format ++ * you can use "%d" to index LUNs for ++ * MSF's with more than one LUN. (Beware ++ * that there is only one integer given ++ * as an argument for the format and ++ * specifying invalid format may cause ++ * unspecified behaviour.) ++ * thread_name Name of the kernel thread process used by the ++ * MSF. You can safely set it to NULL ++ * (in which case default "file-storage" ++ * will be used). ++ * ++ * vendor_name ++ * product_name ++ * release Information used as a reply to INQUIRY ++ * request. To use default set to NULL, ++ * NULL, 0xffff respectively. The first ++ * field should be 8 and the second 16 ++ * characters or less. ++ * ++ * can_stall Set to permit function to halt bulk endpoints. ++ * Disabled on some USB devices known not ++ * to work correctly. You should set it ++ * to true. ++ * ++ * If "removable" is not set for a LUN then a backing file must be ++ * specified. If it is set, then NULL filename means the LUN's medium ++ * is not loaded (an empty string as "filename" in the fsg_config ++ * structure causes error). The CD-ROM emulation includes a single ++ * data track and no audio tracks; hence there need be only one ++ * backing file per LUN. Note also that the CD-ROM block length is ++ * set to 512 rather than the more common value 2048. ++ * ++ * ++ * MSF includes support for module parameters. If gadget using it ++ * decides to use it, the following module parameters will be ++ * available: ++ * ++ * file=filename[,filename...] ++ * Names of the files or block devices used for ++ * backing storage. ++ * ro=b[,b...] Default false, boolean for read-only access. ++ * removable=b[,b...] ++ * Default true, boolean for removable media. ++ * cdrom=b[,b...] Default false, boolean for whether to emulate ++ * a CD-ROM drive. ++ * luns=N Default N = number of filenames, number of ++ * LUNs to support. ++ * stall Default determined according to the type of ++ * USB device controller (usually true), ++ * boolean to permit the driver to halt ++ * bulk endpoints. ++ * ++ * The module parameters may be prefixed with some string. You need ++ * to consult gadget's documentation or source to verify whether it is ++ * using those module parameters and if it does what are the prefixes ++ * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is ++ * the prefix). ++ * + * + * Requirements are modest; only a bulk-in and a bulk-out endpoint are +- * needed (an interrupt-out endpoint is also needed for CBI). The memory +- * requirement amounts to two 16K buffers, size configurable by a parameter. +- * Support is included for both full-speed and high-speed operation. ++ * needed. The memory requirement amounts to two 16K buffers, size ++ * configurable by a parameter. Support is included for both ++ * full-speed and high-speed operation. + * + * Note that the driver is slightly non-portable in that it assumes a + * single memory/DMA buffer will be useable for bulk-in, bulk-out, and +@@ -75,39 +152,28 @@ + * issue, but there may be some with hardware restrictions that prevent + * a buffer from being used by more than one endpoint. + * +- * Module options: + * +- * file=filename[,filename...] +- * Required if "removable" is not set, names of +- * the files or block devices used for +- * backing storage +- * ro=b[,b...] Default false, booleans for read-only access +- * removable Default false, boolean for removable media +- * luns=N Default N = number of filenames, number of +- * LUNs to support +- * stall Default determined according to the type of +- * USB device controller (usually true), +- * boolean to permit the driver to halt +- * bulk endpoints +- * cdrom Default false, boolean for whether to emulate +- * a CD-ROM drive +- * +- * The pathnames of the backing files and the ro settings are available in +- * the attribute files "file" and "ro" in the lun<n> subdirectory of the +- * gadget's sysfs directory. If the "removable" option is set, writing to +- * these files will simulate ejecting/loading the medium (writing an empty +- * line means eject) and adjusting a write-enable tab. Changes to the ro +- * setting are not allowed when the medium is loaded or if CD-ROM emulation +- * is being used. +- * +- * This gadget driver is heavily based on "Gadget Zero" by David Brownell. +- * The driver's SCSI command interface was based on the "Information +- * technology - Small Computer System Interface - 2" document from +- * X3T9.2 Project 375D, Revision 10L, 7-SEP-93, available at +- * <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>. The single exception +- * is opcode 0x23 (READ FORMAT CAPACITIES), which was based on the +- * "Universal Serial Bus Mass Storage Class UFI Command Specification" +- * document, Revision 1.0, December 14, 1998, available at ++ * The pathnames of the backing files and the ro settings are ++ * available in the attribute files "file" and "ro" in the lun<n> (or ++ * to be more precise in a directory which name comes from ++ * "lun_name_format" option!) subdirectory of the gadget's sysfs ++ * directory. If the "removable" option is set, writing to these ++ * files will simulate ejecting/loading the medium (writing an empty ++ * line means eject) and adjusting a write-enable tab. Changes to the ++ * ro setting are not allowed when the medium is loaded or if CD-ROM ++ * emulation is being used. ++ * ++ * ++ * This function is heavily based on "File-backed Storage Gadget" by ++ * Alan Stern which in turn is heavily based on "Gadget Zero" by David ++ * Brownell. The driver's SCSI command interface was based on the ++ * "Information technology - Small Computer System Interface - 2" ++ * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93, ++ * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>. ++ * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which ++ * was based on the "Universal Serial Bus Mass Storage Class UFI ++ * Command Specification" document, Revision 1.0, December 14, 1998, ++ * available at + * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>. + */ + +@@ -115,7 +181,7 @@ + /* + * Driver Design + * +- * The FSG driver is fairly straightforward. There is a main kernel ++ * The MSF is fairly straightforward. There is a main kernel + * thread that handles most of the work. Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. +@@ -138,17 +204,11 @@ + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's +- * fsg_bind() callback and stopped during fsg_unbind(). But it can also +- * exit when it receives a signal, and there's no point leaving the +- * gadget running when the thread is dead. So just before the thread +- * exits, it deregisters the gadget driver. This makes things a little +- * tricky: The driver is deregistered at two places, and the exiting +- * thread can indirectly call fsg_unbind() which in turn can tell the +- * thread to exit. The first problem is resolved through the use of the +- * REGISTERED atomic bitflag; the driver will only be deregistered once. +- * The second problem is resolved by having fsg_unbind() check +- * fsg->state; it won't try to stop the thread if the state is already +- * FSG_STATE_TERMINATED. ++ * fsg_bind() callback and stopped during fsg_unbind(). But it can ++ * also exit when it receives a signal, and there's no point leaving ++ * the gadget running when the thread is dead. At of this moment, MSF ++ * provides no way to deregister the gadget when thread dies -- maybe ++ * a callback functions is needed. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsg_buffhd). In principle the pipeline can be +@@ -236,7 +296,7 @@ + /*------------------------------------------------------------------------*/ + + #define FSG_DRIVER_DESC "Mass Storage Function" +-#define FSG_DRIVER_VERSION "20 November 2008" ++#define FSG_DRIVER_VERSION "2009/09/11" + + static const char fsg_string_interface[] = "Mass Storage"; + +@@ -307,7 +367,7 @@ struct fsg_config { + + struct fsg_dev { + struct usb_function function; +- struct usb_composite_dev*cdev; ++ struct usb_composite_dev *cdev; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ + struct fsg_common *common; + +@@ -322,18 +382,18 @@ struct fsg_dev { + const char *ep0req_name; + + unsigned int bulk_out_maxpacket; +- enum fsg_state state; // For exception handling ++ enum fsg_state state; /* For exception handling */ + unsigned int exception_req_tag; + + u8 config, new_config; + +- unsigned int running : 1; +- unsigned int bulk_in_enabled : 1; +- unsigned int bulk_out_enabled : 1; +- unsigned int phase_error : 1; +- unsigned int short_packet_received : 1; +- unsigned int bad_lun_okay : 1; +- unsigned int can_stall : 1; ++ unsigned int running:1; ++ unsigned int bulk_in_enabled:1; ++ unsigned int bulk_out_enabled:1; ++ unsigned int phase_error:1; ++ unsigned int short_packet_received:1; ++ unsigned int bad_lun_okay:1; ++ unsigned int can_stall:1; + + unsigned long atomic_bitflags; + #define REGISTERED 0 +@@ -461,7 +521,7 @@ static void bulk_in_complete(struct usb_ + if (req->status || req->actual != req->length) + DBG(fsg, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); +- if (req->status == -ECONNRESET) // Request was cancelled ++ if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ +@@ -483,7 +543,7 @@ static void bulk_out_complete(struct usb + DBG(fsg, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, + bh->bulk_out_intended_length); +- if (req->status == -ECONNRESET) // Request was cancelled ++ if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ +@@ -643,7 +703,7 @@ static int do_read(struct fsg_dev *fsg) + /* Carry out the file reads */ + amount_left = fsg->data_size_from_cmnd; + if (unlikely(amount_left == 0)) +- return -EIO; // No default reply ++ return -EIO; /* No default reply */ + + for (;;) { + +@@ -701,7 +761,7 @@ static int do_read(struct fsg_dev *fsg) + } else if (nread < amount) { + LDBG(curlun, "partial file read: %d/%u\n", + (int) nread, amount); +- nread -= (nread & 511); // Round down to a block ++ nread -= (nread & 511); /* Round down to a block */ + } + file_offset += nread; + amount_left -= nread; +@@ -718,7 +778,7 @@ static int do_read(struct fsg_dev *fsg) + } + + if (amount_left == 0) +- break; // No more left to read ++ break; /* No more left to read */ + + /* Send this buffer and go read some more */ + bh->inreq->zero = 0; +@@ -727,7 +787,7 @@ static int do_read(struct fsg_dev *fsg) + fsg->common->next_buffhd_to_fill = bh->next; + } + +- return -EIO; // No default reply ++ return -EIO; /* No default reply */ + } + + +@@ -751,7 +811,7 @@ static int do_write(struct fsg_dev *fsg) + return -EINVAL; + } + spin_lock(&curlun->filp->f_lock); +- curlun->filp->f_flags &= ~O_SYNC; // Default is not to wait ++ curlun->filp->f_flags &= ~O_SYNC; /* Default is not to wait */ + spin_unlock(&curlun->filp->f_lock); + + /* Get the starting Logical Block Address and check that it's +@@ -769,7 +829,7 @@ static int do_write(struct fsg_dev *fsg) + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } +- if (fsg->common->cmnd[1] & 0x08) { // FUA ++ if (fsg->common->cmnd[1] & 0x08) { /* FUA */ + spin_lock(&curlun->filp->f_lock); + curlun->filp->f_flags |= O_SYNC; + spin_unlock(&curlun->filp->f_lock); +@@ -834,8 +894,8 @@ static int do_write(struct fsg_dev *fsg) + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ +- bh->outreq->length = bh->bulk_out_intended_length = +- amount; ++ bh->outreq->length = amount; ++ bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + start_transfer(fsg, fsg->bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state); +@@ -846,7 +906,7 @@ static int do_write(struct fsg_dev *fsg) + /* Write the received data to the backing file */ + bh = fsg->common->next_buffhd_to_drain; + if (bh->state == BUF_STATE_EMPTY && !get_some_more) +- break; // We stopped early ++ break; /* We stopped early */ + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + fsg->common->next_buffhd_to_drain = bh->next; +@@ -878,7 +938,7 @@ static int do_write(struct fsg_dev *fsg) + (unsigned long long) file_offset, + (int) nwritten); + if (signal_pending(current)) +- return -EINTR; // Interrupted! ++ return -EINTR; /* Interrupted! */ + + if (nwritten < 0) { + LDBG(curlun, "error in file write: %d\n", +@@ -888,7 +948,7 @@ static int do_write(struct fsg_dev *fsg) + LDBG(curlun, "partial file write: %d/%u\n", + (int) nwritten, amount); + nwritten -= (nwritten & 511); +- // Round down to a block ++ /* Round down to a block */ + } + file_offset += nwritten; + amount_left_to_write -= nwritten; +@@ -916,7 +976,7 @@ static int do_write(struct fsg_dev *fsg) + return rc; + } + +- return -EIO; // No default reply ++ return -EIO; /* No default reply */ + } + + +@@ -976,7 +1036,7 @@ static int do_verify(struct fsg_dev *fsg + + verification_length = get_unaligned_be16(&fsg->common->cmnd[7]); + if (unlikely(verification_length == 0)) +- return -EIO; // No default reply ++ return -EIO; /* No default reply */ + + /* Prepare to carry out the file verify */ + amount_left = verification_length << 9; +@@ -1029,7 +1089,7 @@ static int do_verify(struct fsg_dev *fsg + } else if (nread < amount) { + LDBG(curlun, "partial file verify: %d/%u\n", + (int) nread, amount); +- nread -= (nread & 511); // Round down to a sector ++ nread -= (nread & 511); /* Round down to a sector */ + } + if (nread == 0) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; +@@ -1054,17 +1114,17 @@ static int do_inquiry(struct fsg_dev *fs + if (!curlun) { /* Unsupported LUNs are okay */ + fsg->bad_lun_okay = 1; + memset(buf, 0, 36); +- buf[0] = 0x7f; // Unsupported, no device-type +- buf[4] = 31; // Additional length ++ buf[0] = 0x7f; /* Unsupported, no device-type */ ++ buf[4] = 31; /* Additional length */ + return 36; + } + + buf[0] = curlun->cdrom ? TYPE_CDROM : TYPE_DISK; + buf[1] = curlun->removable ? 0x80 : 0; +- buf[2] = 2; // ANSI SCSI level 2 +- buf[3] = 2; // SCSI-2 INQUIRY data format +- buf[4] = 31; // Additional length +- buf[5] = 0; // No special options ++ buf[2] = 2; /* ANSI SCSI level 2 */ ++ buf[3] = 2; /* SCSI-2 INQUIRY data format */ ++ buf[4] = 31; /* Additional length */ ++ buf[5] = 0; /* No special options */ + buf[6] = 0; + buf[7] = 0; + memcpy(buf + 8, fsg->common->inquiry_string, +@@ -1102,7 +1162,7 @@ static int do_request_sense(struct fsg_d + } + #endif + +- if (!curlun) { // Unsupported LUNs are okay ++ if (!curlun) { /* Unsupported LUNs are okay */ + fsg->bad_lun_okay = 1; + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + sdinfo = 0; +@@ -1117,10 +1177,10 @@ static int do_request_sense(struct fsg_d + } + + memset(buf, 0, 18); +- buf[0] = valid | 0x70; // Valid, current error ++ buf[0] = valid | 0x70; /* Valid, current error */ + buf[2] = SK(sd); + put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */ +- buf[7] = 18 - 8; // Additional sense length ++ buf[7] = 18 - 8; /* Additional sense length */ + buf[12] = ASC(sd); + buf[13] = ASCQ(sd); + return 18; +@@ -1209,7 +1269,7 @@ static int do_mode_sense(struct fsg_dev + int valid_page = 0; + int len, limit; + +- if ((fsg->common->cmnd[1] & ~0x08) != 0) { // Mask away DBD ++ if ((fsg->common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } +@@ -1228,13 +1288,13 @@ static int do_mode_sense(struct fsg_dev + * the mode data length later. */ + memset(buf, 0, 8); + if (mscmnd == SC_MODE_SENSE_6) { +- buf[2] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA ++ buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 4; + limit = 255; +- } else { // SC_MODE_SENSE_10 +- buf[3] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA ++ } else { /* SC_MODE_SENSE_10 */ ++ buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 8; +- limit = 65535; // Should really be FSG_BUFLEN ++ limit = 65535; /* Should really be FSG_BUFLEN */ + } + + /* No block descriptors */ +@@ -1243,14 +1303,14 @@ static int do_mode_sense(struct fsg_dev + * is the Caching page. */ + if (page_code == 0x08 || all_pages) { + valid_page = 1; +- buf[0] = 0x08; // Page code +- buf[1] = 10; // Page length +- memset(buf+2, 0, 10); // None of the fields are changeable ++ buf[0] = 0x08; /* Page code */ ++ buf[1] = 10; /* Page length */ ++ memset(buf+2, 0, 10); /* None of the fields are changeable */ + + if (!changeable_values) { +- buf[2] = 0x04; // Write cache enable, +- // Read cache not disabled +- // No cache retention priorities ++ buf[2] = 0x04; /* Write cache enable, */ ++ /* Read cache not disabled */ ++ /* No cache retention priorities */ + put_unaligned_be16(0xffff, &buf[4]); + /* Don't disable prefetch */ + /* Minimum prefetch = 0 */ +@@ -1304,7 +1364,7 @@ static int do_prevent_allow(struct fsg_d + } + + prevent = fsg->common->cmnd[4] & 0x01; +- if ((fsg->common->cmnd[4] & ~0x01) != 0) { // Mask away Prevent ++ if ((fsg->common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } +@@ -1323,7 +1383,7 @@ static int do_read_format_capacities(str + u8 *buf = (u8 *) bh->buf; + + buf[0] = buf[1] = buf[2] = 0; +- buf[3] = 8; // Only the Current/Maximum Capacity Descriptor ++ buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */ + buf += 4; + + put_unaligned_be32(curlun->num_sectors, &buf[0]); +@@ -1398,7 +1458,7 @@ static int pad_with_zeros(struct fsg_dev + u32 nsend; + int rc; + +- bh->state = BUF_STATE_EMPTY; // For the first iteration ++ bh->state = BUF_STATE_EMPTY; /* For the first iteration */ + fsg->usb_amount_left = nkeep + fsg->residue; + while (fsg->usb_amount_left > 0) { + +@@ -1454,8 +1514,8 @@ static int throw_away_data(struct fsg_de + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ +- bh->outreq->length = bh->bulk_out_intended_length = +- amount; ++ bh->outreq->length = amount; ++ bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + start_transfer(fsg, fsg->bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state); +@@ -1480,7 +1540,7 @@ static int finish_reply(struct fsg_dev * + + switch (fsg->data_dir) { + case DATA_DIR_NONE: +- break; // Nothing to send ++ break; /* Nothing to send */ + + /* If we don't know whether the host wants to read or write, + * this must be CB or CBI with an unknown command. We mustn't +@@ -1522,14 +1582,13 @@ static int finish_reply(struct fsg_dev * + /* We have processed all we want from the data the host has sent. + * There may still be outstanding bulk-out requests. */ + case DATA_DIR_FROM_HOST: +- if (fsg->residue == 0) +- ; // Nothing to receive ++ if (fsg->residue == 0) { ++ /* Nothing to receive */ + + /* Did the host stop sending unexpectedly early? */ +- else if (fsg->short_packet_received) { ++ } else if (fsg->short_packet_received) { + raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; +- } + + /* We haven't processed all the incoming data. Even though + * we may be allowed to stall, doing so would cause a race. +@@ -1538,17 +1597,17 @@ static int finish_reply(struct fsg_dev * + * STALL. Not realizing the endpoint was halted, it wouldn't + * clear the halt -- leading to problems later on. */ + #if 0 +- else if (fsg->can_stall) { ++ } else if (fsg->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; +- } + #endif + + /* We can't stall. Read in the excess data and throw it + * all away. */ +- else ++ } else { + rc = throw_away_data(fsg); ++ } + break; + } + return rc; +@@ -1593,7 +1652,7 @@ static int send_status(struct fsg_dev *f + } + + /* Store and send the Bulk-only CSW */ +- csw = (void*)bh->buf; ++ csw = (void *)bh->buf; + + csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); + csw->Tag = fsg->tag; +@@ -1629,18 +1688,18 @@ static int check_command(struct fsg_dev + sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir], + fsg->data_size); + VDBG(fsg, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", +- name, cmnd_size, dirletter[(int) data_dir], +- fsg->data_size_from_cmnd, fsg->common->cmnd_size, hdlen); ++ name, cmnd_size, dirletter[(int) data_dir], ++ fsg->data_size_from_cmnd, fsg->common->cmnd_size, hdlen); + + /* We can't reply at all until we know the correct data direction + * and size. */ + if (fsg->data_size_from_cmnd == 0) + data_dir = DATA_DIR_NONE; +- if (fsg->data_dir == DATA_DIR_UNKNOWN) { // CB or CBI ++ if (fsg->data_dir == DATA_DIR_UNKNOWN) { /* CB or CBI */ + fsg->data_dir = data_dir; + fsg->data_size = fsg->data_size_from_cmnd; + +- } else { // Bulk-only ++ } else { /* Bulk-only */ + if (fsg->data_size < fsg->data_size_from_cmnd) { + + /* Host data size < Device data size is a phase error. +@@ -1691,7 +1750,8 @@ static int check_command(struct fsg_dev + + /* Check the LUN */ + if (fsg->common->lun >= 0 && fsg->common->lun < fsg->common->nluns) { +- fsg->common->curlun = curlun = &fsg->common->luns[fsg->common->lun]; ++ curlun = &fsg->common->luns[fsg->common->lun]; ++ fsg->common->curlun = curlun; + if (fsg->common->cmnd[0] != SC_REQUEST_SENSE) { + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; +@@ -1721,7 +1781,7 @@ static int check_command(struct fsg_dev + } + + /* Check that only command bytes listed in the mask are non-zero */ +- fsg->common->cmnd[1] &= 0x1f; // Mask away the LUN ++ fsg->common->cmnd[1] &= 0x1f; /* Mask away the LUN */ + for (i = 1; i < cmnd_size; ++i) { + if (fsg->common->cmnd[i] && !(mask & (1 << i))) { + if (curlun) +@@ -1752,7 +1812,8 @@ static int do_scsi_command(struct fsg_de + dump_cdb(fsg->common); + + /* Wait for the next buffer to become available for data or status */ +- bh = fsg->common->next_buffhd_to_drain = fsg->common->next_buffhd_to_fill; ++ bh = fsg->common->next_buffhd_to_fill; ++ fsg->common->next_buffhd_to_drain = bh; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg); + if (rc) +@@ -1761,141 +1822,163 @@ static int do_scsi_command(struct fsg_de + fsg->phase_error = 0; + fsg->short_packet_received = 0; + +- down_read(&fsg->common->filesem); // We're using the backing file ++ /* We're using the backing file */ ++ down_read(&fsg->common->filesem); + switch (fsg->common->cmnd[0]) { + + case SC_INQUIRY: + fsg->data_size_from_cmnd = fsg->common->cmnd[4]; +- if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, +- (1<<4), 0, +- "INQUIRY")) == 0) ++ reply = check_command(fsg, 6, DATA_DIR_TO_HOST, ++ (1<<4), 0, ++ "INQUIRY"); ++ if (reply == 0) + reply = do_inquiry(fsg, bh); + break; + + case SC_MODE_SELECT_6: + fsg->data_size_from_cmnd = fsg->common->cmnd[4]; +- if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, +- (1<<1) | (1<<4), 0, +- "MODE SELECT(6)")) == 0) ++ reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, ++ (1<<1) | (1<<4), 0, ++ "MODE SELECT(6)"); ++ if (reply == 0) + reply = do_mode_select(fsg, bh); + break; + + case SC_MODE_SELECT_10: +- fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); +- if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, +- (1<<1) | (3<<7), 0, +- "MODE SELECT(10)")) == 0) ++ fsg->data_size_from_cmnd = ++ get_unaligned_be16(&fsg->common->cmnd[7]); ++ reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, ++ (1<<1) | (3<<7), 0, ++ "MODE SELECT(10)"); ++ if (reply == 0) + reply = do_mode_select(fsg, bh); + break; + + case SC_MODE_SENSE_6: + fsg->data_size_from_cmnd = fsg->common->cmnd[4]; +- if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, +- (1<<1) | (1<<2) | (1<<4), 0, +- "MODE SENSE(6)")) == 0) ++ reply = check_command(fsg, 6, DATA_DIR_TO_HOST, ++ (1<<1) | (1<<2) | (1<<4), 0, ++ "MODE SENSE(6)"); ++ if (reply == 0) + reply = do_mode_sense(fsg, bh); + break; + + case SC_MODE_SENSE_10: +- fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); +- if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, +- (1<<1) | (1<<2) | (3<<7), 0, +- "MODE SENSE(10)")) == 0) ++ fsg->data_size_from_cmnd = ++ get_unaligned_be16(&fsg->common->cmnd[7]); ++ reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ (1<<1) | (1<<2) | (3<<7), 0, ++ "MODE SENSE(10)"); ++ if (reply == 0) + reply = do_mode_sense(fsg, bh); + break; + + case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: + fsg->data_size_from_cmnd = 0; +- if ((reply = check_command(fsg, 6, DATA_DIR_NONE, +- (1<<4), 0, +- "PREVENT-ALLOW MEDIUM REMOVAL")) == 0) ++ reply = check_command(fsg, 6, DATA_DIR_NONE, ++ (1<<4), 0, ++ "PREVENT-ALLOW MEDIUM REMOVAL"); ++ if (reply == 0) + reply = do_prevent_allow(fsg); + break; + + case SC_READ_6: + i = fsg->common->cmnd[4]; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; +- if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, +- (7<<1) | (1<<4), 1, +- "READ(6)")) == 0) ++ reply = check_command(fsg, 6, DATA_DIR_TO_HOST, ++ (7<<1) | (1<<4), 1, ++ "READ(6)"); ++ if (reply == 0) + reply = do_read(fsg); + break; + + case SC_READ_10: + fsg->data_size_from_cmnd = + get_unaligned_be16(&fsg->common->cmnd[7]) << 9; +- if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, +- (1<<1) | (0xf<<2) | (3<<7), 1, +- "READ(10)")) == 0) ++ reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ (1<<1) | (0xf<<2) | (3<<7), 1, ++ "READ(10)"); ++ if (reply == 0) + reply = do_read(fsg); + break; + + case SC_READ_12: + fsg->data_size_from_cmnd = + get_unaligned_be32(&fsg->common->cmnd[6]) << 9; +- if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST, +- (1<<1) | (0xf<<2) | (0xf<<6), 1, +- "READ(12)")) == 0) ++ reply = check_command(fsg, 12, DATA_DIR_TO_HOST, ++ (1<<1) | (0xf<<2) | (0xf<<6), 1, ++ "READ(12)"); ++ if (reply == 0) + reply = do_read(fsg); + break; + + case SC_READ_CAPACITY: + fsg->data_size_from_cmnd = 8; +- if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, +- (0xf<<2) | (1<<8), 1, +- "READ CAPACITY")) == 0) ++ reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ (0xf<<2) | (1<<8), 1, ++ "READ CAPACITY"); ++ if (reply == 0) + reply = do_read_capacity(fsg, bh); + break; + + case SC_READ_HEADER: + if (!fsg->common->curlun || !fsg->common->curlun->cdrom) + goto unknown_cmnd; +- fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); +- if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, +- (3<<7) | (0x1f<<1), 1, +- "READ HEADER")) == 0) ++ fsg->data_size_from_cmnd = ++ get_unaligned_be16(&fsg->common->cmnd[7]); ++ reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ (3<<7) | (0x1f<<1), 1, ++ "READ HEADER"); ++ if (reply == 0) + reply = do_read_header(fsg, bh); + break; + + case SC_READ_TOC: + if (!fsg->common->curlun || !fsg->common->curlun->cdrom) + goto unknown_cmnd; +- fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); +- if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, +- (7<<6) | (1<<1), 1, +- "READ TOC")) == 0) ++ fsg->data_size_from_cmnd = ++ get_unaligned_be16(&fsg->common->cmnd[7]); ++ reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ (7<<6) | (1<<1), 1, ++ "READ TOC"); ++ if (reply == 0) + reply = do_read_toc(fsg, bh); + break; + + case SC_READ_FORMAT_CAPACITIES: +- fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); +- if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, +- (3<<7), 1, +- "READ FORMAT CAPACITIES")) == 0) ++ fsg->data_size_from_cmnd = ++ get_unaligned_be16(&fsg->common->cmnd[7]); ++ reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ (3<<7), 1, ++ "READ FORMAT CAPACITIES"); ++ if (reply == 0) + reply = do_read_format_capacities(fsg, bh); + break; + + case SC_REQUEST_SENSE: + fsg->data_size_from_cmnd = fsg->common->cmnd[4]; +- if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, +- (1<<4), 0, +- "REQUEST SENSE")) == 0) ++ reply = check_command(fsg, 6, DATA_DIR_TO_HOST, ++ (1<<4), 0, ++ "REQUEST SENSE"); ++ if (reply == 0) + reply = do_request_sense(fsg, bh); + break; + + case SC_START_STOP_UNIT: + fsg->data_size_from_cmnd = 0; +- if ((reply = check_command(fsg, 6, DATA_DIR_NONE, +- (1<<1) | (1<<4), 0, +- "START-STOP UNIT")) == 0) ++ reply = check_command(fsg, 6, DATA_DIR_NONE, ++ (1<<1) | (1<<4), 0, ++ "START-STOP UNIT"); ++ if (reply == 0) + reply = do_start_stop(fsg); + break; + + case SC_SYNCHRONIZE_CACHE: + fsg->data_size_from_cmnd = 0; +- if ((reply = check_command(fsg, 10, DATA_DIR_NONE, +- (0xf<<2) | (3<<7), 1, +- "SYNCHRONIZE CACHE")) == 0) ++ reply = check_command(fsg, 10, DATA_DIR_NONE, ++ (0xf<<2) | (3<<7), 1, ++ "SYNCHRONIZE CACHE"); ++ if (reply == 0) + reply = do_synchronize_cache(fsg); + break; + +@@ -1910,36 +1993,40 @@ static int do_scsi_command(struct fsg_de + * support a minimal version: BytChk must be 0. */ + case SC_VERIFY: + fsg->data_size_from_cmnd = 0; +- if ((reply = check_command(fsg, 10, DATA_DIR_NONE, +- (1<<1) | (0xf<<2) | (3<<7), 1, +- "VERIFY")) == 0) ++ reply = check_command(fsg, 10, DATA_DIR_NONE, ++ (1<<1) | (0xf<<2) | (3<<7), 1, ++ "VERIFY"); ++ if (reply == 0) + reply = do_verify(fsg); + break; + + case SC_WRITE_6: + i = fsg->common->cmnd[4]; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; +- if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, +- (7<<1) | (1<<4), 1, +- "WRITE(6)")) == 0) ++ reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, ++ (7<<1) | (1<<4), 1, ++ "WRITE(6)"); ++ if (reply == 0) + reply = do_write(fsg); + break; + + case SC_WRITE_10: + fsg->data_size_from_cmnd = + get_unaligned_be16(&fsg->common->cmnd[7]) << 9; +- if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, +- (1<<1) | (0xf<<2) | (3<<7), 1, +- "WRITE(10)")) == 0) ++ reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, ++ (1<<1) | (0xf<<2) | (3<<7), 1, ++ "WRITE(10)"); ++ if (reply == 0) + reply = do_write(fsg); + break; + + case SC_WRITE_12: + fsg->data_size_from_cmnd = + get_unaligned_be32(&fsg->common->cmnd[6]) << 9; +- if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, +- (1<<1) | (0xf<<2) | (0xf<<6), 1, +- "WRITE(12)")) == 0) ++ reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, ++ (1<<1) | (0xf<<2) | (0xf<<6), 1, ++ "WRITE(12)"); ++ if (reply == 0) + reply = do_write(fsg); + break; + +@@ -1951,14 +2038,15 @@ static int do_scsi_command(struct fsg_de + case SC_RELEASE: + case SC_RESERVE: + case SC_SEND_DIAGNOSTIC: +- // Fall through ++ /* Fall through */ + + default: +- unknown_cmnd: ++unknown_cmnd: + fsg->data_size_from_cmnd = 0; + sprintf(unknown, "Unknown x%02x", fsg->common->cmnd[0]); +- if ((reply = check_command(fsg, fsg->common->cmnd_size, +- DATA_DIR_UNKNOWN, 0xff, 0, unknown)) == 0) { ++ reply = check_command(fsg, fsg->common->cmnd_size, ++ DATA_DIR_UNKNOWN, 0xff, 0, unknown); ++ if (reply == 0) { + fsg->common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } +@@ -1971,13 +2059,13 @@ static int do_scsi_command(struct fsg_de + + /* Set up the single reply buffer for finish_reply() */ + if (reply == -EINVAL) +- reply = 0; // Error reply length ++ reply = 0; /* Error reply length */ + if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) { + reply = min((u32) reply, fsg->data_size_from_cmnd); + bh->inreq->length = reply; + bh->state = BUF_STATE_FULL; + fsg->residue -= reply; +- } // Otherwise it's already set ++ } /* Otherwise it's already set */ + + return 0; + } +@@ -2157,13 +2245,15 @@ reset: + /* Enable the endpoints */ + d = fsg_ep_desc(fsg->gadget, + &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); +- if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) ++ rc = enable_endpoint(fsg, fsg->bulk_in, d); ++ if (rc != 0) + goto reset; + fsg->bulk_in_enabled = 1; + + d = fsg_ep_desc(fsg->gadget, + &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); +- if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) ++ rc = enable_endpoint(fsg, fsg->bulk_out, d); ++ if (rc != 0) + goto reset; + fsg->bulk_out_enabled = 1; + fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); +@@ -2173,9 +2263,11 @@ reset: + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &fsg->common->buffhds[i]; + +- if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) ++ rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq); ++ if (rc != 0) + goto reset; +- if ((rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq)) != 0) ++ rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq); ++ if (rc != 0) + goto reset; + bh->inreq->buf = bh->outreq->buf = bh->buf; + bh->inreq->context = bh->outreq->context = bh; +@@ -2302,9 +2394,8 @@ static void handle_exception(struct fsg_ + bh = &fsg->common->buffhds[i]; + bh->state = BUF_STATE_EMPTY; + } +- fsg->common->next_buffhd_to_fill = fsg->common->next_buffhd_to_drain = +- &fsg->common->buffhds[0]; +- ++ fsg->common->next_buffhd_to_fill = &fsg->common->buffhds[0]; ++ fsg->common->next_buffhd_to_drain = &fsg->common->buffhds[0]; + exception_req_tag = fsg->exception_req_tag; + new_config = fsg->new_config; + old_state = fsg->state; +@@ -2315,8 +2406,8 @@ static void handle_exception(struct fsg_ + for (i = 0; i < fsg->common->nluns; ++i) { + curlun = &fsg->common->luns[i]; + curlun->prevent_medium_removal = 0; +- curlun->sense_data = curlun->unit_attention_data = +- SS_NO_SENSE; ++ curlun->sense_data = SS_NO_SENSE; ++ curlun->unit_attention_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } +@@ -2342,30 +2433,31 @@ static void handle_exception(struct fsg_ + usb_ep_clear_halt(fsg->bulk_in); + + if (fsg->ep0_req_tag == exception_req_tag) +- ep0_queue(fsg); // Complete the status stage ++ ep0_queue(fsg); /* Complete the status stage */ + + /* Technically this should go here, but it would only be + * a waste of time. Ditto for the INTERFACE_CHANGE and + * CONFIG_CHANGE cases. */ +- // for (i = 0; i < fsg->common->nluns; ++i) +- // fsg->common->luns[i].unit_attention_data = SS_RESET_OCCURRED; ++ /* for (i = 0; i < fsg->common->nluns; ++i) */ ++ /* fsg->common->luns[i].unit_attention_data = */ ++ /* SS_RESET_OCCURRED; */ + break; + + case FSG_STATE_CONFIG_CHANGE: + rc = do_set_config(fsg, new_config); + if (fsg->ep0_req_tag != exception_req_tag) + break; +- if (rc != 0) // STALL on errors ++ if (rc != 0) /* STALL on errors */ + fsg_set_halt(fsg, fsg->ep0); +- else // Complete the status stage ++ else /* Complete the status stage */ + ep0_queue(fsg); + break; + + case FSG_STATE_EXIT: + case FSG_STATE_TERMINATED: +- do_set_config(fsg, 0); // Free resources ++ do_set_config(fsg, 0); /* Free resources */ + spin_lock_irq(&fsg->lock); +- fsg->state = FSG_STATE_TERMINATED; // Stop the thread ++ fsg->state = FSG_STATE_TERMINATED; /* Stop the thread */ + spin_unlock_irq(&fsg->lock); + break; + +@@ -2645,7 +2737,8 @@ static struct fsg_common *fsg_common_ini + error_luns: + common->nluns = i + 1; + error_release: +- /* Call fsg_common_release() directly, ref is not initialised */ ++ /* Call fsg_common_release() directly, ref might be not ++ * initialised */ + fsg_common_release(&common->ref); + return ERR_PTR(rc); + } +@@ -2721,13 +2814,13 @@ static int fsg_bind(struct usb_configura + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; +- ep->driver_data = fsg; // claim the endpoint ++ ep->driver_data = fsg; /* claim the endpoint */ + fsg->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; +- ep->driver_data = fsg; // claim the endpoint ++ ep->driver_data = fsg; /* claim the endpoint */ + fsg->bulk_out = ep; + + if (gadget_is_dualspeed(gadget)) { +@@ -2770,7 +2863,7 @@ autoconf_fail: + rc = -ENOTSUPP; + + out: +- fsg->state = FSG_STATE_TERMINATED; // The thread is dead ++ fsg->state = FSG_STATE_TERMINATED; /* The thread is dead */ + fsg_unbind(c, f); + complete(&fsg->thread_notifier); + return rc; +@@ -2874,18 +2967,16 @@ fsg_config_from_params(struct fsg_config + const struct fsg_module_parameters *params) + { + struct fsg_lun_config *lun; +- unsigned i, nluns; ++ unsigned i; + + /* Configure LUNs */ +- nluns = cfg->nluns = !params->luns +- ? params->file_count ? params->file_count : 1 +- : params->luns; +- for (i = 0, lun = cfg->luns; +- i < FSG_MAX_LUNS && i < nluns; +- ++i, ++lun) { ++ cfg->nluns = ++ min(params->luns ?: (params->file_count ?: 1u), ++ (unsigned)FSG_MAX_LUNS); ++ for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) { + lun->ro = !!params->ro[i]; + lun->cdrom = !!params->cdrom[i]; +- lun->removable = ++ lun->removable = /* Removable by default */ + params->removable_count <= i || params->removable[i]; + lun->filename = + params->file_count > i && params->file[i][0] +@@ -2893,7 +2984,7 @@ fsg_config_from_params(struct fsg_config + : 0; + } + +- /* Let FSG use defaults */ ++ /* Let MSF use defaults */ + cfg->lun_name_format = 0; + cfg->thread_name = 0; + cfg->vendor_name = 0; +--- a/drivers/usb/gadget/mass_storage.c ++++ b/drivers/usb/gadget/mass_storage.c +@@ -1,10 +1,10 @@ + /* +- * mass_storage.c -- File-backed USB Storage Gadget, for USB development ++ * mass_storage.c -- Mass Storage USB Gadget + * + * Copyright (C) 2003-2008 Alan Stern +- * + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> ++ * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -45,7 +45,7 @@ + /*-------------------------------------------------------------------------*/ + + #define DRIVER_DESC "Mass Storage Gadget" +-#define DRIVER_VERSION "2009/07/21" ++#define DRIVER_VERSION "2009/09/11" + + /*-------------------------------------------------------------------------*/ + +--- a/drivers/usb/gadget/storage_common.c ++++ b/drivers/usb/gadget/storage_common.c +@@ -61,8 +61,8 @@ + * + * DO NOT REUSE THESE IDs with any other driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. */ +-#define FSG_VENDOR_ID 0x0525 // NetChip +-#define FSG_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget ++#define FSG_VENDOR_ID 0x0525 /* NetChip */ ++#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ + + + /*-------------------------------------------------------------------------*/ +@@ -103,7 +103,7 @@ + #ifdef DUMP_MSGS + + # define dump_msg(fsg, /* const char * */ label, \ +- /* const u8 * */ buf, /* unsigned */ length) do { \ ++ /* const u8 * */ buf, /* unsigned */ length) do { \ + if (length < 512) { \ + DBG(fsg, "%s, length %u:\n", label, length); \ + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ +@@ -116,11 +116,11 @@ + #else + + # define dump_msg(fsg, /* const char * */ label, \ +- /* const u8 * */ buf, /* unsigned */ length) do { } while (0) ++ /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + + # ifdef VERBOSE_DEBUG + +-#define dump_cdb(fsg) \ ++# define dump_cdb(fsg) \ + print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \ + 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ + +@@ -143,45 +143,45 @@ + #define TYPE_CDROM 0x05 + + /* USB protocol value = the transport method */ +-#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt +-#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt +-#define USB_PR_BULK 0x50 // Bulk-only ++#define USB_PR_CBI 0x00 /* Control/Bulk/Interrupt */ ++#define USB_PR_CB 0x01 /* Control/Bulk w/o interrupt */ ++#define USB_PR_BULK 0x50 /* Bulk-only */ + + /* USB subclass value = the protocol encapsulation */ +-#define USB_SC_RBC 0x01 // Reduced Block Commands (flash) +-#define USB_SC_8020 0x02 // SFF-8020i, MMC-2, ATAPI (CD-ROM) +-#define USB_SC_QIC 0x03 // QIC-157 (tape) +-#define USB_SC_UFI 0x04 // UFI (floppy) +-#define USB_SC_8070 0x05 // SFF-8070i (removable) +-#define USB_SC_SCSI 0x06 // Transparent SCSI ++#define USB_SC_RBC 0x01 /* Reduced Block Commands (flash) */ ++#define USB_SC_8020 0x02 /* SFF-8020i, MMC-2, ATAPI (CD-ROM) */ ++#define USB_SC_QIC 0x03 /* QIC-157 (tape) */ ++#define USB_SC_UFI 0x04 /* UFI (floppy) */ ++#define USB_SC_8070 0x05 /* SFF-8070i (removable) */ ++#define USB_SC_SCSI 0x06 /* Transparent SCSI */ + + /* Bulk-only data structures */ + + /* Command Block Wrapper */ + struct fsg_bulk_cb_wrap { +- __le32 Signature; // Contains 'USBC' +- u32 Tag; // Unique per command id +- __le32 DataTransferLength; // Size of the data +- u8 Flags; // Direction in bit 7 +- u8 Lun; // LUN (normally 0) +- u8 Length; // Of the CDB, <= MAX_COMMAND_SIZE +- u8 CDB[16]; // Command Data Block ++ __le32 Signature; /* Contains 'USBC' */ ++ u32 Tag; /* Unique per command id */ ++ __le32 DataTransferLength; /* Size of the data */ ++ u8 Flags; /* Direction in bit 7 */ ++ u8 Lun; /* LUN (normally 0) */ ++ u8 Length; /* Of the CDB, <= MAX_COMMAND_SIZE */ ++ u8 CDB[16]; /* Command Data Block */ + }; + + #define USB_BULK_CB_WRAP_LEN 31 +-#define USB_BULK_CB_SIG 0x43425355 // Spells out USBC ++#define USB_BULK_CB_SIG 0x43425355 /* Spells out USBC */ + #define USB_BULK_IN_FLAG 0x80 + + /* Command Status Wrapper */ + struct bulk_cs_wrap { +- __le32 Signature; // Should = 'USBS' +- u32 Tag; // Same as original command +- __le32 Residue; // Amount not transferred +- u8 Status; // See below ++ __le32 Signature; /* Should = 'USBS' */ ++ u32 Tag; /* Same as original command */ ++ __le32 Residue; /* Amount not transferred */ ++ u8 Status; /* See below */ + }; + + #define USB_BULK_CS_WRAP_LEN 13 +-#define USB_BULK_CS_SIG 0x53425355 // Spells out 'USBS' ++#define USB_BULK_CS_SIG 0x53425355 /* Spells out 'USBS' */ + #define USB_STATUS_PASS 0 + #define USB_STATUS_FAIL 1 + #define USB_STATUS_PHASE_ERROR 2 +@@ -203,7 +203,8 @@ struct interrupt_data { + #define USB_CBI_ADSC_REQUEST 0x00 + + +-#define MAX_COMMAND_SIZE 16 // Length of a SCSI Command Data Block ++/* Length of a SCSI Command Data Block */ ++#define MAX_COMMAND_SIZE 16 + + /* SCSI commands that we recognize */ + #define SC_FORMAT_UNIT 0x04 +@@ -248,7 +249,7 @@ struct interrupt_data { + #define SS_WRITE_ERROR 0x030c02 + #define SS_WRITE_PROTECTED 0x072700 + +-#define SK(x) ((u8) ((x) >> 16)) // Sense Key byte, etc. ++#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ + #define ASC(x) ((u8) ((x) >> 8)) + #define ASCQ(x) ((u8) (x)) + +@@ -261,13 +262,13 @@ struct fsg_lun { + loff_t file_length; + loff_t num_sectors; + +- unsigned int initially_ro : 1; +- unsigned int ro : 1; +- unsigned int removable : 1; +- unsigned int cdrom : 1; +- unsigned int prevent_medium_removal : 1; +- unsigned int registered : 1; +- unsigned int info_valid : 1; ++ unsigned int initially_ro:1; ++ unsigned int ro:1; ++ unsigned int removable:1; ++ unsigned int cdrom:1; ++ unsigned int prevent_medium_removal:1; ++ unsigned int registered:1; ++ unsigned int info_valid:1; + + u32 sense_data; + u32 sense_data_info; +@@ -286,7 +287,7 @@ static struct fsg_lun *fsg_lun_from_dev( + + /* Big enough to hold our biggest descriptor */ + #define EP0_BUFSIZE 256 +-#define DELAYED_STATUS (EP0_BUFSIZE + 999) // An impossibly large value ++#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ + + /* Number of buffers we will use. 2 is enough for double-buffering */ + #define FSG_NUM_BUFFERS 2 +@@ -324,7 +325,8 @@ struct fsg_buffhd { + }; + + enum fsg_state { +- FSG_STATE_COMMAND_PHASE = -10, // This one isn't used anywhere ++ /* This one isn't used anywhere */ ++ FSG_STATE_COMMAND_PHASE = -10, + FSG_STATE_DATA_PHASE, + FSG_STATE_STATUS_PHASE, + +@@ -386,10 +388,10 @@ fsg_intf_desc = { + .bLength = sizeof fsg_intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + +- .bNumEndpoints = 2, // Adjusted during fsg_bind() ++ .bNumEndpoints = 2, /* Adjusted during fsg_bind() */ + .bInterfaceClass = USB_CLASS_MASS_STORAGE, +- .bInterfaceSubClass = USB_SC_SCSI, // Adjusted during fsg_bind() +- .bInterfaceProtocol = USB_PR_BULK, // Adjusted during fsg_bind() ++ .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */ ++ .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */ + .iInterface = FSG_STRING_INTERFACE, + }; + +@@ -426,7 +428,7 @@ fsg_fs_intr_in_desc = { + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), +- .bInterval = 32, // frames -> 32 ms ++ .bInterval = 32, /* frames -> 32 ms */ + }; + + #ifndef FSG_NO_OTG +@@ -477,7 +479,7 @@ fsg_hs_bulk_out_desc = { + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +- .bInterval = 1, // NAK every 1 uframe ++ .bInterval = 1, /* NAK every 1 uframe */ + }; + + #ifndef FSG_NO_INTR_EP +@@ -490,7 +492,7 @@ fsg_hs_intr_in_desc = { + /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), +- .bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms ++ .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */ + }; + + #ifndef FSG_NO_OTG +@@ -538,7 +540,7 @@ static struct usb_string fsg_strings[] + }; + + static struct usb_gadget_strings fsg_stringtab = { +- .language = 0x0409, // en-us ++ .language = 0x0409, /* en-us */ + .strings = fsg_strings, + }; + +@@ -600,11 +602,11 @@ static int fsg_lun_open(struct fsg_lun * + rc = (int) size; + goto out; + } +- num_sectors = size >> 9; // File size in 512-byte blocks ++ num_sectors = size >> 9; /* File size in 512-byte blocks */ + min_sectors = 1; + if (curlun->cdrom) { +- num_sectors &= ~3; // Reduce to a multiple of 2048 +- min_sectors = 300*4; // Smallest track is 300 frames ++ num_sectors &= ~3; /* Reduce to a multiple of 2048 */ ++ min_sectors = 300*4; /* Smallest track is 300 frames */ + if (num_sectors >= 256*60*75*4) { + num_sectors = (256*60*75 - 1) * 4; + LINFO(curlun, "file too big: %s\n", filename); +@@ -696,17 +698,17 @@ static ssize_t fsg_show_file(struct devi + ssize_t rc; + + down_read(filesem); +- if (fsg_lun_is_open(curlun)) { // Get the complete pathname ++ if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */ + p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1); + if (IS_ERR(p)) + rc = PTR_ERR(p); + else { + rc = strlen(p); + memmove(buf, p, rc); +- buf[rc] = '\n'; // Add a newline ++ buf[rc] = '\n'; /* Add a newline */ + buf[++rc] = 0; + } +- } else { // No file, return 0 bytes ++ } else { /* No file, return 0 bytes */ + *buf = 0; + rc = 0; + } +@@ -750,12 +752,12 @@ static ssize_t fsg_store_file(struct dev + + if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { + LDBG(curlun, "eject attempt prevented\n"); +- return -EBUSY; // "Door is locked" ++ return -EBUSY; /* "Door is locked" */ + } + + /* Remove a trailing newline */ + if (count > 0 && buf[count-1] == '\n') +- ((char *) buf)[count-1] = 0; // Ugh! ++ ((char *) buf)[count-1] = 0; /* Ugh! */ + + /* Eject current medium */ + down_write(filesem); diff --git a/usb/usb-g_mass_storage-fsg_config-added-module-params-handlig-changed.patch b/usb/usb-g_mass_storage-fsg_config-added-module-params-handlig-changed.patch new file mode 100644 index 00000000000000..3f5657f2399cdb --- /dev/null +++ b/usb/usb-g_mass_storage-fsg_config-added-module-params-handlig-changed.patch @@ -0,0 +1,493 @@ +From m.nazarewicz@samsung.com Fri Nov 20 12:26:16 2009 +From: Michal Nazarewicz <m.nazarewicz@samsung.com> +Date: Mon, 09 Nov 2009 14:15:21 +0100 +Subject: USB: g_mass_storage: fsg_config added & module params handlig changed +To: Greg KH <greg@kroah.com> +Cc: Michal Nazarewicz <m.nazarewicz@samsung.com> +Message-ID: <1257772527-870-3-git-send-email-m.nazarewicz@samsung.com> + + +From: Michal Nazarewicz <m.nazarewicz@samsung.com> + +Removed all references to mod_data in f_mass_storage.c and +instead created fsg_config structure fsg_common_init() takes +as an argument -- it stores all configuration options that +were previously taken from mod_data. + +Moreover, The fsg_config structure allows per-LUN +configuration of removable and CD-ROM emulation. + +Module parameters are handled by defining an object of +fsg_module_parameters structure and then declaring module +parameters via FSG_MODULE_PARAMETERS() macro. It adds proper +declarations to the code making specified object be populated +from module parameters. + +To use values stored there one may use either +fsg_config_from_params() which will will a fsg_config structure +with values taken from fsg_module_parameters structure or +fsg_common_from_params() which will initialise fsg_common +structure directly. + +Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/f_mass_storage.c | 267 +++++++++++++++++++++++------------- + drivers/usb/gadget/mass_storage.c | 7 + 2 files changed, 178 insertions(+), 96 deletions(-) + +--- a/drivers/usb/gadget/f_mass_storage.c ++++ b/drivers/usb/gadget/f_mass_storage.c +@@ -253,51 +253,6 @@ static const char fsg_string_interface[] + /*-------------------------------------------------------------------------*/ + + +-/* Encapsulate the module parameter settings */ +- +-static struct { +- char *file[FSG_MAX_LUNS]; +- int ro[FSG_MAX_LUNS]; +- unsigned int num_filenames; +- unsigned int num_ros; +- unsigned int nluns; +- +- int removable; +- int can_stall; +- int cdrom; +- +- unsigned short release; +-} mod_data = { // Default values +- .removable = 0, +- .can_stall = 1, +- .cdrom = 0, +- .release = 0xffff, +- }; +- +- +-module_param_array_named(file, mod_data.file, charp, &mod_data.num_filenames, +- S_IRUGO); +-MODULE_PARM_DESC(file, "names of backing files or devices"); +- +-module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO); +-MODULE_PARM_DESC(ro, "true to force read-only"); +- +-module_param_named(luns, mod_data.nluns, uint, S_IRUGO); +-MODULE_PARM_DESC(luns, "number of LUNs"); +- +-module_param_named(removable, mod_data.removable, bool, S_IRUGO); +-MODULE_PARM_DESC(removable, "true to simulate removable media"); +- +-module_param_named(stall, mod_data.can_stall, bool, S_IRUGO); +-MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); +- +-module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO); +-MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); +- +- +-/*-------------------------------------------------------------------------*/ +- +- + /* Data shared by all the FSG instances. */ + struct fsg_common { + struct usb_gadget *gadget; +@@ -317,12 +272,34 @@ struct fsg_common { + struct fsg_lun *luns; + struct fsg_lun *curlun; + ++ unsigned int can_stall:1; + unsigned int free_storage_on_release:1; + ++ /* Vendor (8 chars), product (16 chars), release (4 ++ * hexadecimal digits) and NUL byte */ ++ char inquiry_string[8 + 16 + 4 + 1]; ++ + struct kref ref; + }; + + ++struct fsg_config { ++ unsigned nluns; ++ struct fsg_lun_config { ++ const char *filename; ++ char ro; ++ char removable; ++ char cdrom; ++ } luns[FSG_MAX_LUNS]; ++ ++ const char *vendor_name; /* 8 characters or less */ ++ const char *product_name; /* 16 characters or less */ ++ u16 release; ++ ++ char can_stall; ++}; ++ ++ + struct fsg_dev { + struct usb_function function; + struct usb_composite_dev*cdev; +@@ -351,6 +328,7 @@ struct fsg_dev { + unsigned int phase_error : 1; + unsigned int short_packet_received : 1; + unsigned int bad_lun_okay : 1; ++ unsigned int can_stall : 1; + + unsigned long atomic_bitflags; + #define REGISTERED 0 +@@ -1065,13 +1043,10 @@ static int do_verify(struct fsg_dev *fsg + + static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) + { ++ struct fsg_lun *curlun = fsg->common->curlun; + u8 *buf = (u8 *) bh->buf; + +- static char vendor_id[] = "Linux "; +- static char product_disk_id[] = "File-Stor Gadget"; +- static char product_cdrom_id[] = "File-CD Gadget "; +- +- if (!fsg->common->curlun) { // Unsupported LUNs are okay ++ if (!curlun) { /* Unsupported LUNs are okay */ + fsg->bad_lun_okay = 1; + memset(buf, 0, 36); + buf[0] = 0x7f; // Unsupported, no device-type +@@ -1079,18 +1054,16 @@ static int do_inquiry(struct fsg_dev *fs + return 36; + } + +- memset(buf, 0, 8); +- buf[0] = (mod_data.cdrom ? TYPE_CDROM : TYPE_DISK); +- if (mod_data.removable) +- buf[1] = 0x80; ++ buf[0] = curlun->cdrom ? TYPE_CDROM : TYPE_DISK; ++ buf[1] = curlun->removable ? 0x80 : 0; + buf[2] = 2; // ANSI SCSI level 2 + buf[3] = 2; // SCSI-2 INQUIRY data format + buf[4] = 31; // Additional length +- // No special options +- sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, +- (mod_data.cdrom ? product_cdrom_id : +- product_disk_id), +- mod_data.release); ++ buf[5] = 0; // No special options ++ buf[6] = 0; ++ buf[7] = 0; ++ memcpy(buf + 8, fsg->common->inquiry_string, ++ sizeof fsg->common->inquiry_string); + return 36; + } + +@@ -1303,7 +1276,9 @@ static int do_mode_sense(struct fsg_dev + + static int do_start_stop(struct fsg_dev *fsg) + { +- if (!mod_data.removable) { ++ if (!fsg->common->curlun) { ++ return -EINVAL; ++ } else if (!fsg->common->curlun->removable) { + fsg->common->curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } +@@ -1316,8 +1291,10 @@ static int do_prevent_allow(struct fsg_d + struct fsg_lun *curlun = fsg->common->curlun; + int prevent; + +- if (!mod_data.removable) { +- curlun->sense_data = SS_INVALID_COMMAND; ++ if (!fsg->common->curlun) { ++ return -EINVAL; ++ } else if (!fsg->common->curlun->removable) { ++ fsg->common->curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + +@@ -1505,7 +1482,7 @@ static int finish_reply(struct fsg_dev * + * try to send or receive any data. So stall both bulk pipes + * if we can and wait for a reset. */ + case DATA_DIR_UNKNOWN: +- if (mod_data.can_stall) { ++ if (fsg->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + rc = halt_bulk_in_endpoint(fsg); + } +@@ -1526,7 +1503,7 @@ static int finish_reply(struct fsg_dev * + /* For Bulk-only, if we're allowed to stall then send the + * short packet and halt the bulk-in endpoint. If we can't + * stall, pad out the remaining data with 0's. */ +- } else if (mod_data.can_stall) { ++ } else if (fsg->can_stall) { + bh->inreq->zero = 1; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); +@@ -1556,7 +1533,7 @@ static int finish_reply(struct fsg_dev * + * STALL. Not realizing the endpoint was halted, it wouldn't + * clear the halt -- leading to problems later on. */ + #if 0 +- else if (mod_data.can_stall) { ++ else if (fsg->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; +@@ -1866,7 +1843,7 @@ static int do_scsi_command(struct fsg_de + break; + + case SC_READ_HEADER: +- if (!mod_data.cdrom) ++ if (!fsg->common->curlun || !fsg->common->curlun->cdrom) + goto unknown_cmnd; + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, +@@ -1876,7 +1853,7 @@ static int do_scsi_command(struct fsg_de + break; + + case SC_READ_TOC: +- if (!mod_data.cdrom) ++ if (!fsg->common->curlun || !fsg->common->curlun->cdrom) + goto unknown_cmnd; + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, +@@ -2043,7 +2020,7 @@ static int received_cbw(struct fsg_dev * + + /* We can do anything we want here, so let's stall the + * bulk pipes if we are allowed to. */ +- if (mod_data.can_stall) { ++ if (fsg->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + halt_bulk_in_endpoint(fsg); + } +@@ -2499,18 +2476,18 @@ static inline void fsg_common_put(struct + + + static struct fsg_common *fsg_common_init(struct fsg_common *common, +- struct usb_composite_dev *cdev) ++ struct usb_composite_dev *cdev, ++ struct fsg_config *cfg) + { + struct usb_gadget *gadget = cdev->gadget; + struct fsg_buffhd *bh; + struct fsg_lun *curlun; ++ struct fsg_lun_config *lcfg; + int nluns, i, rc; + char *pathbuf; + + /* Find out how many LUNs there should be */ +- nluns = mod_data.nluns; +- if (nluns == 0) +- nluns = max(mod_data.num_filenames, 1u); ++ nluns = cfg->nluns; + if (nluns < 1 || nluns > FSG_MAX_LUNS) { + dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns); + return ERR_PTR(-EINVAL); +@@ -2539,10 +2516,10 @@ static struct fsg_common *fsg_common_ini + + init_rwsem(&common->filesem); + +- for (i = 0; i < nluns; ++i, ++curlun) { +- curlun->cdrom = !!mod_data.cdrom; +- curlun->ro = mod_data.cdrom || mod_data.ro[i]; +- curlun->removable = mod_data.removable; ++ for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) { ++ curlun->cdrom = !!lcfg->cdrom; ++ curlun->ro = lcfg->cdrom || lcfg->ro; ++ curlun->removable = lcfg->removable; + curlun->dev.release = fsg_lun_release; + curlun->dev.parent = &gadget->dev; + /* curlun->dev.driver = &fsg_driver.driver; XXX */ +@@ -2564,11 +2541,11 @@ static struct fsg_common *fsg_common_ini + if (rc) + goto error_luns; + +- if (mod_data.file[i] && *mod_data.file[i]) { +- rc = fsg_lun_open(curlun, mod_data.file[i]); ++ if (lcfg->filename) { ++ rc = fsg_lun_open(curlun, lcfg->filename); + if (rc) + goto error_luns; +- } else if (!mod_data.removable) { ++ } else if (!curlun->removable) { + ERROR(common, "no file given for LUN%d\n", i); + rc = -EINVAL; + goto error_luns; +@@ -2588,33 +2565,40 @@ static struct fsg_common *fsg_common_ini + bh->next = common->buffhds; + + +- /* Release */ +- if (mod_data.release == 0xffff) { // Parameter wasn't set +- int gcnum; +- ++ /* Prepare inquiryString */ ++ if (cfg->release != 0xffff) { ++ i = cfg->release; ++ } else { + /* The sa1100 controller is not supported */ +- if (gadget_is_sa1100(gadget)) +- gcnum = -1; +- else +- gcnum = usb_gadget_controller_number(gadget); +- if (gcnum >= 0) +- mod_data.release = 0x0300 + gcnum; +- else { +- WARNING(common, "controller '%s' not recognized\n", +- gadget->name); ++ i = gadget_is_sa1100(gadget) ++ ? -1 ++ : usb_gadget_controller_number(gadget); ++ if (i >= 0) { ++ i = 0x0300 + i; ++ } else { + WARNING(common, "controller '%s' not recognized\n", + gadget->name); +- mod_data.release = 0x0399; ++ i = 0x0399; + } + } ++#define OR(x, y) ((x) ? (x) : (y)) ++ snprintf(common->inquiry_string, sizeof common->inquiry_string, ++ "%-8s%-16s%04x", ++ OR(cfg->vendor_name, "Linux "), ++ /* Assume product name dependent on the first LUN */ ++ OR(cfg->product_name, common->luns->cdrom ++ ? "File-Stor Gadget" ++ : "File-CD Gadget "), ++ i); ++#undef OR + + + /* Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. + */ +- if (gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)) +- mod_data.can_stall = 0; ++ common->can_stall = cfg->can_stall && ++ !(gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)); + + + kref_init(&common->ref); +@@ -2820,6 +2804,7 @@ static int fsg_add(struct usb_composite_ + * from this function. So instead of incrementing counter now + * and decrement in error recovery we increment it only when + * call to usb_add_function() was successful. */ ++ fsg->can_stall = common->can_stall; + + rc = usb_add_function(c, &fsg->function); + +@@ -2830,3 +2815,95 @@ static int fsg_add(struct usb_composite_ + + return rc; + } ++ ++ ++ ++/************************* Module parameters *************************/ ++ ++ ++struct fsg_module_parameters { ++ char *file[FSG_MAX_LUNS]; ++ int ro[FSG_MAX_LUNS]; ++ int removable[FSG_MAX_LUNS]; ++ int cdrom[FSG_MAX_LUNS]; ++ ++ unsigned int file_count, ro_count, removable_count, cdrom_count; ++ unsigned int luns; /* nluns */ ++ int stall; /* can_stall */ ++}; ++ ++ ++#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \ ++ module_param_array_named(prefix ## name, params.name, type, \ ++ &prefix ## params.name ## _count, \ ++ S_IRUGO); \ ++ MODULE_PARM_DESC(prefix ## name, desc) ++ ++#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \ ++ module_param_named(prefix ## name, params.name, type, \ ++ S_IRUGO); \ ++ MODULE_PARM_DESC(prefix ## name, desc) ++ ++#define FSG_MODULE_PARAMETERS(prefix, params) \ ++ _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \ ++ "names of backing files or devices"); \ ++ _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \ ++ "true to force read-only"); \ ++ _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \ ++ "true to simulate removable media"); \ ++ _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \ ++ "true to simulate CD-ROM instead of disk"); \ ++ _FSG_MODULE_PARAM(prefix, params, luns, uint, \ ++ "number of LUNs"); \ ++ _FSG_MODULE_PARAM(prefix, params, stall, bool, \ ++ "false to prevent bulk stalls") ++ ++ ++static void ++fsg_config_from_params(struct fsg_config *cfg, ++ const struct fsg_module_parameters *params) ++{ ++ struct fsg_lun_config *lun; ++ unsigned i, nluns; ++ ++ /* Configure LUNs */ ++ nluns = cfg->nluns = !params->luns ++ ? params->file_count ? params->file_count : 1 ++ : params->luns; ++ for (i = 0, lun = cfg->luns; ++ i < FSG_MAX_LUNS && i < nluns; ++ ++i, ++lun) { ++ lun->ro = !!params->ro[i]; ++ lun->cdrom = !!params->cdrom[i]; ++ lun->removable = ++ params->removable_count <= i || params->removable[i]; ++ lun->filename = ++ params->file_count > i && params->file[i][0] ++ ? params->file[i] ++ : 0; ++ } ++ ++ /* Let FSG use defaults */ ++ cfg->vendor_name = 0; ++ cfg->product_name = 0; ++ cfg->release = 0xffff; ++ ++ /* Finalise */ ++ cfg->can_stall = params->stall; ++} ++ ++static inline struct fsg_common * ++fsg_common_from_params(struct fsg_common *common, ++ struct usb_composite_dev *cdev, ++ const struct fsg_module_parameters *params) ++ __attribute__((unused)); ++static inline struct fsg_common * ++fsg_common_from_params(struct fsg_common *common, ++ struct usb_composite_dev *cdev, ++ const struct fsg_module_parameters *params) ++{ ++ struct fsg_config cfg; ++ fsg_config_from_params(&cfg, params); ++ return fsg_common_init(common, cdev, &cfg); ++} ++ +--- a/drivers/usb/gadget/mass_storage.c ++++ b/drivers/usb/gadget/mass_storage.c +@@ -127,6 +127,11 @@ static struct usb_gadget_strings *dev_st + + /****************************** Configurations ******************************/ + ++static struct fsg_module_parameters mod_data = { ++ .stall = 1 ++}; ++FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); ++ + static int __init msg_do_config(struct usb_configuration *c) + { + struct fsg_common *common; +@@ -137,7 +142,7 @@ static int __init msg_do_config(struct u + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + +- common = fsg_common_init(0, c->cdev); ++ common = fsg_common_from_params(0, c->cdev, &mod_data); + if (IS_ERR(common)) + return PTR_ERR(common); + diff --git a/usb/usb-g_mass_storage-lun_name_format-and-thread_name-added.patch b/usb/usb-g_mass_storage-lun_name_format-and-thread_name-added.patch new file mode 100644 index 00000000000000..1b1467dbd75b6c --- /dev/null +++ b/usb/usb-g_mass_storage-lun_name_format-and-thread_name-added.patch @@ -0,0 +1,110 @@ +From m.nazarewicz@samsung.com Fri Nov 20 12:28:11 2009 +From: Michal Nazarewicz <m.nazarewicz@samsung.com> +Date: Mon, 09 Nov 2009 14:15:22 +0100 +Subject: USB: g_mass_storage: lun_name_format and thread_name added +To: Greg KH <greg@kroah.com> +Cc: Michal Nazarewicz <m.nazarewicz@samsung.com> +Message-ID: <1257772527-870-4-git-send-email-m.nazarewicz@samsung.com> + + +From: Michal Nazarewicz <m.nazarewicz@samsung.com> + +A two fsg_config fields were added: +* lun_name_format which lets one specify format of a name + used when registering LUN devices. It is useful if there + would be ever need for two MSFs to be used in a single + composite gadget (as opposed to single MSF in two + configuration); and +* thread_name which lets one specify the name of a kernel + thread used by MSF. This is not required since two or more + threads can have the same name but nevertheless it's here + for consistency. + +Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/f_mass_storage.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +--- a/drivers/usb/gadget/f_mass_storage.c ++++ b/drivers/usb/gadget/f_mass_storage.c +@@ -233,7 +233,7 @@ + + + +-/*-------------------------------------------------------------------------*/ ++/*------------------------------------------------------------------------*/ + + #define FSG_DRIVER_DESC "Mass Storage Function" + #define FSG_DRIVER_VERSION "20 November 2008" +@@ -275,6 +275,8 @@ struct fsg_common { + unsigned int can_stall:1; + unsigned int free_storage_on_release:1; + ++ const char *thread_name; ++ + /* Vendor (8 chars), product (16 chars), release (4 + * hexadecimal digits) and NUL byte */ + char inquiry_string[8 + 16 + 4 + 1]; +@@ -292,6 +294,9 @@ struct fsg_config { + char cdrom; + } luns[FSG_MAX_LUNS]; + ++ const char *lun_name_format; ++ const char *thread_name; ++ + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + u16 release; +@@ -2524,8 +2529,11 @@ static struct fsg_common *fsg_common_ini + curlun->dev.parent = &gadget->dev; + /* curlun->dev.driver = &fsg_driver.driver; XXX */ + dev_set_drvdata(&curlun->dev, &common->filesem); +- dev_set_name(&curlun->dev,"%s-lun%d", +- dev_name(&gadget->dev), i); ++ dev_set_name(&curlun->dev, ++ cfg->lun_name_format ++ ? cfg->lun_name_format ++ : "lun%d", ++ i); + + rc = device_register(&curlun->dev); + if (rc) { +@@ -2590,7 +2598,6 @@ static struct fsg_common *fsg_common_ini + ? "File-Stor Gadget" + : "File-CD Gadget "), + i); +-#undef OR + + + /* Some peripheral controllers are known not to be able to +@@ -2601,7 +2608,10 @@ static struct fsg_common *fsg_common_ini + !(gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)); + + ++ common->thread_name = OR(cfg->thread_name, "file-storage"); + kref_init(&common->ref); ++#undef OR ++ + + /* Information */ + INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); +@@ -2741,7 +2751,7 @@ static int fsg_bind(struct usb_configura + + + fsg->thread_task = kthread_create(fsg_main_thread, fsg, +- "file-storage-gadget"); ++ fsg->common->thread_name); + if (IS_ERR(fsg->thread_task)) { + rc = PTR_ERR(fsg->thread_task); + goto out; +@@ -2884,6 +2894,8 @@ fsg_config_from_params(struct fsg_config + } + + /* Let FSG use defaults */ ++ cfg->lun_name_format = 0; ++ cfg->thread_name = 0; + cfg->vendor_name = 0; + cfg->product_name = 0; + cfg->release = 0xffff; diff --git a/usb/usb-g_mass_storage-mass-storage-function-created.patch b/usb/usb-g_mass_storage-mass-storage-function-created.patch new file mode 100644 index 00000000000000..0b278fc81362f8 --- /dev/null +++ b/usb/usb-g_mass_storage-mass-storage-function-created.patch @@ -0,0 +1,1285 @@ +From m.nazarewicz@samsung.com Fri Nov 20 12:14:22 2009 +From: Michal Nazarewicz <m.nazarewicz@samsung.com> +Date: Mon, 09 Nov 2009 14:15:20 +0100 +Subject: USB: g_mass_storage: Mass Storage Function created +To: Greg KH <greg@kroah.com> +Cc: Michal Nazarewicz <m.nazarewicz@samsung.com> +Message-ID: <1257772527-870-2-git-send-email-m.nazarewicz@samsung.com> + +From: Michal Nazarewicz <m.nazarewicz@samsung.com> + +The f_mass_storage.c has been changed into a composite function. +mass_storage.c file has been introduced which defines a +g_mass_storage gadget based on composite framework. + +Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/Kconfig | 18 + + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_mass_storage.c | 644 ++++++++---------------------------- + drivers/usb/gadget/file_storage.c | 7 + drivers/usb/gadget/mass_storage.c | 233 +++++++++++++ + drivers/usb/gadget/storage_common.c | 22 - + 6 files changed, 426 insertions(+), 500 deletions(-) + +--- a/drivers/usb/gadget/file_storage.c ++++ b/drivers/usb/gadget/file_storage.c +@@ -594,10 +594,9 @@ static int populate_config_buf(struct us + + if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG) + speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; +- if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH) +- function = fsg_hs_function; +- else +- function = fsg_fs_function; ++ function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH ++ ? (const struct usb_descriptor_header **)fsg_hs_function ++ : (const struct usb_descriptor_header **)fsg_fs_function; + + /* for now, don't advertise srp-only devices */ + if (!gadget_is_otg(gadget)) +--- a/drivers/usb/gadget/f_mass_storage.c ++++ b/drivers/usb/gadget/f_mass_storage.c +@@ -233,47 +233,23 @@ + + + +-/* +- * Kbuild is not very cooperative with respect to linking separately +- * compiled library objects into one module. So for now we won't use +- * separate compilation ... ensuring init/exit sections work to shrink +- * the runtime footprint, and giving us at least some parts of what +- * a "gcc --combine ... part1.c part2.c part3.c ... " build would. +- */ +-#include "usbstring.c" +-#include "config.c" +-#include "epautoconf.c" +- + /*-------------------------------------------------------------------------*/ + +-#define DRIVER_DESC "File-backed Storage Gadget" +-#define DRIVER_NAME "g_file_storage" +-#define DRIVER_VERSION "20 November 2008" ++#define FSG_DRIVER_DESC "Mass Storage Function" ++#define FSG_DRIVER_VERSION "20 November 2008" + +-static char fsg_string_manufacturer[64]; +-static const char fsg_string_product[] = DRIVER_DESC; +-static char fsg_string_serial[13]; +-static const char fsg_string_config[] = "Self-powered"; + static const char fsg_string_interface[] = "Mass Storage"; + + + #define FSG_NO_INTR_EP 1 + #define FSG_BUFFHD_STATIC_BUFFER 1 ++#define FSG_NO_DEVICE_STRINGS 1 ++#define FSG_NO_OTG 1 ++#define FSG_NO_INTR_EP 1 + + #include "storage_common.c" + + +-MODULE_DESCRIPTION(DRIVER_DESC); +-MODULE_AUTHOR("Alan Stern"); +-MODULE_LICENSE("Dual BSD/GPL"); +- +-/* +- * This driver assumes self-powered hardware and has no way for users to +- * trigger remote wakeup. It uses autoconfiguration to select endpoints +- * and endpoint addresses. +- */ +- +- + /*-------------------------------------------------------------------------*/ + + +@@ -295,6 +271,7 @@ static struct { + .removable = 0, + .can_stall = 1, + .cdrom = 0, ++ .release = 0xffff, + }; + + +@@ -347,14 +324,18 @@ struct fsg_common { + + + struct fsg_dev { ++ struct usb_function function; ++ struct usb_composite_dev*cdev; ++ struct usb_gadget *gadget; /* Copy of cdev->gadget */ + struct fsg_common *common; + ++ u16 interface_number; ++ + /* lock protects: state, all the req_busy's */ + spinlock_t lock; +- struct usb_gadget *gadget; + +- struct usb_ep *ep0; // Handy copy of gadget->ep0 +- struct usb_request *ep0req; // For control responses ++ struct usb_ep *ep0; /* Copy of gadget->ep0 */ ++ struct usb_request *ep0req; /* Copy of cdev->req */ + unsigned int ep0_req_tag; + const char *ep0req_name; + +@@ -390,6 +371,13 @@ struct fsg_dev { + u32 usb_amount_left; + }; + ++ ++static inline struct fsg_dev *fsg_from_func(struct usb_function *f) ++{ ++ return container_of(f, struct fsg_dev, function); ++} ++ ++ + typedef void (*fsg_routine_t)(struct fsg_dev *); + + static int exception_in_progress(struct fsg_dev *fsg) +@@ -410,10 +398,6 @@ static void set_bulk_out_req_length(stru + bh->outreq->length = length; + } + +-static struct fsg_dev *the_fsg; +-static struct usb_gadget_driver fsg_driver; +- +- + /*-------------------------------------------------------------------------*/ + + static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) +@@ -433,95 +417,6 @@ static int fsg_set_halt(struct fsg_dev * + + /*-------------------------------------------------------------------------*/ + +-/* +- * DESCRIPTORS ... most are static, but strings and (full) configuration +- * descriptors are built on demand. Also the (static) config and interface +- * descriptors are adjusted during fsg_bind(). +- */ +- +-/* There is only one configuration. */ +-#define CONFIG_VALUE 1 +- +-static struct usb_device_descriptor +-device_desc = { +- .bLength = sizeof device_desc, +- .bDescriptorType = USB_DT_DEVICE, +- +- .bcdUSB = cpu_to_le16(0x0200), +- .bDeviceClass = USB_CLASS_PER_INTERFACE, +- +- /* The next three values can be overridden by module parameters */ +- .idVendor = cpu_to_le16(FSG_VENDOR_ID), +- .idProduct = cpu_to_le16(FSG_PRODUCT_ID), +- .bcdDevice = cpu_to_le16(0xffff), +- +- .iManufacturer = FSG_STRING_MANUFACTURER, +- .iProduct = FSG_STRING_PRODUCT, +- .iSerialNumber = FSG_STRING_SERIAL, +- .bNumConfigurations = 1, +-}; +- +-static struct usb_config_descriptor +-config_desc = { +- .bLength = sizeof config_desc, +- .bDescriptorType = USB_DT_CONFIG, +- +- /* wTotalLength computed by usb_gadget_config_buf() */ +- .bNumInterfaces = 1, +- .bConfigurationValue = CONFIG_VALUE, +- .iConfiguration = FSG_STRING_CONFIG, +- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +- .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, +-}; +- +- +-static struct usb_qualifier_descriptor +-dev_qualifier = { +- .bLength = sizeof dev_qualifier, +- .bDescriptorType = USB_DT_DEVICE_QUALIFIER, +- +- .bcdUSB = cpu_to_le16(0x0200), +- .bDeviceClass = USB_CLASS_PER_INTERFACE, +- +- .bNumConfigurations = 1, +-}; +- +- +- +-/* +- * Config descriptors must agree with the code that sets configurations +- * and with code managing interfaces and their altsettings. They must +- * also handle different speeds and other-speed requests. +- */ +-static int populate_config_buf(struct usb_gadget *gadget, +- u8 *buf, u8 type, unsigned index) +-{ +- enum usb_device_speed speed = gadget->speed; +- int len; +- const struct usb_descriptor_header **function; +- +- if (index > 0) +- return -EINVAL; +- +- if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG) +- speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; +- if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH) +- function = fsg_hs_function; +- else +- function = fsg_fs_function; +- +- /* for now, don't advertise srp-only devices */ +- if (!gadget_is_otg(gadget)) +- function++; +- +- len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); +- ((struct usb_config_descriptor *) buf)->bDescriptorType = type; +- return len; +-} +- +- +-/*-------------------------------------------------------------------------*/ +- + /* These routines may be called in process context or in_irq */ + + /* Caller must hold fsg->lock */ +@@ -555,25 +450,12 @@ static void raise_exception(struct fsg_d + + /*-------------------------------------------------------------------------*/ + +-/* The disconnect callback and ep0 routines. These always run in_irq, +- * except that ep0_queue() is called in the main thread to acknowledge +- * completion of various requests: set config, set interface, and +- * Bulk-only device reset. */ +- +-static void fsg_disconnect(struct usb_gadget *gadget) +-{ +- struct fsg_dev *fsg = get_gadget_data(gadget); +- +- DBG(fsg, "disconnect or port reset\n"); +- raise_exception(fsg, FSG_STATE_DISCONNECT); +-} +- +- + static int ep0_queue(struct fsg_dev *fsg) + { + int rc; + + rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC); ++ fsg->ep0->driver_data = fsg; + if (rc != 0 && rc != -ESHUTDOWN) { + + /* We can't do much more than wait for a reset */ +@@ -583,23 +465,6 @@ static int ep0_queue(struct fsg_dev *fsg + return rc; + } + +-static void ep0_complete(struct usb_ep *ep, struct usb_request *req) +-{ +- struct fsg_dev *fsg = ep->driver_data; +- +- if (req->actual > 0) +- dump_msg(fsg, fsg->ep0req_name, req->buf, req->actual); +- if (req->status || req->actual != req->length) +- DBG(fsg, "%s --> %d, %u/%u\n", __func__, +- req->status, req->actual, req->length); +- if (req->status == -ECONNRESET) // Request was cancelled +- usb_ep_fifo_flush(ep); +- +- if (req->status == 0 && req->context) +- ((fsg_routine_t) (req->context))(fsg); +-} +- +- + /*-------------------------------------------------------------------------*/ + + /* Bulk and interrupt endpoint completion handlers. +@@ -652,9 +517,10 @@ static void bulk_out_complete(struct usb + + /* Ep0 class-specific handlers. These always run in_irq. */ + +-static int class_setup_req(struct fsg_dev *fsg, ++static int fsg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) + { ++ struct fsg_dev *fsg = fsg_from_func(f); + struct usb_request *req = fsg->ep0req; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); +@@ -669,7 +535,7 @@ static int class_setup_req(struct fsg_de + if (ctrl->bRequestType != + (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; +- if (w_index != 0 || w_value != 0) ++ if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + + /* Raise an exception to stop the current operation +@@ -682,7 +548,7 @@ static int class_setup_req(struct fsg_de + if (ctrl->bRequestType != + (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; +- if (w_index != 0 || w_value != 0) ++ if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + VDBG(fsg, "get max LUN\n"); + *(u8 *) req->buf = fsg->common->nluns - 1; +@@ -700,160 +566,6 @@ static int class_setup_req(struct fsg_de + + /*-------------------------------------------------------------------------*/ + +-/* Ep0 standard request handlers. These always run in_irq. */ +- +-static int standard_setup_req(struct fsg_dev *fsg, +- const struct usb_ctrlrequest *ctrl) +-{ +- struct usb_request *req = fsg->ep0req; +- int value = -EOPNOTSUPP; +- u16 w_index = le16_to_cpu(ctrl->wIndex); +- u16 w_value = le16_to_cpu(ctrl->wValue); +- +- /* Usually this just stores reply data in the pre-allocated ep0 buffer, +- * but config change events will also reconfigure hardware. */ +- switch (ctrl->bRequest) { +- +- case USB_REQ_GET_DESCRIPTOR: +- if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | +- USB_RECIP_DEVICE)) +- break; +- switch (w_value >> 8) { +- +- case USB_DT_DEVICE: +- VDBG(fsg, "get device descriptor\n"); +- value = sizeof device_desc; +- memcpy(req->buf, &device_desc, value); +- break; +- case USB_DT_DEVICE_QUALIFIER: +- VDBG(fsg, "get device qualifier\n"); +- if (!gadget_is_dualspeed(fsg->gadget)) +- break; +- value = sizeof dev_qualifier; +- memcpy(req->buf, &dev_qualifier, value); +- break; +- +- case USB_DT_OTHER_SPEED_CONFIG: +- VDBG(fsg, "get other-speed config descriptor\n"); +- if (!gadget_is_dualspeed(fsg->gadget)) +- break; +- goto get_config; +- case USB_DT_CONFIG: +- VDBG(fsg, "get configuration descriptor\n"); +-get_config: +- value = populate_config_buf(fsg->gadget, +- req->buf, +- w_value >> 8, +- w_value & 0xff); +- break; +- +- case USB_DT_STRING: +- VDBG(fsg, "get string descriptor\n"); +- +- /* wIndex == language code */ +- value = usb_gadget_get_string(&fsg_stringtab, +- w_value & 0xff, req->buf); +- break; +- } +- break; +- +- /* One config, two speeds */ +- case USB_REQ_SET_CONFIGURATION: +- if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | +- USB_RECIP_DEVICE)) +- break; +- VDBG(fsg, "set configuration\n"); +- if (w_value == CONFIG_VALUE || w_value == 0) { +- fsg->new_config = w_value; +- +- /* Raise an exception to wipe out previous transaction +- * state (queued bufs, etc) and set the new config. */ +- raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); +- value = DELAYED_STATUS; +- } +- break; +- case USB_REQ_GET_CONFIGURATION: +- if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | +- USB_RECIP_DEVICE)) +- break; +- VDBG(fsg, "get configuration\n"); +- *(u8 *) req->buf = fsg->config; +- value = 1; +- break; +- +- case USB_REQ_SET_INTERFACE: +- if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | +- USB_RECIP_INTERFACE)) +- break; +- if (fsg->config && w_index == 0) { +- +- /* Raise an exception to wipe out previous transaction +- * state (queued bufs, etc) and install the new +- * interface altsetting. */ +- raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE); +- value = DELAYED_STATUS; +- } +- break; +- case USB_REQ_GET_INTERFACE: +- if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | +- USB_RECIP_INTERFACE)) +- break; +- if (!fsg->config) +- break; +- if (w_index != 0) { +- value = -EDOM; +- break; +- } +- VDBG(fsg, "get interface\n"); +- *(u8 *) req->buf = 0; +- value = 1; +- break; +- +- default: +- VDBG(fsg, +- "unknown control req %02x.%02x v%04x i%04x l%u\n", +- ctrl->bRequestType, ctrl->bRequest, +- w_value, w_index, le16_to_cpu(ctrl->wLength)); +- } +- +- return value; +-} +- +- +-static int fsg_setup(struct usb_gadget *gadget, +- const struct usb_ctrlrequest *ctrl) +-{ +- struct fsg_dev *fsg = get_gadget_data(gadget); +- int rc; +- int w_length = le16_to_cpu(ctrl->wLength); +- +- ++fsg->ep0_req_tag; // Record arrival of a new request +- fsg->ep0req->context = NULL; +- fsg->ep0req->length = 0; +- dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); +- +- if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) +- rc = class_setup_req(fsg, ctrl); +- else +- rc = standard_setup_req(fsg, ctrl); +- +- /* Respond with data/status or defer until later? */ +- if (rc >= 0 && rc != DELAYED_STATUS) { +- rc = min(rc, w_length); +- fsg->ep0req->length = rc; +- fsg->ep0req->zero = rc < w_length; +- fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? +- "ep0-in" : "ep0-out"); +- rc = ep0_queue(fsg); +- } +- +- /* Device either stalls (rc < 0) or reports success */ +- return rc; +-} +- +- +-/*-------------------------------------------------------------------------*/ +- + /* All the following routines run in process context */ + + +@@ -2518,24 +2230,33 @@ static int do_set_config(struct fsg_dev + /* Enable the interface */ + if (new_config != 0) { + fsg->config = new_config; +- if ((rc = do_set_interface(fsg, 0)) != 0) +- fsg->config = 0; // Reset on errors +- else { +- char *speed; +- +- switch (fsg->gadget->speed) { +- case USB_SPEED_LOW: speed = "low"; break; +- case USB_SPEED_FULL: speed = "full"; break; +- case USB_SPEED_HIGH: speed = "high"; break; +- default: speed = "?"; break; +- } +- INFO(fsg, "%s speed config #%d\n", speed, fsg->config); +- } ++ rc = do_set_interface(fsg, 0); ++ if (rc != 0) ++ fsg->config = 0; /* Reset on errors */ + } + return rc; + } + + ++/****************************** ALT CONFIGS ******************************/ ++ ++ ++static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct fsg_dev *fsg = fsg_from_func(f); ++ fsg->new_config = 1; ++ raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); ++ return 0; ++} ++ ++static void fsg_disable(struct usb_function *f) ++{ ++ struct fsg_dev *fsg = fsg_from_func(f); ++ fsg->new_config = 0; ++ raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); ++} ++ ++ + /*-------------------------------------------------------------------------*/ + + static void handle_exception(struct fsg_dev *fsg) +@@ -2623,9 +2344,6 @@ static void handle_exception(struct fsg_ + + /* Carry out any extra actions required for the exception */ + switch (old_state) { +- default: +- break; +- + case FSG_STATE_ABORT_BULK_OUT: + send_status(fsg); + spin_lock_irq(&fsg->lock); +@@ -2651,16 +2369,6 @@ static void handle_exception(struct fsg_ + // fsg->common->luns[i].unit_attention_data = SS_RESET_OCCURRED; + break; + +- case FSG_STATE_INTERFACE_CHANGE: +- rc = do_set_interface(fsg, 0); +- if (fsg->ep0_req_tag != exception_req_tag) +- break; +- if (rc != 0) // STALL on errors +- fsg_set_halt(fsg, fsg->ep0); +- else // Complete the status stage +- ep0_queue(fsg); +- break; +- + case FSG_STATE_CONFIG_CHANGE: + rc = do_set_config(fsg, new_config); + if (fsg->ep0_req_tag != exception_req_tag) +@@ -2671,12 +2379,6 @@ static void handle_exception(struct fsg_ + ep0_queue(fsg); + break; + +- case FSG_STATE_DISCONNECT: +- for (i = 0; i < fsg->common->nluns; ++i) +- fsg_lun_fsync_sub(&fsg->common->luns[i]); +- do_set_config(fsg, 0); // Unconfigured state +- break; +- + case FSG_STATE_EXIT: + case FSG_STATE_TERMINATED: + do_set_config(fsg, 0); // Free resources +@@ -2684,6 +2386,14 @@ static void handle_exception(struct fsg_ + fsg->state = FSG_STATE_TERMINATED; // Stop the thread + spin_unlock_irq(&fsg->lock); + break; ++ ++ case FSG_STATE_INTERFACE_CHANGE: ++ case FSG_STATE_DISCONNECT: ++ case FSG_STATE_COMMAND_PHASE: ++ case FSG_STATE_DATA_PHASE: ++ case FSG_STATE_STATUS_PHASE: ++ case FSG_STATE_IDLE: ++ break; + } + } + +@@ -2744,16 +2454,17 @@ static int fsg_main_thread(void *fsg_) + if (!exception_in_progress(fsg)) + fsg->state = FSG_STATE_IDLE; + spin_unlock_irq(&fsg->lock); +- } ++ } + + spin_lock_irq(&fsg->lock); + fsg->thread_task = NULL; + spin_unlock_irq(&fsg->lock); + ++ /* XXX */ + /* If we are exiting because of a signal, unregister the + * gadget driver. */ +- if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) +- usb_gadget_unregister_driver(&fsg_driver); ++ /* if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) */ ++ /* usb_gadget_unregister_driver(&fsg_driver); */ + + /* Let the unbind and cleanup routines know the thread has exited */ + complete_and_exit(&fsg->thread_notifier, 0); +@@ -2762,10 +2473,9 @@ static int fsg_main_thread(void *fsg_) + + /*************************** DEVICE ATTRIBUTES ***************************/ + +- +-/* The write permissions and store_xxx pointers are set in fsg_bind() */ +-static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL); +-static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); ++/* Write permission is checked per LUN in store_*() functions. */ ++static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro); ++static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file); + + + /****************************** FSG COMMON ******************************/ +@@ -2789,11 +2499,13 @@ static inline void fsg_common_put(struct + + + static struct fsg_common *fsg_common_init(struct fsg_common *common, +- struct usb_gadget *gadget) ++ struct usb_composite_dev *cdev) + { ++ struct usb_gadget *gadget = cdev->gadget; + struct fsg_buffhd *bh; + struct fsg_lun *curlun; + int nluns, i, rc; ++ char *pathbuf; + + /* Find out how many LUNs there should be */ + nluns = mod_data.nluns; +@@ -2833,7 +2545,7 @@ static struct fsg_common *fsg_common_ini + curlun->removable = mod_data.removable; + curlun->dev.release = fsg_lun_release; + curlun->dev.parent = &gadget->dev; +- curlun->dev.driver = &fsg_driver.driver; ++ /* curlun->dev.driver = &fsg_driver.driver; XXX */ + dev_set_drvdata(&curlun->dev, &common->filesem); + dev_set_name(&curlun->dev,"%s-lun%d", + dev_name(&gadget->dev), i); +@@ -2906,6 +2618,33 @@ static struct fsg_common *fsg_common_ini + + + kref_init(&common->ref); ++ ++ /* Information */ ++ INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); ++ INFO(common, "Number of LUNs=%d\n", common->nluns); ++ ++ pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); ++ for (i = 0, nluns = common->nluns, curlun = common->luns; ++ i < nluns; ++ ++curlun, ++i) { ++ char *p = "(no medium)"; ++ if (fsg_lun_is_open(curlun)) { ++ p = "(error)"; ++ if (pathbuf) { ++ p = d_path(&curlun->filp->f_path, ++ pathbuf, PATH_MAX); ++ if (IS_ERR(p)) ++ p = "(error)"; ++ } ++ } ++ LINFO(curlun, "LUN: %s%s%sfile: %s\n", ++ curlun->removable ? "removable " : "", ++ curlun->ro ? "read only " : "", ++ curlun->cdrom ? "CD-ROM " : "", ++ p); ++ } ++ kfree(pathbuf); ++ + return common; + + +@@ -2944,10 +2683,9 @@ static void fsg_common_release(struct kr + /*-------------------------------------------------------------------------*/ + + +-static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) ++static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) + { +- struct fsg_dev *fsg = get_gadget_data(gadget); +- struct usb_request *req = fsg->ep0req; ++ struct fsg_dev *fsg = fsg_from_func(f); + + DBG(fsg, "unbind\n"); + clear_bit(REGISTERED, &fsg->atomic_bitflags); +@@ -2961,59 +2699,31 @@ static void /* __init_or_exit */ fsg_unb + complete(&fsg->thread_notifier); + } + +- /* Free the request and buffer for endpoint 0 */ +- if (req) { +- kfree(req->buf); +- usb_ep_free_request(fsg->ep0, req); +- } +- + fsg_common_put(fsg->common); + kfree(fsg); +- set_gadget_data(gadget, NULL); + } + + +-static int __init fsg_bind(struct usb_gadget *gadget) ++static int fsg_bind(struct usb_configuration *c, struct usb_function *f) + { +- struct fsg_dev *fsg; ++ struct fsg_dev *fsg = fsg_from_func(f); ++ struct usb_gadget *gadget = c->cdev->gadget; + int rc; + int i; +- struct fsg_lun *curlun; + struct usb_ep *ep; +- struct usb_request *req; +- char *pathbuf, *p; +- +- /* Allocate */ +- fsg = kzalloc(sizeof *fsg, GFP_KERNEL); +- if (!fsg) +- return -ENOMEM; + +- /* Initialise common */ +- fsg->common = fsg_common_init(0, gadget); +- if (IS_ERR(fsg->common)) +- return PTR_ERR(fsg->common); +- +- /* Basic parameters */ + fsg->gadget = gadget; +- set_gadget_data(gadget, fsg); + fsg->ep0 = gadget->ep0; +- fsg->ep0->driver_data = fsg; ++ fsg->ep0req = c->cdev->req; + +- spin_lock_init(&fsg->lock); +- init_completion(&fsg->thread_notifier); +- +- /* Enable the store_xxx attributes */ +- if (mod_data.removable) { +- dev_attr_file.attr.mode = 0644; +- dev_attr_file.store = fsg_store_file; +- if (!mod_data.cdrom) { +- dev_attr_ro.attr.mode = 0644; +- dev_attr_ro.store = fsg_store_ro; +- } +- } ++ /* New interface */ ++ i = usb_interface_id(c, f); ++ if (i < 0) ++ return i; ++ fsg_intf_desc.bInterfaceNumber = i; ++ fsg->interface_number = i; + + /* Find all the endpoints we will use */ +- usb_ep_autoconfig_reset(gadget); + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; +@@ -3026,53 +2736,26 @@ static int __init fsg_bind(struct usb_ga + ep->driver_data = fsg; // claim the endpoint + fsg->bulk_out = ep; + +- /* Fix up the descriptors */ +- device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket; +- device_desc.bcdDevice = cpu_to_le16(mod_data.release); +- + if (gadget_is_dualspeed(gadget)) { +- /* Assume ep0 uses the same maxpacket value for both speeds */ +- dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; +- + /* Assume endpoint addresses are the same for both speeds */ + fsg_hs_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_hs_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; ++ f->hs_descriptors = fsg_hs_function; + } + +- if (gadget_is_otg(gadget)) +- fsg_otg_desc.bmAttributes |= USB_OTG_HNP; +- +- rc = -ENOMEM; + +- /* Allocate the request and buffer for endpoint 0 */ +- fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL); +- if (!req) +- goto out; +- req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL); +- if (!req->buf) +- goto out; +- req->complete = ep0_complete; +- +- /* This should reflect the actual gadget power source */ +- usb_gadget_set_selfpowered(gadget); +- +- snprintf(fsg_string_manufacturer, sizeof fsg_string_manufacturer, +- "%s %s with %s", +- init_utsname()->sysname, init_utsname()->release, +- gadget->name); +- +- /* On a real device, serial[] would be loaded from permanent +- * storage. We just encode it from the driver version string. */ +- for (i = 0; i < sizeof fsg_string_serial - 2; i += 2) { +- unsigned char c = DRIVER_VERSION[i / 2]; +- +- if (!c) +- break; +- sprintf(&fsg_string_serial[i], "%02X", c); ++ /* maybe allocate device-global string IDs, and patch descriptors */ ++ if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { ++ i = usb_string_id(c->cdev); ++ if (i < 0) ++ return i; ++ fsg_strings[FSG_STRING_INTERFACE].id = i; ++ fsg_intf_desc.iInterface = i; + } + ++ + fsg->thread_task = kthread_create(fsg_main_thread, fsg, + "file-storage-gadget"); + if (IS_ERR(fsg->thread_task)) { +@@ -3080,37 +2763,12 @@ static int __init fsg_bind(struct usb_ga + goto out; + } + +- INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); +- INFO(fsg, "Number of LUNs=%d\n", fsg->common->nluns); +- +- pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); +- for (i = 0; i < fsg->common->nluns; ++i) { +- curlun = &fsg->common->luns[i]; +- if (fsg_lun_is_open(curlun)) { +- p = NULL; +- if (pathbuf) { +- p = d_path(&curlun->filp->f_path, +- pathbuf, PATH_MAX); +- if (IS_ERR(p)) +- p = NULL; +- } +- LINFO(curlun, "ro=%d, file: %s\n", +- curlun->ro, (p ? p : "(error)")); +- } +- } +- kfree(pathbuf); +- +- DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n", +- mod_data.removable, mod_data.can_stall, +- mod_data.cdrom, FSG_BUFLEN); + DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task)); + + set_bit(REGISTERED, &fsg->atomic_bitflags); + + /* Tell the thread to start working */ + wake_up_process(fsg->thread_task); +- +- the_fsg = fsg; + return 0; + + autoconf_fail: +@@ -3119,48 +2777,56 @@ autoconf_fail: + + out: + fsg->state = FSG_STATE_TERMINATED; // The thread is dead +- fsg_unbind(gadget); ++ fsg_unbind(c, f); + complete(&fsg->thread_notifier); + return rc; + } + + +-/*-------------------------------------------------------------------------*/ ++/****************************** ADD FUNCTION ******************************/ + +-static struct usb_gadget_driver fsg_driver = { +-#ifdef CONFIG_USB_GADGET_DUALSPEED +- .speed = USB_SPEED_HIGH, +-#else +- .speed = USB_SPEED_FULL, +-#endif +- .function = (char *) fsg_string_product, +- .bind = fsg_bind, +- .unbind = fsg_unbind, +- .disconnect = fsg_disconnect, +- .setup = fsg_setup, +- +- .driver = { +- .name = DRIVER_NAME, +- .owner = THIS_MODULE, +- // .release = ... +- // .suspend = ... +- // .resume = ... +- }, ++static struct usb_gadget_strings *fsg_strings_array[] = { ++ &fsg_stringtab, ++ NULL, + }; + +- +-static int __init fsg_init(void) ++static int fsg_add(struct usb_composite_dev *cdev, ++ struct usb_configuration *c, ++ struct fsg_common *common) + { +- return usb_gadget_register_driver(&fsg_driver); +-} +-module_init(fsg_init); ++ struct fsg_dev *fsg; ++ int rc; + ++ fsg = kzalloc(sizeof *fsg, GFP_KERNEL); ++ if (unlikely(!fsg)) ++ return -ENOMEM; + +-static void __exit fsg_cleanup(void) +-{ +- /* Unregister the driver iff the thread hasn't already done so */ +- if (the_fsg && +- test_and_clear_bit(REGISTERED, &the_fsg->atomic_bitflags)) +- usb_gadget_unregister_driver(&fsg_driver); ++ spin_lock_init(&fsg->lock); ++ init_completion(&fsg->thread_notifier); ++ ++ fsg->cdev = cdev; ++ fsg->function.name = FSG_DRIVER_DESC; ++ fsg->function.strings = fsg_strings_array; ++ fsg->function.descriptors = fsg_fs_function; ++ fsg->function.bind = fsg_bind; ++ fsg->function.unbind = fsg_unbind; ++ fsg->function.setup = fsg_setup; ++ fsg->function.set_alt = fsg_set_alt; ++ fsg->function.disable = fsg_disable; ++ ++ fsg->common = common; ++ /* Our caller holds a reference to common structure so we ++ * don't have to be worry about it being freed until we return ++ * from this function. So instead of incrementing counter now ++ * and decrement in error recovery we increment it only when ++ * call to usb_add_function() was successful. */ ++ ++ rc = usb_add_function(c, &fsg->function); ++ ++ if (likely(rc == 0)) ++ fsg_common_get(fsg->common); ++ else ++ kfree(fsg); ++ ++ return rc; + } +-module_exit(fsg_cleanup); +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -732,6 +732,24 @@ config USB_FILE_STORAGE_TEST + behavior of USB Mass Storage hosts. Not needed for + normal operation. + ++config USB_MASS_STORAGE ++ tristate "Mass Storage Gadget" ++ depends on BLOCK ++ help ++ The Mass Storage Gadget acts as a USB Mass Storage disk drive. ++ As its storage repository it can use a regular file or a block ++ device (in much the same way as the "loop" device driver), ++ specified as a module parameter or sysfs option. ++ ++ This is heavily based on File-backed Storage Gadget and in most ++ cases you will want to use FSG instead. This gadget is mostly ++ here to test the functionality of the Mass Storage Function ++ which may be used with composite framework. ++ ++ Say "y" to link the driver statically, or "m" to build ++ a dynamically linked module called "g_file_storage". If unsure, ++ consider File-backed Storage Gadget. ++ + config USB_G_SERIAL + tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" + help +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -39,6 +39,7 @@ g_serial-objs := serial.o + g_midi-objs := gmidi.o + gadgetfs-objs := inode.o + g_file_storage-objs := file_storage.o ++g_mass_storage-objs := mass_storage.o + g_printer-objs := printer.o + g_cdc-objs := cdc2.o + +@@ -47,6 +48,7 @@ obj-$(CONFIG_USB_AUDIO) += g_audio.o + obj-$(CONFIG_USB_ETH) += g_ether.o + obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o + obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o ++obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o + obj-$(CONFIG_USB_G_SERIAL) += g_serial.o + obj-$(CONFIG_USB_G_PRINTER) += g_printer.o + obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o +--- /dev/null ++++ b/drivers/usb/gadget/mass_storage.c +@@ -0,0 +1,233 @@ ++/* ++ * mass_storage.c -- File-backed USB Storage Gadget, for USB development ++ * ++ * Copyright (C) 2003-2008 Alan Stern ++ * ++ * Copyright (C) 2009 Samsung Electronics ++ * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++ ++/* ++ * The Mass Storage Gadget acts as a USB Mass Storage device, ++ * appearing to the host as a disk drive or as a CD-ROM drive. In ++ * addition to providing an example of a genuinely useful gadget ++ * driver for a USB device, it also illustrates a technique of ++ * double-buffering for increased throughput. Last but not least, it ++ * gives an easy way to probe the behavior of the Mass Storage drivers ++ * in a USB host. ++ * ++ * Since this file serves only administrative purposes and all the ++ * business logic is implemented in f_mass_storage.* file. Read ++ * comments in this file for more detailed description. ++ */ ++ ++ ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++#include <linux/usb/ch9.h> ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define DRIVER_DESC "Mass Storage Gadget" ++#define DRIVER_VERSION "2009/07/21" ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * kbuild is not very cooperative with respect to linking separately ++ * compiled library objects into one module. So for now we won't use ++ * separate compilation ... ensuring init/exit sections work to shrink ++ * the runtime footprint, and giving us at least some parts of what ++ * a "gcc --combine ... part1.c part2.c part3.c ... " build would. ++ */ ++ ++#include "composite.c" ++#include "usbstring.c" ++#include "config.c" ++#include "epautoconf.c" ++#include "f_mass_storage.c" ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_device_descriptor msg_device_desc = { ++ .bLength = sizeof msg_device_desc, ++ .bDescriptorType = USB_DT_DEVICE, ++ ++ .bcdUSB = cpu_to_le16(0x0200), ++ .bDeviceClass = USB_CLASS_PER_INTERFACE, ++ ++ /* Vendor and product id can be overridden by module parameters. */ ++ .idVendor = cpu_to_le16(FSG_VENDOR_ID), ++ .idProduct = cpu_to_le16(FSG_PRODUCT_ID), ++ /* .bcdDevice = f(hardware) */ ++ /* .iManufacturer = DYNAMIC */ ++ /* .iProduct = DYNAMIC */ ++ /* NO SERIAL NUMBER */ ++ .bNumConfigurations = 1, ++}; ++ ++static struct usb_otg_descriptor otg_descriptor = { ++ .bLength = sizeof otg_descriptor, ++ .bDescriptorType = USB_DT_OTG, ++ ++ /* REVISIT SRP-only hardware is possible, although ++ * it would not be called "OTG" ... ++ */ ++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, ++}; ++ ++static const struct usb_descriptor_header *otg_desc[] = { ++ (struct usb_descriptor_header *) &otg_descriptor, ++ NULL, ++}; ++ ++ ++/* string IDs are assigned dynamically */ ++ ++#define STRING_MANUFACTURER_IDX 0 ++#define STRING_PRODUCT_IDX 1 ++#define STRING_CONFIGURATION_IDX 2 ++ ++static char manufacturer[50]; ++ ++static struct usb_string strings_dev[] = { ++ [STRING_MANUFACTURER_IDX].s = manufacturer, ++ [STRING_PRODUCT_IDX].s = DRIVER_DESC, ++ [STRING_CONFIGURATION_IDX].s = "Self Powered", ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab_dev = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_dev, ++}; ++ ++static struct usb_gadget_strings *dev_strings[] = { ++ &stringtab_dev, ++ NULL, ++}; ++ ++ ++ ++/****************************** Configurations ******************************/ ++ ++static int __init msg_do_config(struct usb_configuration *c) ++{ ++ struct fsg_common *common; ++ int ret; ++ ++ if (gadget_is_otg(c->cdev->gadget)) { ++ c->descriptors = otg_desc; ++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ } ++ ++ common = fsg_common_init(0, c->cdev); ++ if (IS_ERR(common)) ++ return PTR_ERR(common); ++ ++ ret = fsg_add(c->cdev, c, common); ++ fsg_common_put(common); ++ return ret; ++} ++ ++static struct usb_configuration msg_config_driver = { ++ .label = "Linux File-Backed Storage", ++ .bind = msg_do_config, ++ .bConfigurationValue = 1, ++ /* .iConfiguration = DYNAMIC */ ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++}; ++ ++ ++ ++/****************************** Gadget Bind ******************************/ ++ ++ ++static int __init msg_bind(struct usb_composite_dev *cdev) ++{ ++ struct usb_gadget *gadget = cdev->gadget; ++ int status; ++ ++ /* Allocate string descriptor numbers ... note that string ++ * contents can be overridden by the composite_dev glue. ++ */ ++ ++ /* device descriptor strings: manufacturer, product */ ++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", ++ init_utsname()->sysname, init_utsname()->release, ++ gadget->name); ++ status = usb_string_id(cdev); ++ if (status < 0) ++ return status; ++ strings_dev[STRING_MANUFACTURER_IDX].id = status; ++ msg_device_desc.iManufacturer = status; ++ ++ status = usb_string_id(cdev); ++ if (status < 0) ++ return status; ++ strings_dev[STRING_PRODUCT_IDX].id = status; ++ msg_device_desc.iProduct = status; ++ ++ status = usb_string_id(cdev); ++ if (status < 0) ++ return status; ++ strings_dev[STRING_CONFIGURATION_IDX].id = status; ++ msg_config_driver.iConfiguration = status; ++ ++ /* register our second configuration */ ++ status = usb_add_config(cdev, &msg_config_driver); ++ if (status < 0) ++ return status; ++ ++ dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); ++ return 0; ++} ++ ++static int __exit msg_unbind(struct usb_composite_dev *cdev) ++{ ++ return 0; ++} ++ ++ ++/****************************** Some noise ******************************/ ++ ++ ++static struct usb_composite_driver msg_driver = { ++ .name = "g_mass_storage", ++ .dev = &msg_device_desc, ++ .strings = dev_strings, ++ .bind = msg_bind, ++ .unbind = __exit_p(msg_unbind), ++}; ++ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR("Michal Nazarewicz"); ++MODULE_LICENSE("GPL"); ++ ++static int __init msg_init(void) ++{ ++ return usb_composite_register(&msg_driver); ++} ++module_init(msg_init); ++ ++static void __exit msg_cleanup(void) ++{ ++ usb_composite_unregister(&msg_driver); ++} ++module_exit(msg_cleanup); +--- a/drivers/usb/gadget/storage_common.c ++++ b/drivers/usb/gadget/storage_common.c +@@ -84,11 +84,19 @@ + #define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args) + #define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args) + +-#define DBG(d, fmt, args...) dev_dbg (&(d)->gadget->dev, fmt, ## args) +-#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev, fmt, ## args) +-#define ERROR(d, fmt, args...) dev_err (&(d)->gadget->dev, fmt, ## args) +-#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev, fmt, ## args) +-#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev, fmt, ## args) ++/* Keep those macros in sync with thos in ++ * include/linux/ubs/composite.h or else GCC will complain. If they ++ * are identical (the same names of arguments, white spaces in the ++ * same places) GCC will allow redefinition otherwise (even if some ++ * white space is removed or added) warning will be issued. No ++ * checking if those symbols is defined is performed because warning ++ * is desired when those macros were defined by someone else to mean ++ * something else. */ ++#define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args) ++#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args) ++#define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args) ++#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev , fmt , ## args) ++#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev , fmt , ## args) + + + +@@ -429,7 +437,7 @@ fsg_fs_intr_in_desc = { + + #endif + +-static const struct usb_descriptor_header *fsg_fs_function[] = { ++static struct usb_descriptor_header *fsg_fs_function[] = { + #ifndef FSG_NO_OTG + (struct usb_descriptor_header *) &fsg_otg_desc, + #endif +@@ -493,7 +501,7 @@ fsg_hs_intr_in_desc = { + + #endif + +-static const struct usb_descriptor_header *fsg_hs_function[] = { ++static struct usb_descriptor_header *fsg_hs_function[] = { + #ifndef FSG_NO_OTG + (struct usb_descriptor_header *) &fsg_otg_desc, + #endif diff --git a/usb/usb-g_mass_storage-most-data-moved-to-fsg_common.patch b/usb/usb-g_mass_storage-most-data-moved-to-fsg_common.patch new file mode 100644 index 00000000000000..a6a4f0efadc344 --- /dev/null +++ b/usb/usb-g_mass_storage-most-data-moved-to-fsg_common.patch @@ -0,0 +1,2419 @@ +From m.nazarewicz@samsung.com Fri Nov 20 12:28:37 2009 +From: Michal Nazarewicz <m.nazarewicz@samsung.com> +Date: Mon, 09 Nov 2009 14:15:24 +0100 +Subject: USB: g_mass_storage: most data moved to fsg_common +To: Greg KH <greg@kroah.com> +Cc: Michal Nazarewicz <m.nazarewicz@samsung.com> +Message-ID: <1257772527-870-6-git-send-email-m.nazarewicz@samsung.com> + + +From: Michal Nazarewicz <m.nazarewicz@samsung.com> + +Most of the data from fsg_dev have been moved to fsg_common +structure. The fsg_dev structure holds only endpoint dependent +data. The fsg_common structure has a fsg pointer which points +to active fsg_dev structure -- endpoints are referenced via this +pointer. + +This fixes the problem of several threads created when a single +instance of MSF is used in several USB configurations. + +Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +--- + drivers/usb/gadget/f_mass_storage.c | 1230 ++++++++++++++++++------------------ + 1 file changed, 648 insertions(+), 582 deletions(-) + +--- a/drivers/usb/gadget/f_mass_storage.c ++++ b/drivers/usb/gadget/f_mass_storage.c +@@ -312,14 +312,26 @@ static const char fsg_string_interface[] + + /*-------------------------------------------------------------------------*/ + ++struct fsg_dev; ++ + + /* Data shared by all the FSG instances. */ + struct fsg_common { + struct usb_gadget *gadget; ++ struct fsg_dev *fsg; ++ struct fsg_dev *prev_fsg; + + /* filesem protects: backing files in use */ + struct rw_semaphore filesem; + ++ /* lock protects: state, all the req_busy's */ ++ spinlock_t lock; ++ ++ struct usb_ep *ep0; /* Copy of gadget->ep0 */ ++ struct usb_request *ep0req; /* Copy of cdev->req */ ++ unsigned int ep0_req_tag; ++ const char *ep0req_name; ++ + struct fsg_buffhd *next_buffhd_to_fill; + struct fsg_buffhd *next_buffhd_to_drain; + struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; +@@ -332,10 +344,28 @@ struct fsg_common { + struct fsg_lun *luns; + struct fsg_lun *curlun; + ++ unsigned int bulk_out_maxpacket; ++ enum fsg_state state; /* For exception handling */ ++ unsigned int exception_req_tag; ++ ++ u8 config, new_config; ++ enum data_direction data_dir; ++ u32 data_size; ++ u32 data_size_from_cmnd; ++ u32 tag; ++ u32 residue; ++ u32 usb_amount_left; ++ + unsigned int can_stall:1; + unsigned int free_storage_on_release:1; ++ unsigned int phase_error:1; ++ unsigned int short_packet_received:1; ++ unsigned int bad_lun_okay:1; ++ unsigned int running:1; + +- const char *thread_name; ++ int thread_wakeup_needed; ++ struct completion thread_notifier; ++ struct task_struct *thread_task; + + /* Vendor (8 chars), product (16 chars), release (4 + * hexadecimal digits) and NUL byte */ +@@ -367,52 +397,32 @@ struct fsg_config { + + struct fsg_dev { + struct usb_function function; +- struct usb_composite_dev *cdev; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ + struct fsg_common *common; + + u16 interface_number; + +- /* lock protects: state, all the req_busy's */ +- spinlock_t lock; +- +- struct usb_ep *ep0; /* Copy of gadget->ep0 */ +- struct usb_request *ep0req; /* Copy of cdev->req */ +- unsigned int ep0_req_tag; +- const char *ep0req_name; +- +- unsigned int bulk_out_maxpacket; +- enum fsg_state state; /* For exception handling */ +- unsigned int exception_req_tag; +- +- u8 config, new_config; +- +- unsigned int running:1; + unsigned int bulk_in_enabled:1; + unsigned int bulk_out_enabled:1; +- unsigned int phase_error:1; +- unsigned int short_packet_received:1; +- unsigned int bad_lun_okay:1; +- unsigned int can_stall:1; + + unsigned long atomic_bitflags; +-#define REGISTERED 0 +-#define IGNORE_BULK_OUT 1 ++#define IGNORE_BULK_OUT 0 + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; ++}; + +- int thread_wakeup_needed; +- struct completion thread_notifier; +- struct task_struct *thread_task; + +- enum data_direction data_dir; +- u32 data_size; +- u32 data_size_from_cmnd; +- u32 tag; +- u32 residue; +- u32 usb_amount_left; +-}; ++static inline int __fsg_is_set(struct fsg_common *common, ++ const char *func, unsigned line) ++{ ++ if (common->fsg) ++ return 1; ++ ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); ++ return 0; ++} ++ ++#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__)) + + + static inline struct fsg_dev *fsg_from_func(struct usb_function *f) +@@ -423,21 +433,21 @@ static inline struct fsg_dev *fsg_from_f + + typedef void (*fsg_routine_t)(struct fsg_dev *); + +-static int exception_in_progress(struct fsg_dev *fsg) ++static int exception_in_progress(struct fsg_common *common) + { +- return (fsg->state > FSG_STATE_IDLE); ++ return common->state > FSG_STATE_IDLE; + } + + /* Make bulk-out requests be divisible by the maxpacket size */ +-static void set_bulk_out_req_length(struct fsg_dev *fsg, ++static void set_bulk_out_req_length(struct fsg_common *common, + struct fsg_buffhd *bh, unsigned int length) + { + unsigned int rem; + + bh->bulk_out_intended_length = length; +- rem = length % fsg->bulk_out_maxpacket; ++ rem = length % common->bulk_out_maxpacket; + if (rem > 0) +- length += fsg->bulk_out_maxpacket - rem; ++ length += common->bulk_out_maxpacket - rem; + bh->outreq->length = length; + } + +@@ -463,47 +473,46 @@ static int fsg_set_halt(struct fsg_dev * + /* These routines may be called in process context or in_irq */ + + /* Caller must hold fsg->lock */ +-static void wakeup_thread(struct fsg_dev *fsg) ++static void wakeup_thread(struct fsg_common *common) + { + /* Tell the main thread that something has happened */ +- fsg->thread_wakeup_needed = 1; +- if (fsg->thread_task) +- wake_up_process(fsg->thread_task); ++ common->thread_wakeup_needed = 1; ++ if (common->thread_task) ++ wake_up_process(common->thread_task); + } + + +-static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state) ++static void raise_exception(struct fsg_common *common, enum fsg_state new_state) + { + unsigned long flags; + + /* Do nothing if a higher-priority exception is already in progress. + * If a lower-or-equal priority exception is in progress, preempt it + * and notify the main thread by sending it a signal. */ +- spin_lock_irqsave(&fsg->lock, flags); +- if (fsg->state <= new_state) { +- fsg->exception_req_tag = fsg->ep0_req_tag; +- fsg->state = new_state; +- if (fsg->thread_task) ++ spin_lock_irqsave(&common->lock, flags); ++ if (common->state <= new_state) { ++ common->exception_req_tag = common->ep0_req_tag; ++ common->state = new_state; ++ if (common->thread_task) + send_sig_info(SIGUSR1, SEND_SIG_FORCED, +- fsg->thread_task); ++ common->thread_task); + } +- spin_unlock_irqrestore(&fsg->lock, flags); ++ spin_unlock_irqrestore(&common->lock, flags); + } + + + /*-------------------------------------------------------------------------*/ + +-static int ep0_queue(struct fsg_dev *fsg) ++static int ep0_queue(struct fsg_common *common) + { + int rc; + +- rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC); +- fsg->ep0->driver_data = fsg; ++ rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC); ++ common->ep0->driver_data = common; + if (rc != 0 && rc != -ESHUTDOWN) { +- + /* We can't do much more than wait for a reset */ +- WARNING(fsg, "error in submission: %s --> %d\n", +- fsg->ep0->name, rc); ++ WARNING(common, "error in submission: %s --> %d\n", ++ common->ep0->name, rc); + } + return rc; + } +@@ -515,32 +524,32 @@ static int ep0_queue(struct fsg_dev *fsg + + static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) + { +- struct fsg_dev *fsg = ep->driver_data; ++ struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + if (req->status || req->actual != req->length) +- DBG(fsg, "%s --> %d, %u/%u\n", __func__, ++ DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); +- spin_lock(&fsg->lock); ++ spin_lock(&common->lock); + bh->inreq_busy = 0; + bh->state = BUF_STATE_EMPTY; +- wakeup_thread(fsg); +- spin_unlock(&fsg->lock); ++ wakeup_thread(common); ++ spin_unlock(&common->lock); + } + + static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) + { +- struct fsg_dev *fsg = ep->driver_data; ++ struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + +- dump_msg(fsg, "bulk-out", req->buf, req->actual); ++ dump_msg(common, "bulk-out", req->buf, req->actual); + if (req->status || req->actual != bh->bulk_out_intended_length) +- DBG(fsg, "%s --> %d, %u/%u\n", __func__, ++ DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, + bh->bulk_out_intended_length); + if (req->status == -ECONNRESET) /* Request was cancelled */ +@@ -548,11 +557,11 @@ static void bulk_out_complete(struct usb + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); +- spin_lock(&fsg->lock); ++ spin_lock(&common->lock); + bh->outreq_busy = 0; + bh->state = BUF_STATE_FULL; +- wakeup_thread(fsg); +- spin_unlock(&fsg->lock); ++ wakeup_thread(common); ++ spin_unlock(&common->lock); + } + + +@@ -564,12 +573,12 @@ static int fsg_setup(struct usb_function + const struct usb_ctrlrequest *ctrl) + { + struct fsg_dev *fsg = fsg_from_func(f); +- struct usb_request *req = fsg->ep0req; ++ struct usb_request *req = fsg->common->ep0req; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + +- if (!fsg->config) ++ if (!fsg->common->config) + return -EOPNOTSUPP; + + switch (ctrl->bRequest) { +@@ -584,7 +593,7 @@ static int fsg_setup(struct usb_function + /* Raise an exception to stop the current operation + * and reinitialize our state. */ + DBG(fsg, "bulk reset request\n"); +- raise_exception(fsg, FSG_STATE_RESET); ++ raise_exception(fsg->common, FSG_STATE_RESET); + return DELAYED_STATUS; + + case USB_BULK_GET_MAX_LUN_REQUEST: +@@ -622,10 +631,10 @@ static void start_transfer(struct fsg_de + if (ep == fsg->bulk_in) + dump_msg(fsg, "bulk-in", req->buf, req->length); + +- spin_lock_irq(&fsg->lock); ++ spin_lock_irq(&fsg->common->lock); + *pbusy = 1; + *state = BUF_STATE_BUSY; +- spin_unlock_irq(&fsg->lock); ++ spin_unlock_irq(&fsg->common->lock); + rc = usb_ep_queue(ep, req, GFP_KERNEL); + if (rc != 0) { + *pbusy = 0; +@@ -642,8 +651,18 @@ static void start_transfer(struct fsg_de + } + } + ++#define START_TRANSFER_OR(common, ep_name, req, pbusy, state) \ ++ if (fsg_is_set(common)) \ ++ start_transfer((common)->fsg, (common)->fsg->ep_name, \ ++ req, pbusy, state); \ ++ else ++ ++#define START_TRANSFER(common, ep_name, req, pbusy, state) \ ++ START_TRANSFER_OR(common, ep_name, req, pbusy, state) (void)0 ++ + +-static int sleep_thread(struct fsg_dev *fsg) ++ ++static int sleep_thread(struct fsg_common *common) + { + int rc = 0; + +@@ -655,21 +674,21 @@ static int sleep_thread(struct fsg_dev * + rc = -EINTR; + break; + } +- if (fsg->thread_wakeup_needed) ++ if (common->thread_wakeup_needed) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); +- fsg->thread_wakeup_needed = 0; ++ common->thread_wakeup_needed = 0; + return rc; + } + + + /*-------------------------------------------------------------------------*/ + +-static int do_read(struct fsg_dev *fsg) ++static int do_read(struct fsg_common *common) + { +- struct fsg_lun *curlun = fsg->common->curlun; ++ struct fsg_lun *curlun = common->curlun; + u32 lba; + struct fsg_buffhd *bh; + int rc; +@@ -681,15 +700,15 @@ static int do_read(struct fsg_dev *fsg) + + /* Get the starting Logical Block Address and check that it's + * not too big */ +- if (fsg->common->cmnd[0] == SC_READ_6) +- lba = get_unaligned_be24(&fsg->common->cmnd[1]); ++ if (common->cmnd[0] == SC_READ_6) ++ lba = get_unaligned_be24(&common->cmnd[1]); + else { +- lba = get_unaligned_be32(&fsg->common->cmnd[2]); ++ lba = get_unaligned_be32(&common->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = don't read from the + * cache), but we don't implement them. */ +- if ((fsg->common->cmnd[1] & ~0x18) != 0) { ++ if ((common->cmnd[1] & ~0x18) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } +@@ -701,7 +720,7 @@ static int do_read(struct fsg_dev *fsg) + file_offset = ((loff_t) lba) << 9; + + /* Carry out the file reads */ +- amount_left = fsg->data_size_from_cmnd; ++ amount_left = common->data_size_from_cmnd; + if (unlikely(amount_left == 0)) + return -EIO; /* No default reply */ + +@@ -724,9 +743,9 @@ static int do_read(struct fsg_dev *fsg) + partial_page); + + /* Wait for the next buffer to become available */ +- bh = fsg->common->next_buffhd_to_fill; ++ bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { +- rc = sleep_thread(fsg); ++ rc = sleep_thread(common); + if (rc) + return rc; + } +@@ -765,7 +784,7 @@ static int do_read(struct fsg_dev *fsg) + } + file_offset += nread; + amount_left -= nread; +- fsg->residue -= nread; ++ common->residue -= nread; + bh->inreq->length = nread; + bh->state = BUF_STATE_FULL; + +@@ -782,9 +801,12 @@ static int do_read(struct fsg_dev *fsg) + + /* Send this buffer and go read some more */ + bh->inreq->zero = 0; +- start_transfer(fsg, fsg->bulk_in, bh->inreq, +- &bh->inreq_busy, &bh->state); +- fsg->common->next_buffhd_to_fill = bh->next; ++ START_TRANSFER_OR(common, bulk_in, bh->inreq, ++ &bh->inreq_busy, &bh->state) ++ /* Don't know what to do if ++ * common->fsg is NULL */ ++ return -EIO; ++ common->next_buffhd_to_fill = bh->next; + } + + return -EIO; /* No default reply */ +@@ -793,9 +815,9 @@ static int do_read(struct fsg_dev *fsg) + + /*-------------------------------------------------------------------------*/ + +-static int do_write(struct fsg_dev *fsg) ++static int do_write(struct fsg_common *common) + { +- struct fsg_lun *curlun = fsg->common->curlun; ++ struct fsg_lun *curlun = common->curlun; + u32 lba; + struct fsg_buffhd *bh; + int get_some_more; +@@ -816,20 +838,20 @@ static int do_write(struct fsg_dev *fsg) + + /* Get the starting Logical Block Address and check that it's + * not too big */ +- if (fsg->common->cmnd[0] == SC_WRITE_6) +- lba = get_unaligned_be24(&fsg->common->cmnd[1]); ++ if (common->cmnd[0] == SC_WRITE_6) ++ lba = get_unaligned_be24(&common->cmnd[1]); + else { +- lba = get_unaligned_be32(&fsg->common->cmnd[2]); ++ lba = get_unaligned_be32(&common->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = write directly to the + * medium). We don't implement DPO; we implement FUA by + * performing synchronous output. */ +- if ((fsg->common->cmnd[1] & ~0x18) != 0) { ++ if (common->cmnd[1] & ~0x18) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } +- if (fsg->common->cmnd[1] & 0x08) { /* FUA */ ++ if (common->cmnd[1] & 0x08) { /* FUA */ + spin_lock(&curlun->filp->f_lock); + curlun->filp->f_flags |= O_SYNC; + spin_unlock(&curlun->filp->f_lock); +@@ -843,12 +865,13 @@ static int do_write(struct fsg_dev *fsg) + /* Carry out the file writes */ + get_some_more = 1; + file_offset = usb_offset = ((loff_t) lba) << 9; +- amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; ++ amount_left_to_req = common->data_size_from_cmnd; ++ amount_left_to_write = common->data_size_from_cmnd; + + while (amount_left_to_write > 0) { + + /* Queue a request for more data from the host */ +- bh = fsg->common->next_buffhd_to_fill; ++ bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && get_some_more) { + + /* Figure out how much we want to get: +@@ -887,7 +910,7 @@ static int do_write(struct fsg_dev *fsg) + + /* Get the next buffer */ + usb_offset += amount; +- fsg->usb_amount_left -= amount; ++ common->usb_amount_left -= amount; + amount_left_to_req -= amount; + if (amount_left_to_req == 0) + get_some_more = 0; +@@ -897,19 +920,22 @@ static int do_write(struct fsg_dev *fsg) + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; +- start_transfer(fsg, fsg->bulk_out, bh->outreq, +- &bh->outreq_busy, &bh->state); +- fsg->common->next_buffhd_to_fill = bh->next; ++ START_TRANSFER_OR(common, bulk_out, bh->outreq, ++ &bh->outreq_busy, &bh->state) ++ /* Don't know what to do if ++ * common->fsg is NULL */ ++ return -EIO; ++ common->next_buffhd_to_fill = bh->next; + continue; + } + + /* Write the received data to the backing file */ +- bh = fsg->common->next_buffhd_to_drain; ++ bh = common->next_buffhd_to_drain; + if (bh->state == BUF_STATE_EMPTY && !get_some_more) + break; /* We stopped early */ + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); +- fsg->common->next_buffhd_to_drain = bh->next; ++ common->next_buffhd_to_drain = bh->next; + bh->state = BUF_STATE_EMPTY; + + /* Did something go wrong with the transfer? */ +@@ -952,7 +978,7 @@ static int do_write(struct fsg_dev *fsg) + } + file_offset += nwritten; + amount_left_to_write -= nwritten; +- fsg->residue -= nwritten; ++ common->residue -= nwritten; + + /* If an error occurred, report it and its position */ + if (nwritten < amount) { +@@ -964,14 +990,14 @@ static int do_write(struct fsg_dev *fsg) + + /* Did the host decide to stop early? */ + if (bh->outreq->actual != bh->outreq->length) { +- fsg->short_packet_received = 1; ++ common->short_packet_received = 1; + break; + } + continue; + } + + /* Wait for something to happen */ +- rc = sleep_thread(fsg); ++ rc = sleep_thread(common); + if (rc) + return rc; + } +@@ -982,9 +1008,9 @@ static int do_write(struct fsg_dev *fsg) + + /*-------------------------------------------------------------------------*/ + +-static int do_synchronize_cache(struct fsg_dev *fsg) ++static int do_synchronize_cache(struct fsg_common *common) + { +- struct fsg_lun *curlun = fsg->common->curlun; ++ struct fsg_lun *curlun = common->curlun; + int rc; + + /* We ignore the requested LBA and write out all file's +@@ -1008,12 +1034,12 @@ static void invalidate_sub(struct fsg_lu + VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc); + } + +-static int do_verify(struct fsg_dev *fsg) ++static int do_verify(struct fsg_common *common) + { +- struct fsg_lun *curlun = fsg->common->curlun; ++ struct fsg_lun *curlun = common->curlun; + u32 lba; + u32 verification_length; +- struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; ++ struct fsg_buffhd *bh = common->next_buffhd_to_fill; + loff_t file_offset, file_offset_tmp; + u32 amount_left; + unsigned int amount; +@@ -1021,7 +1047,7 @@ static int do_verify(struct fsg_dev *fsg + + /* Get the starting Logical Block Address and check that it's + * not too big */ +- lba = get_unaligned_be32(&fsg->common->cmnd[2]); ++ lba = get_unaligned_be32(&common->cmnd[2]); + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; +@@ -1029,12 +1055,12 @@ static int do_verify(struct fsg_dev *fsg + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) but we don't implement it. */ +- if ((fsg->common->cmnd[1] & ~0x10) != 0) { ++ if (common->cmnd[1] & ~0x10) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + +- verification_length = get_unaligned_be16(&fsg->common->cmnd[7]); ++ verification_length = get_unaligned_be16(&common->cmnd[7]); + if (unlikely(verification_length == 0)) + return -EIO; /* No default reply */ + +@@ -1106,13 +1132,13 @@ static int do_verify(struct fsg_dev *fsg + + /*-------------------------------------------------------------------------*/ + +-static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) ++static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) + { +- struct fsg_lun *curlun = fsg->common->curlun; ++ struct fsg_lun *curlun = common->curlun; + u8 *buf = (u8 *) bh->buf; + + if (!curlun) { /* Unsupported LUNs are okay */ +- fsg->bad_lun_okay = 1; ++ common->bad_lun_okay = 1; + memset(buf, 0, 36); + buf[0] = 0x7f; /* Unsupported, no device-type */ + buf[4] = 31; /* Additional length */ +@@ -1127,15 +1153,14 @@ static int do_inquiry(struct fsg_dev *fs + buf[5] = 0; /* No special options */ + buf[6] = 0; + buf[7] = 0; +- memcpy(buf + 8, fsg->common->inquiry_string, +- sizeof fsg->common->inquiry_string); ++ memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string); + return 36; + } + + +-static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) ++static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) + { +- struct fsg_lun *curlun = fsg->common->curlun; ++ struct fsg_lun *curlun = common->curlun; + u8 *buf = (u8 *) bh->buf; + u32 sd, sdinfo; + int valid; +@@ -1163,7 +1188,7 @@ static int do_request_sense(struct fsg_d + #endif + + if (!curlun) { /* Unsupported LUNs are okay */ +- fsg->bad_lun_okay = 1; ++ common->bad_lun_okay = 1; + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + sdinfo = 0; + valid = 0; +@@ -1187,11 +1212,11 @@ static int do_request_sense(struct fsg_d + } + + +-static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) ++static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) + { +- struct fsg_lun *curlun = fsg->common->curlun; +- u32 lba = get_unaligned_be32(&fsg->common->cmnd[2]); +- int pmi = fsg->common->cmnd[8]; ++ struct fsg_lun *curlun = common->curlun; ++ u32 lba = get_unaligned_be32(&common->cmnd[2]); ++ int pmi = common->cmnd[8]; + u8 *buf = (u8 *) bh->buf; + + /* Check the PMI and LBA fields */ +@@ -1207,14 +1232,14 @@ static int do_read_capacity(struct fsg_d + } + + +-static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) ++static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) + { +- struct fsg_lun *curlun = fsg->common->curlun; +- int msf = fsg->common->cmnd[1] & 0x02; +- u32 lba = get_unaligned_be32(&fsg->common->cmnd[2]); ++ struct fsg_lun *curlun = common->curlun; ++ int msf = common->cmnd[1] & 0x02; ++ u32 lba = get_unaligned_be32(&common->cmnd[2]); + u8 *buf = (u8 *) bh->buf; + +- if ((fsg->common->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */ ++ if (common->cmnd[1] & ~0x02) { /* Mask away MSF */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } +@@ -1230,14 +1255,14 @@ static int do_read_header(struct fsg_dev + } + + +-static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) ++static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) + { +- struct fsg_lun *curlun = fsg->common->curlun; +- int msf = fsg->common->cmnd[1] & 0x02; +- int start_track = fsg->common->cmnd[6]; ++ struct fsg_lun *curlun = common->curlun; ++ int msf = common->cmnd[1] & 0x02; ++ int start_track = common->cmnd[6]; + u8 *buf = (u8 *) bh->buf; + +- if ((fsg->common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ ++ if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + start_track > 1) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; +@@ -1258,10 +1283,10 @@ static int do_read_toc(struct fsg_dev *f + } + + +-static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) ++static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) + { +- struct fsg_lun *curlun = fsg->common->curlun; +- int mscmnd = fsg->common->cmnd[0]; ++ struct fsg_lun *curlun = common->curlun; ++ int mscmnd = common->cmnd[0]; + u8 *buf = (u8 *) bh->buf; + u8 *buf0 = buf; + int pc, page_code; +@@ -1269,12 +1294,12 @@ static int do_mode_sense(struct fsg_dev + int valid_page = 0; + int len, limit; + +- if ((fsg->common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ ++ if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } +- pc = fsg->common->cmnd[2] >> 6; +- page_code = fsg->common->cmnd[2] & 0x3f; ++ pc = common->cmnd[2] >> 6; ++ page_code = common->cmnd[2] & 0x3f; + if (pc == 3) { + curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; + return -EINVAL; +@@ -1339,32 +1364,32 @@ static int do_mode_sense(struct fsg_dev + } + + +-static int do_start_stop(struct fsg_dev *fsg) ++static int do_start_stop(struct fsg_common *common) + { +- if (!fsg->common->curlun) { ++ if (!common->curlun) { + return -EINVAL; +- } else if (!fsg->common->curlun->removable) { +- fsg->common->curlun->sense_data = SS_INVALID_COMMAND; ++ } else if (!common->curlun->removable) { ++ common->curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + return 0; + } + + +-static int do_prevent_allow(struct fsg_dev *fsg) ++static int do_prevent_allow(struct fsg_common *common) + { +- struct fsg_lun *curlun = fsg->common->curlun; ++ struct fsg_lun *curlun = common->curlun; + int prevent; + +- if (!fsg->common->curlun) { ++ if (!common->curlun) { + return -EINVAL; +- } else if (!fsg->common->curlun->removable) { +- fsg->common->curlun->sense_data = SS_INVALID_COMMAND; ++ } else if (!common->curlun->removable) { ++ common->curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + +- prevent = fsg->common->cmnd[4] & 0x01; +- if ((fsg->common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ ++ prevent = common->cmnd[4] & 0x01; ++ if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } +@@ -1376,10 +1401,10 @@ static int do_prevent_allow(struct fsg_d + } + + +-static int do_read_format_capacities(struct fsg_dev *fsg, ++static int do_read_format_capacities(struct fsg_common *common, + struct fsg_buffhd *bh) + { +- struct fsg_lun *curlun = fsg->common->curlun; ++ struct fsg_lun *curlun = common->curlun; + u8 *buf = (u8 *) bh->buf; + + buf[0] = buf[1] = buf[2] = 0; +@@ -1394,12 +1419,13 @@ static int do_read_format_capacities(str + } + + +-static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh) ++static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) + { +- struct fsg_lun *curlun = fsg->common->curlun; ++ struct fsg_lun *curlun = common->curlun; + + /* We don't support MODE SELECT */ +- curlun->sense_data = SS_INVALID_COMMAND; ++ if (curlun) ++ curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + +@@ -1459,73 +1485,78 @@ static int pad_with_zeros(struct fsg_dev + int rc; + + bh->state = BUF_STATE_EMPTY; /* For the first iteration */ +- fsg->usb_amount_left = nkeep + fsg->residue; +- while (fsg->usb_amount_left > 0) { ++ fsg->common->usb_amount_left = nkeep + fsg->common->residue; ++ while (fsg->common->usb_amount_left > 0) { + + /* Wait for the next buffer to be free */ + while (bh->state != BUF_STATE_EMPTY) { +- rc = sleep_thread(fsg); ++ rc = sleep_thread(fsg->common); + if (rc) + return rc; + } + +- nsend = min(fsg->usb_amount_left, FSG_BUFLEN); ++ nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN); + memset(bh->buf + nkeep, 0, nsend - nkeep); + bh->inreq->length = nsend; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + bh = fsg->common->next_buffhd_to_fill = bh->next; +- fsg->usb_amount_left -= nsend; ++ fsg->common->usb_amount_left -= nsend; + nkeep = 0; + } + return 0; + } + +-static int throw_away_data(struct fsg_dev *fsg) ++static int throw_away_data(struct fsg_common *common) + { + struct fsg_buffhd *bh; + u32 amount; + int rc; + +- for (bh = fsg->common->next_buffhd_to_drain; +- bh->state != BUF_STATE_EMPTY || fsg->usb_amount_left > 0; +- bh = fsg->common->next_buffhd_to_drain) { ++ for (bh = common->next_buffhd_to_drain; ++ bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0; ++ bh = common->next_buffhd_to_drain) { + + /* Throw away the data in a filled buffer */ + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + bh->state = BUF_STATE_EMPTY; +- fsg->common->next_buffhd_to_drain = bh->next; ++ common->next_buffhd_to_drain = bh->next; + + /* A short packet or an error ends everything */ + if (bh->outreq->actual != bh->outreq->length || + bh->outreq->status != 0) { +- raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); ++ raise_exception(common, ++ FSG_STATE_ABORT_BULK_OUT); + return -EINTR; + } + continue; + } + + /* Try to submit another request if we need one */ +- bh = fsg->common->next_buffhd_to_fill; +- if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) { +- amount = min(fsg->usb_amount_left, FSG_BUFLEN); ++ bh = common->next_buffhd_to_fill; ++ if (bh->state == BUF_STATE_EMPTY ++ && common->usb_amount_left > 0) { ++ amount = min(common->usb_amount_left, FSG_BUFLEN); + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; +- start_transfer(fsg, fsg->bulk_out, bh->outreq, +- &bh->outreq_busy, &bh->state); +- fsg->common->next_buffhd_to_fill = bh->next; +- fsg->usb_amount_left -= amount; ++ START_TRANSFER_OR(common, bulk_out, bh->outreq, ++ &bh->outreq_busy, &bh->state) ++ /* Don't know what to do if ++ * common->fsg is NULL */ ++ return -EIO; ++ common->next_buffhd_to_fill = bh->next; ++ common->usb_amount_left -= amount; + continue; + } + + /* Otherwise wait for something to happen */ +- rc = sleep_thread(fsg); ++ rc = sleep_thread(common); + if (rc) + return rc; + } +@@ -1533,12 +1564,12 @@ static int throw_away_data(struct fsg_de + } + + +-static int finish_reply(struct fsg_dev *fsg) ++static int finish_reply(struct fsg_common *common) + { +- struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; ++ struct fsg_buffhd *bh = common->next_buffhd_to_fill; + int rc = 0; + +- switch (fsg->data_dir) { ++ switch (common->data_dir) { + case DATA_DIR_NONE: + break; /* Nothing to send */ + +@@ -1547,47 +1578,60 @@ static int finish_reply(struct fsg_dev * + * try to send or receive any data. So stall both bulk pipes + * if we can and wait for a reset. */ + case DATA_DIR_UNKNOWN: +- if (fsg->can_stall) { +- fsg_set_halt(fsg, fsg->bulk_out); +- rc = halt_bulk_in_endpoint(fsg); ++ if (!common->can_stall) { ++ /* Nothing */ ++ } else if (fsg_is_set(common)) { ++ fsg_set_halt(common->fsg, common->fsg->bulk_out); ++ rc = halt_bulk_in_endpoint(common->fsg); ++ } else { ++ /* Don't know what to do if common->fsg is NULL */ ++ rc = -EIO; + } + break; + + /* All but the last buffer of data must have already been sent */ + case DATA_DIR_TO_HOST: +- if (fsg->data_size == 0) { ++ if (common->data_size == 0) { + /* Nothing to send */ + + /* If there's no residue, simply send the last buffer */ +- } else if (fsg->residue == 0) { ++ } else if (common->residue == 0) { + bh->inreq->zero = 0; +- start_transfer(fsg, fsg->bulk_in, bh->inreq, +- &bh->inreq_busy, &bh->state); +- fsg->common->next_buffhd_to_fill = bh->next; ++ START_TRANSFER_OR(common, bulk_in, bh->inreq, ++ &bh->inreq_busy, &bh->state) ++ return -EIO; ++ common->next_buffhd_to_fill = bh->next; + + /* For Bulk-only, if we're allowed to stall then send the + * short packet and halt the bulk-in endpoint. If we can't + * stall, pad out the remaining data with 0's. */ +- } else if (fsg->can_stall) { ++ } else if (common->can_stall) { + bh->inreq->zero = 1; +- start_transfer(fsg, fsg->bulk_in, bh->inreq, +- &bh->inreq_busy, &bh->state); +- fsg->common->next_buffhd_to_fill = bh->next; +- rc = halt_bulk_in_endpoint(fsg); ++ START_TRANSFER_OR(common, bulk_in, bh->inreq, ++ &bh->inreq_busy, &bh->state) ++ /* Don't know what to do if ++ * common->fsg is NULL */ ++ rc = -EIO; ++ common->next_buffhd_to_fill = bh->next; ++ if (common->fsg) ++ rc = halt_bulk_in_endpoint(common->fsg); ++ } else if (fsg_is_set(common)) { ++ rc = pad_with_zeros(common->fsg); + } else { +- rc = pad_with_zeros(fsg); ++ /* Don't know what to do if common->fsg is NULL */ ++ rc = -EIO; + } + break; + + /* We have processed all we want from the data the host has sent. + * There may still be outstanding bulk-out requests. */ + case DATA_DIR_FROM_HOST: +- if (fsg->residue == 0) { ++ if (common->residue == 0) { + /* Nothing to receive */ + + /* Did the host stop sending unexpectedly early? */ +- } else if (fsg->short_packet_received) { +- raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); ++ } else if (common->short_packet_received) { ++ raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; + + /* We haven't processed all the incoming data. Even though +@@ -1597,16 +1641,18 @@ static int finish_reply(struct fsg_dev * + * STALL. Not realizing the endpoint was halted, it wouldn't + * clear the halt -- leading to problems later on. */ + #if 0 +- } else if (fsg->can_stall) { +- fsg_set_halt(fsg, fsg->bulk_out); +- raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); ++ } else if (common->can_stall) { ++ if (fsg_is_set(common)) ++ fsg_set_halt(common->fsg, ++ common->fsg->bulk_out); ++ raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; + #endif + + /* We can't stall. Read in the excess data and throw it + * all away. */ + } else { +- rc = throw_away_data(fsg); ++ rc = throw_away_data(common); + } + break; + } +@@ -1614,9 +1660,9 @@ static int finish_reply(struct fsg_dev * + } + + +-static int send_status(struct fsg_dev *fsg) ++static int send_status(struct fsg_common *common) + { +- struct fsg_lun *curlun = fsg->common->curlun; ++ struct fsg_lun *curlun = common->curlun; + struct fsg_buffhd *bh; + struct bulk_cs_wrap *csw; + int rc; +@@ -1624,9 +1670,9 @@ static int send_status(struct fsg_dev *f + u32 sd, sdinfo = 0; + + /* Wait for the next buffer to become available */ +- bh = fsg->common->next_buffhd_to_fill; ++ bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { +- rc = sleep_thread(fsg); ++ rc = sleep_thread(common); + if (rc) + return rc; + } +@@ -1634,19 +1680,19 @@ static int send_status(struct fsg_dev *f + if (curlun) { + sd = curlun->sense_data; + sdinfo = curlun->sense_data_info; +- } else if (fsg->bad_lun_okay) ++ } else if (common->bad_lun_okay) + sd = SS_NO_SENSE; + else + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + +- if (fsg->phase_error) { +- DBG(fsg, "sending phase-error status\n"); ++ if (common->phase_error) { ++ DBG(common, "sending phase-error status\n"); + status = USB_STATUS_PHASE_ERROR; + sd = SS_INVALID_COMMAND; + } else if (sd != SS_NO_SENSE) { +- DBG(fsg, "sending command-failure status\n"); ++ DBG(common, "sending command-failure status\n"); + status = USB_STATUS_FAIL; +- VDBG(fsg, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" ++ VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" + " info x%x\n", + SK(sd), ASC(sd), ASCQ(sd), sdinfo); + } +@@ -1655,16 +1701,18 @@ static int send_status(struct fsg_dev *f + csw = (void *)bh->buf; + + csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); +- csw->Tag = fsg->tag; +- csw->Residue = cpu_to_le32(fsg->residue); ++ csw->Tag = common->tag; ++ csw->Residue = cpu_to_le32(common->residue); + csw->Status = status; + + bh->inreq->length = USB_BULK_CS_WRAP_LEN; + bh->inreq->zero = 0; +- start_transfer(fsg, fsg->bulk_in, bh->inreq, +- &bh->inreq_busy, &bh->state); ++ START_TRANSFER_OR(common, bulk_in, bh->inreq, ++ &bh->inreq_busy, &bh->state) ++ /* Don't know what to do if common->fsg is NULL */ ++ return -EIO; + +- fsg->common->next_buffhd_to_fill = bh->next; ++ common->next_buffhd_to_fill = bh->next; + return 0; + } + +@@ -1673,52 +1721,47 @@ static int send_status(struct fsg_dev *f + + /* Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. */ +-static int check_command(struct fsg_dev *fsg, int cmnd_size, ++static int check_command(struct fsg_common *common, int cmnd_size, + enum data_direction data_dir, unsigned int mask, + int needs_medium, const char *name) + { + int i; +- int lun = fsg->common->cmnd[1] >> 5; ++ int lun = common->cmnd[1] >> 5; + static const char dirletter[4] = {'u', 'o', 'i', 'n'}; + char hdlen[20]; + struct fsg_lun *curlun; + + hdlen[0] = 0; +- if (fsg->data_dir != DATA_DIR_UNKNOWN) +- sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir], +- fsg->data_size); +- VDBG(fsg, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", ++ if (common->data_dir != DATA_DIR_UNKNOWN) ++ sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir], ++ common->data_size); ++ VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", + name, cmnd_size, dirletter[(int) data_dir], +- fsg->data_size_from_cmnd, fsg->common->cmnd_size, hdlen); ++ common->data_size_from_cmnd, common->cmnd_size, hdlen); + + /* We can't reply at all until we know the correct data direction + * and size. */ +- if (fsg->data_size_from_cmnd == 0) ++ if (common->data_size_from_cmnd == 0) + data_dir = DATA_DIR_NONE; +- if (fsg->data_dir == DATA_DIR_UNKNOWN) { /* CB or CBI */ +- fsg->data_dir = data_dir; +- fsg->data_size = fsg->data_size_from_cmnd; +- +- } else { /* Bulk-only */ +- if (fsg->data_size < fsg->data_size_from_cmnd) { +- +- /* Host data size < Device data size is a phase error. +- * Carry out the command, but only transfer as much +- * as we are allowed. */ +- fsg->data_size_from_cmnd = fsg->data_size; +- fsg->phase_error = 1; +- } ++ if (common->data_size < common->data_size_from_cmnd) { ++ /* Host data size < Device data size is a phase error. ++ * Carry out the command, but only transfer as much as ++ * we are allowed. */ ++ common->data_size_from_cmnd = common->data_size; ++ common->phase_error = 1; + } +- fsg->residue = fsg->usb_amount_left = fsg->data_size; ++ common->residue = common->data_size; ++ common->usb_amount_left = common->data_size; + + /* Conflicting data directions is a phase error */ +- if (fsg->data_dir != data_dir && fsg->data_size_from_cmnd > 0) { +- fsg->phase_error = 1; ++ if (common->data_dir != data_dir ++ && common->data_size_from_cmnd > 0) { ++ common->phase_error = 1; + return -EINVAL; + } + + /* Verify the length of the command itself */ +- if (cmnd_size != fsg->common->cmnd_size) { ++ if (cmnd_size != common->cmnd_size) { + + /* Special case workaround: There are plenty of buggy SCSI + * implementations. Many have issues with cbw->Length +@@ -1732,40 +1775,41 @@ static int check_command(struct fsg_dev + * REQUEST SENSE with cbw->Length == 10 where it should + * be 6 as well. + */ +- if (cmnd_size <= fsg->common->cmnd_size) { +- DBG(fsg, "%s is buggy! Expected length %d " ++ if (cmnd_size <= common->cmnd_size) { ++ DBG(common, "%s is buggy! Expected length %d " + "but we got %d\n", name, +- cmnd_size, fsg->common->cmnd_size); +- cmnd_size = fsg->common->cmnd_size; ++ cmnd_size, common->cmnd_size); ++ cmnd_size = common->cmnd_size; + } else { +- fsg->phase_error = 1; ++ common->phase_error = 1; + return -EINVAL; + } + } + + /* Check that the LUN values are consistent */ +- if (fsg->common->lun != lun) +- DBG(fsg, "using LUN %d from CBW, not LUN %d from CDB\n", +- fsg->common->lun, lun); ++ if (common->lun != lun) ++ DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n", ++ common->lun, lun); + + /* Check the LUN */ +- if (fsg->common->lun >= 0 && fsg->common->lun < fsg->common->nluns) { +- curlun = &fsg->common->luns[fsg->common->lun]; +- fsg->common->curlun = curlun; +- if (fsg->common->cmnd[0] != SC_REQUEST_SENSE) { ++ if (common->lun >= 0 && common->lun < common->nluns) { ++ curlun = &common->luns[common->lun]; ++ common->curlun = curlun; ++ if (common->cmnd[0] != SC_REQUEST_SENSE) { + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + } else { +- fsg->common->curlun = curlun = NULL; +- fsg->bad_lun_okay = 0; ++ common->curlun = NULL; ++ curlun = NULL; ++ common->bad_lun_okay = 0; + + /* INQUIRY and REQUEST SENSE commands are explicitly allowed + * to use unsupported LUNs; all others may not. */ +- if (fsg->common->cmnd[0] != SC_INQUIRY && +- fsg->common->cmnd[0] != SC_REQUEST_SENSE) { +- DBG(fsg, "unsupported LUN %d\n", fsg->common->lun); ++ if (common->cmnd[0] != SC_INQUIRY && ++ common->cmnd[0] != SC_REQUEST_SENSE) { ++ DBG(common, "unsupported LUN %d\n", common->lun); + return -EINVAL; + } + } +@@ -1773,17 +1817,17 @@ static int check_command(struct fsg_dev + /* If a unit attention condition exists, only INQUIRY and + * REQUEST SENSE commands are allowed; anything else must fail. */ + if (curlun && curlun->unit_attention_data != SS_NO_SENSE && +- fsg->common->cmnd[0] != SC_INQUIRY && +- fsg->common->cmnd[0] != SC_REQUEST_SENSE) { ++ common->cmnd[0] != SC_INQUIRY && ++ common->cmnd[0] != SC_REQUEST_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + return -EINVAL; + } + + /* Check that only command bytes listed in the mask are non-zero */ +- fsg->common->cmnd[1] &= 0x1f; /* Mask away the LUN */ ++ common->cmnd[1] &= 0x1f; /* Mask away the LUN */ + for (i = 1; i < cmnd_size; ++i) { +- if (fsg->common->cmnd[i] && !(mask & (1 << i))) { ++ if (common->cmnd[i] && !(mask & (1 << i))) { + if (curlun) + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; +@@ -1801,7 +1845,7 @@ static int check_command(struct fsg_dev + } + + +-static int do_scsi_command(struct fsg_dev *fsg) ++static int do_scsi_command(struct fsg_common *common) + { + struct fsg_buffhd *bh; + int rc; +@@ -1809,182 +1853,181 @@ static int do_scsi_command(struct fsg_de + int i; + static char unknown[16]; + +- dump_cdb(fsg->common); ++ dump_cdb(common); + + /* Wait for the next buffer to become available for data or status */ +- bh = fsg->common->next_buffhd_to_fill; +- fsg->common->next_buffhd_to_drain = bh; ++ bh = common->next_buffhd_to_fill; ++ common->next_buffhd_to_drain = bh; + while (bh->state != BUF_STATE_EMPTY) { +- rc = sleep_thread(fsg); ++ rc = sleep_thread(common); + if (rc) + return rc; + } +- fsg->phase_error = 0; +- fsg->short_packet_received = 0; ++ common->phase_error = 0; ++ common->short_packet_received = 0; + +- /* We're using the backing file */ +- down_read(&fsg->common->filesem); +- switch (fsg->common->cmnd[0]) { ++ down_read(&common->filesem); /* We're using the backing file */ ++ switch (common->cmnd[0]) { + + case SC_INQUIRY: +- fsg->data_size_from_cmnd = fsg->common->cmnd[4]; +- reply = check_command(fsg, 6, DATA_DIR_TO_HOST, ++ common->data_size_from_cmnd = common->cmnd[4]; ++ reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "INQUIRY"); + if (reply == 0) +- reply = do_inquiry(fsg, bh); ++ reply = do_inquiry(common, bh); + break; + + case SC_MODE_SELECT_6: +- fsg->data_size_from_cmnd = fsg->common->cmnd[4]; +- reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, ++ common->data_size_from_cmnd = common->cmnd[4]; ++ reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (1<<1) | (1<<4), 0, + "MODE SELECT(6)"); + if (reply == 0) +- reply = do_mode_select(fsg, bh); ++ reply = do_mode_select(common, bh); + break; + + case SC_MODE_SELECT_10: +- fsg->data_size_from_cmnd = +- get_unaligned_be16(&fsg->common->cmnd[7]); +- reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, ++ common->data_size_from_cmnd = ++ get_unaligned_be16(&common->cmnd[7]); ++ reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (3<<7), 0, + "MODE SELECT(10)"); + if (reply == 0) +- reply = do_mode_select(fsg, bh); ++ reply = do_mode_select(common, bh); + break; + + case SC_MODE_SENSE_6: +- fsg->data_size_from_cmnd = fsg->common->cmnd[4]; +- reply = check_command(fsg, 6, DATA_DIR_TO_HOST, ++ common->data_size_from_cmnd = common->cmnd[4]; ++ reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4), 0, + "MODE SENSE(6)"); + if (reply == 0) +- reply = do_mode_sense(fsg, bh); ++ reply = do_mode_sense(common, bh); + break; + + case SC_MODE_SENSE_10: +- fsg->data_size_from_cmnd = +- get_unaligned_be16(&fsg->common->cmnd[7]); +- reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ common->data_size_from_cmnd = ++ get_unaligned_be16(&common->cmnd[7]); ++ reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7), 0, + "MODE SENSE(10)"); + if (reply == 0) +- reply = do_mode_sense(fsg, bh); ++ reply = do_mode_sense(common, bh); + break; + + case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: +- fsg->data_size_from_cmnd = 0; +- reply = check_command(fsg, 6, DATA_DIR_NONE, ++ common->data_size_from_cmnd = 0; ++ reply = check_command(common, 6, DATA_DIR_NONE, + (1<<4), 0, + "PREVENT-ALLOW MEDIUM REMOVAL"); + if (reply == 0) +- reply = do_prevent_allow(fsg); ++ reply = do_prevent_allow(common); + break; + + case SC_READ_6: +- i = fsg->common->cmnd[4]; +- fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; +- reply = check_command(fsg, 6, DATA_DIR_TO_HOST, ++ i = common->cmnd[4]; ++ common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; ++ reply = check_command(common, 6, DATA_DIR_TO_HOST, + (7<<1) | (1<<4), 1, + "READ(6)"); + if (reply == 0) +- reply = do_read(fsg); ++ reply = do_read(common); + break; + + case SC_READ_10: +- fsg->data_size_from_cmnd = +- get_unaligned_be16(&fsg->common->cmnd[7]) << 9; +- reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ common->data_size_from_cmnd = ++ get_unaligned_be16(&common->cmnd[7]) << 9; ++ reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "READ(10)"); + if (reply == 0) +- reply = do_read(fsg); ++ reply = do_read(common); + break; + + case SC_READ_12: +- fsg->data_size_from_cmnd = +- get_unaligned_be32(&fsg->common->cmnd[6]) << 9; +- reply = check_command(fsg, 12, DATA_DIR_TO_HOST, ++ common->data_size_from_cmnd = ++ get_unaligned_be32(&common->cmnd[6]) << 9; ++ reply = check_command(common, 12, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "READ(12)"); + if (reply == 0) +- reply = do_read(fsg); ++ reply = do_read(common); + break; + + case SC_READ_CAPACITY: +- fsg->data_size_from_cmnd = 8; +- reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ common->data_size_from_cmnd = 8; ++ reply = check_command(common, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (1<<8), 1, + "READ CAPACITY"); + if (reply == 0) +- reply = do_read_capacity(fsg, bh); ++ reply = do_read_capacity(common, bh); + break; + + case SC_READ_HEADER: +- if (!fsg->common->curlun || !fsg->common->curlun->cdrom) ++ if (!common->curlun || !common->curlun->cdrom) + goto unknown_cmnd; +- fsg->data_size_from_cmnd = +- get_unaligned_be16(&fsg->common->cmnd[7]); +- reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ common->data_size_from_cmnd = ++ get_unaligned_be16(&common->cmnd[7]); ++ reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER"); + if (reply == 0) +- reply = do_read_header(fsg, bh); ++ reply = do_read_header(common, bh); + break; + + case SC_READ_TOC: +- if (!fsg->common->curlun || !fsg->common->curlun->cdrom) ++ if (!common->curlun || !common->curlun->cdrom) + goto unknown_cmnd; +- fsg->data_size_from_cmnd = +- get_unaligned_be16(&fsg->common->cmnd[7]); +- reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ common->data_size_from_cmnd = ++ get_unaligned_be16(&common->cmnd[7]); ++ reply = check_command(common, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC"); + if (reply == 0) +- reply = do_read_toc(fsg, bh); ++ reply = do_read_toc(common, bh); + break; + + case SC_READ_FORMAT_CAPACITIES: +- fsg->data_size_from_cmnd = +- get_unaligned_be16(&fsg->common->cmnd[7]); +- reply = check_command(fsg, 10, DATA_DIR_TO_HOST, ++ common->data_size_from_cmnd = ++ get_unaligned_be16(&common->cmnd[7]); ++ reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7), 1, + "READ FORMAT CAPACITIES"); + if (reply == 0) +- reply = do_read_format_capacities(fsg, bh); ++ reply = do_read_format_capacities(common, bh); + break; + + case SC_REQUEST_SENSE: +- fsg->data_size_from_cmnd = fsg->common->cmnd[4]; +- reply = check_command(fsg, 6, DATA_DIR_TO_HOST, ++ common->data_size_from_cmnd = common->cmnd[4]; ++ reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "REQUEST SENSE"); + if (reply == 0) +- reply = do_request_sense(fsg, bh); ++ reply = do_request_sense(common, bh); + break; + + case SC_START_STOP_UNIT: +- fsg->data_size_from_cmnd = 0; +- reply = check_command(fsg, 6, DATA_DIR_NONE, ++ common->data_size_from_cmnd = 0; ++ reply = check_command(common, 6, DATA_DIR_NONE, + (1<<1) | (1<<4), 0, + "START-STOP UNIT"); + if (reply == 0) +- reply = do_start_stop(fsg); ++ reply = do_start_stop(common); + break; + + case SC_SYNCHRONIZE_CACHE: +- fsg->data_size_from_cmnd = 0; +- reply = check_command(fsg, 10, DATA_DIR_NONE, ++ common->data_size_from_cmnd = 0; ++ reply = check_command(common, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7), 1, + "SYNCHRONIZE CACHE"); + if (reply == 0) +- reply = do_synchronize_cache(fsg); ++ reply = do_synchronize_cache(common); + break; + + case SC_TEST_UNIT_READY: +- fsg->data_size_from_cmnd = 0; +- reply = check_command(fsg, 6, DATA_DIR_NONE, ++ common->data_size_from_cmnd = 0; ++ reply = check_command(common, 6, DATA_DIR_NONE, + 0, 1, + "TEST UNIT READY"); + break; +@@ -1992,42 +2035,42 @@ static int do_scsi_command(struct fsg_de + /* Although optional, this command is used by MS-Windows. We + * support a minimal version: BytChk must be 0. */ + case SC_VERIFY: +- fsg->data_size_from_cmnd = 0; +- reply = check_command(fsg, 10, DATA_DIR_NONE, ++ common->data_size_from_cmnd = 0; ++ reply = check_command(common, 10, DATA_DIR_NONE, + (1<<1) | (0xf<<2) | (3<<7), 1, + "VERIFY"); + if (reply == 0) +- reply = do_verify(fsg); ++ reply = do_verify(common); + break; + + case SC_WRITE_6: +- i = fsg->common->cmnd[4]; +- fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; +- reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, ++ i = common->cmnd[4]; ++ common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; ++ reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (7<<1) | (1<<4), 1, + "WRITE(6)"); + if (reply == 0) +- reply = do_write(fsg); ++ reply = do_write(common); + break; + + case SC_WRITE_10: +- fsg->data_size_from_cmnd = +- get_unaligned_be16(&fsg->common->cmnd[7]) << 9; +- reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, ++ common->data_size_from_cmnd = ++ get_unaligned_be16(&common->cmnd[7]) << 9; ++ reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "WRITE(10)"); + if (reply == 0) +- reply = do_write(fsg); ++ reply = do_write(common); + break; + + case SC_WRITE_12: +- fsg->data_size_from_cmnd = +- get_unaligned_be32(&fsg->common->cmnd[6]) << 9; +- reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, ++ common->data_size_from_cmnd = ++ get_unaligned_be32(&common->cmnd[6]) << 9; ++ reply = check_command(common, 12, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "WRITE(12)"); + if (reply == 0) +- reply = do_write(fsg); ++ reply = do_write(common); + break; + + /* Some mandatory commands that we recognize but don't implement. +@@ -2042,17 +2085,17 @@ static int do_scsi_command(struct fsg_de + + default: + unknown_cmnd: +- fsg->data_size_from_cmnd = 0; +- sprintf(unknown, "Unknown x%02x", fsg->common->cmnd[0]); +- reply = check_command(fsg, fsg->common->cmnd_size, ++ common->data_size_from_cmnd = 0; ++ sprintf(unknown, "Unknown x%02x", common->cmnd[0]); ++ reply = check_command(common, common->cmnd_size, + DATA_DIR_UNKNOWN, 0xff, 0, unknown); + if (reply == 0) { +- fsg->common->curlun->sense_data = SS_INVALID_COMMAND; ++ common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } + break; + } +- up_read(&fsg->common->filesem); ++ up_read(&common->filesem); + + if (reply == -EINTR || signal_pending(current)) + return -EINTR; +@@ -2060,11 +2103,11 @@ unknown_cmnd: + /* Set up the single reply buffer for finish_reply() */ + if (reply == -EINVAL) + reply = 0; /* Error reply length */ +- if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) { +- reply = min((u32) reply, fsg->data_size_from_cmnd); ++ if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { ++ reply = min((u32) reply, common->data_size_from_cmnd); + bh->inreq->length = reply; + bh->state = BUF_STATE_FULL; +- fsg->residue -= reply; ++ common->residue -= reply; + } /* Otherwise it's already set */ + + return 0; +@@ -2075,8 +2118,9 @@ unknown_cmnd: + + static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) + { +- struct usb_request *req = bh->outreq; ++ struct usb_request *req = bh->outreq; + struct fsg_bulk_cb_wrap *cbw = req->buf; ++ struct fsg_common *common = fsg->common; + + /* Was this a real packet? Should it be ignored? */ + if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) +@@ -2113,7 +2157,7 @@ static int received_cbw(struct fsg_dev * + + /* We can do anything we want here, so let's stall the + * bulk pipes if we are allowed to. */ +- if (fsg->can_stall) { ++ if (common->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + halt_bulk_in_endpoint(fsg); + } +@@ -2121,39 +2165,41 @@ static int received_cbw(struct fsg_dev * + } + + /* Save the command for later */ +- fsg->common->cmnd_size = cbw->Length; +- memcpy(fsg->common->cmnd, cbw->CDB, fsg->common->cmnd_size); ++ common->cmnd_size = cbw->Length; ++ memcpy(common->cmnd, cbw->CDB, common->cmnd_size); + if (cbw->Flags & USB_BULK_IN_FLAG) +- fsg->data_dir = DATA_DIR_TO_HOST; ++ common->data_dir = DATA_DIR_TO_HOST; + else +- fsg->data_dir = DATA_DIR_FROM_HOST; +- fsg->data_size = le32_to_cpu(cbw->DataTransferLength); +- if (fsg->data_size == 0) +- fsg->data_dir = DATA_DIR_NONE; +- fsg->common->lun = cbw->Lun; +- fsg->tag = cbw->Tag; ++ common->data_dir = DATA_DIR_FROM_HOST; ++ common->data_size = le32_to_cpu(cbw->DataTransferLength); ++ if (common->data_size == 0) ++ common->data_dir = DATA_DIR_NONE; ++ common->lun = cbw->Lun; ++ common->tag = cbw->Tag; + return 0; + } + + +-static int get_next_command(struct fsg_dev *fsg) ++static int get_next_command(struct fsg_common *common) + { + struct fsg_buffhd *bh; + int rc = 0; + + /* Wait for the next buffer to become available */ +- bh = fsg->common->next_buffhd_to_fill; ++ bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { +- rc = sleep_thread(fsg); ++ rc = sleep_thread(common); + if (rc) + return rc; + } + + /* Queue a request to read a Bulk-only CBW */ +- set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); ++ set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); + bh->outreq->short_not_ok = 1; +- start_transfer(fsg, fsg->bulk_out, bh->outreq, +- &bh->outreq_busy, &bh->state); ++ START_TRANSFER_OR(common, bulk_out, bh->outreq, ++ &bh->outreq_busy, &bh->state) ++ /* Don't know what to do if common->fsg is NULL */ ++ return -EIO; + + /* We will drain the buffer in software, which means we + * can reuse it for the next filling. No need to advance +@@ -2161,12 +2207,12 @@ static int get_next_command(struct fsg_d + + /* Wait for the CBW to arrive */ + while (bh->state != BUF_STATE_FULL) { +- rc = sleep_thread(fsg); ++ rc = sleep_thread(common); + if (rc) + return rc; + } + smp_rmb(); +- rc = received_cbw(fsg, bh); ++ rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO; + bh->state = BUF_STATE_EMPTY; + + return rc; +@@ -2175,25 +2221,25 @@ static int get_next_command(struct fsg_d + + /*-------------------------------------------------------------------------*/ + +-static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep, ++static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep, + const struct usb_endpoint_descriptor *d) + { + int rc; + +- ep->driver_data = fsg; ++ ep->driver_data = common; + rc = usb_ep_enable(ep, d); + if (rc) +- ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc); ++ ERROR(common, "can't enable %s, result %d\n", ep->name, rc); + return rc; + } + +-static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep, ++static int alloc_request(struct fsg_common *common, struct usb_ep *ep, + struct usb_request **preq) + { + *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (*preq) + return 0; +- ERROR(fsg, "can't allocate request for %s\n", ep->name); ++ ERROR(common, "can't allocate request for %s\n", ep->name); + return -ENOMEM; + } + +@@ -2202,83 +2248,96 @@ static int alloc_request(struct fsg_dev + * Call with altsetting < 0 to disable the interface. The only other + * available altsetting is 0, which enables the interface. + */ +-static int do_set_interface(struct fsg_dev *fsg, int altsetting) ++static int do_set_interface(struct fsg_common *common, int altsetting) + { + int rc = 0; + int i; + const struct usb_endpoint_descriptor *d; + +- if (fsg->running) +- DBG(fsg, "reset interface\n"); ++ if (common->running) ++ DBG(common, "reset interface\n"); + + reset: + /* Deallocate the requests */ +- for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +- struct fsg_buffhd *bh = &fsg->common->buffhds[i]; ++ if (common->prev_fsg) { ++ struct fsg_dev *fsg = common->prev_fsg; ++ ++ for (i = 0; i < FSG_NUM_BUFFERS; ++i) { ++ struct fsg_buffhd *bh = &common->buffhds[i]; + +- if (bh->inreq) { +- usb_ep_free_request(fsg->bulk_in, bh->inreq); +- bh->inreq = NULL; ++ if (bh->inreq) { ++ usb_ep_free_request(fsg->bulk_in, bh->inreq); ++ bh->inreq = NULL; ++ } ++ if (bh->outreq) { ++ usb_ep_free_request(fsg->bulk_out, bh->outreq); ++ bh->outreq = NULL; ++ } + } +- if (bh->outreq) { +- usb_ep_free_request(fsg->bulk_out, bh->outreq); +- bh->outreq = NULL; ++ ++ /* Disable the endpoints */ ++ if (fsg->bulk_in_enabled) { ++ usb_ep_disable(fsg->bulk_in); ++ fsg->bulk_in_enabled = 0; ++ } ++ if (fsg->bulk_out_enabled) { ++ usb_ep_disable(fsg->bulk_out); ++ fsg->bulk_out_enabled = 0; + } +- } + +- /* Disable the endpoints */ +- if (fsg->bulk_in_enabled) { +- usb_ep_disable(fsg->bulk_in); +- fsg->bulk_in_enabled = 0; +- } +- if (fsg->bulk_out_enabled) { +- usb_ep_disable(fsg->bulk_out); +- fsg->bulk_out_enabled = 0; ++ common->prev_fsg = 0; + } + +- fsg->running = 0; ++ common->running = 0; + if (altsetting < 0 || rc != 0) + return rc; + +- DBG(fsg, "set interface %d\n", altsetting); +- +- /* Enable the endpoints */ +- d = fsg_ep_desc(fsg->gadget, +- &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); +- rc = enable_endpoint(fsg, fsg->bulk_in, d); +- if (rc != 0) +- goto reset; +- fsg->bulk_in_enabled = 1; +- +- d = fsg_ep_desc(fsg->gadget, +- &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); +- rc = enable_endpoint(fsg, fsg->bulk_out, d); +- if (rc != 0) +- goto reset; +- fsg->bulk_out_enabled = 1; +- fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); +- clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); ++ DBG(common, "set interface %d\n", altsetting); + +- /* Allocate the requests */ +- for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +- struct fsg_buffhd *bh = &fsg->common->buffhds[i]; +- +- rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq); +- if (rc != 0) ++ if (fsg_is_set(common)) { ++ struct fsg_dev *fsg = common->fsg; ++ common->prev_fsg = common->fsg; ++ ++ /* Enable the endpoints */ ++ d = fsg_ep_desc(common->gadget, ++ &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); ++ rc = enable_endpoint(common, fsg->bulk_in, d); ++ if (rc) + goto reset; +- rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq); +- if (rc != 0) ++ fsg->bulk_in_enabled = 1; ++ ++ d = fsg_ep_desc(common->gadget, ++ &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); ++ rc = enable_endpoint(common, fsg->bulk_out, d); ++ if (rc) + goto reset; +- bh->inreq->buf = bh->outreq->buf = bh->buf; +- bh->inreq->context = bh->outreq->context = bh; +- bh->inreq->complete = bulk_in_complete; +- bh->outreq->complete = bulk_out_complete; +- } ++ fsg->bulk_out_enabled = 1; ++ common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); ++ clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + +- fsg->running = 1; +- for (i = 0; i < fsg->common->nluns; ++i) +- fsg->common->luns[i].unit_attention_data = SS_RESET_OCCURRED; +- return rc; ++ /* Allocate the requests */ ++ for (i = 0; i < FSG_NUM_BUFFERS; ++i) { ++ struct fsg_buffhd *bh = &common->buffhds[i]; ++ ++ rc = alloc_request(common, fsg->bulk_in, &bh->inreq); ++ if (rc) ++ goto reset; ++ rc = alloc_request(common, fsg->bulk_out, &bh->outreq); ++ if (rc) ++ goto reset; ++ bh->inreq->buf = bh->outreq->buf = bh->buf; ++ bh->inreq->context = bh->outreq->context = bh; ++ bh->inreq->complete = bulk_in_complete; ++ bh->outreq->complete = bulk_out_complete; ++ } ++ ++ common->running = 1; ++ for (i = 0; i < common->nluns; ++i) ++ common->luns[i].unit_attention_data = SS_RESET_OCCURRED; ++ return rc; ++ } else { ++ return -EIO; ++ } + } + + +@@ -2290,23 +2349,23 @@ reset: + * configurations might not work with our current power sources. + * For now we just assume the gadget is always self-powered. + */ +-static int do_set_config(struct fsg_dev *fsg, u8 new_config) ++static int do_set_config(struct fsg_common *common, u8 new_config) + { + int rc = 0; + + /* Disable the single interface */ +- if (fsg->config != 0) { +- DBG(fsg, "reset config\n"); +- fsg->config = 0; +- rc = do_set_interface(fsg, -1); ++ if (common->config != 0) { ++ DBG(common, "reset config\n"); ++ common->config = 0; ++ rc = do_set_interface(common, -1); + } + + /* Enable the interface */ + if (new_config != 0) { +- fsg->config = new_config; +- rc = do_set_interface(fsg, 0); ++ common->config = new_config; ++ rc = do_set_interface(common, 0); + if (rc != 0) +- fsg->config = 0; /* Reset on errors */ ++ common->config = 0; /* Reset on errors */ + } + return rc; + } +@@ -2318,22 +2377,26 @@ static int do_set_config(struct fsg_dev + static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) + { + struct fsg_dev *fsg = fsg_from_func(f); +- fsg->new_config = 1; +- raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); ++ fsg->common->prev_fsg = fsg->common->fsg; ++ fsg->common->fsg = fsg; ++ fsg->common->new_config = 1; ++ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + return 0; + } + + static void fsg_disable(struct usb_function *f) + { + struct fsg_dev *fsg = fsg_from_func(f); +- fsg->new_config = 0; +- raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); ++ fsg->common->prev_fsg = fsg->common->fsg; ++ fsg->common->fsg = fsg; ++ fsg->common->new_config = 0; ++ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + } + + + /*-------------------------------------------------------------------------*/ + +-static void handle_exception(struct fsg_dev *fsg) ++static void handle_exception(struct fsg_common *common) + { + siginfo_t info; + int sig; +@@ -2352,113 +2415,121 @@ static void handle_exception(struct fsg_ + if (!sig) + break; + if (sig != SIGUSR1) { +- if (fsg->state < FSG_STATE_EXIT) +- DBG(fsg, "Main thread exiting on signal\n"); +- raise_exception(fsg, FSG_STATE_EXIT); ++ if (common->state < FSG_STATE_EXIT) ++ DBG(common, "Main thread exiting on signal\n"); ++ raise_exception(common, FSG_STATE_EXIT); + } + } + + /* Cancel all the pending transfers */ +- for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +- bh = &fsg->common->buffhds[i]; +- if (bh->inreq_busy) +- usb_ep_dequeue(fsg->bulk_in, bh->inreq); +- if (bh->outreq_busy) +- usb_ep_dequeue(fsg->bulk_out, bh->outreq); +- } +- +- /* Wait until everything is idle */ +- for (;;) { +- int num_active = 0; ++ if (fsg_is_set(common)) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +- bh = &fsg->common->buffhds[i]; +- num_active += bh->inreq_busy + bh->outreq_busy; ++ bh = &common->buffhds[i]; ++ if (bh->inreq_busy) ++ usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); ++ if (bh->outreq_busy) ++ usb_ep_dequeue(common->fsg->bulk_out, ++ bh->outreq); + } +- if (num_active == 0) +- break; +- if (sleep_thread(fsg)) +- return; +- } + +- /* Clear out the controller's fifos */ +- if (fsg->bulk_in_enabled) +- usb_ep_fifo_flush(fsg->bulk_in); +- if (fsg->bulk_out_enabled) +- usb_ep_fifo_flush(fsg->bulk_out); ++ /* Wait until everything is idle */ ++ for (;;) { ++ int num_active = 0; ++ for (i = 0; i < FSG_NUM_BUFFERS; ++i) { ++ bh = &common->buffhds[i]; ++ num_active += bh->inreq_busy + bh->outreq_busy; ++ } ++ if (num_active == 0) ++ break; ++ if (sleep_thread(common)) ++ return; ++ } ++ ++ /* Clear out the controller's fifos */ ++ if (common->fsg->bulk_in_enabled) ++ usb_ep_fifo_flush(common->fsg->bulk_in); ++ if (common->fsg->bulk_out_enabled) ++ usb_ep_fifo_flush(common->fsg->bulk_out); ++ } + + /* Reset the I/O buffer states and pointers, the SCSI + * state, and the exception. Then invoke the handler. */ +- spin_lock_irq(&fsg->lock); ++ spin_lock_irq(&common->lock); + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +- bh = &fsg->common->buffhds[i]; ++ bh = &common->buffhds[i]; + bh->state = BUF_STATE_EMPTY; + } +- fsg->common->next_buffhd_to_fill = &fsg->common->buffhds[0]; +- fsg->common->next_buffhd_to_drain = &fsg->common->buffhds[0]; +- exception_req_tag = fsg->exception_req_tag; +- new_config = fsg->new_config; +- old_state = fsg->state; ++ common->next_buffhd_to_fill = &common->buffhds[0]; ++ common->next_buffhd_to_drain = &common->buffhds[0]; ++ exception_req_tag = common->exception_req_tag; ++ new_config = common->new_config; ++ old_state = common->state; + + if (old_state == FSG_STATE_ABORT_BULK_OUT) +- fsg->state = FSG_STATE_STATUS_PHASE; ++ common->state = FSG_STATE_STATUS_PHASE; + else { +- for (i = 0; i < fsg->common->nluns; ++i) { +- curlun = &fsg->common->luns[i]; ++ for (i = 0; i < common->nluns; ++i) { ++ curlun = &common->luns[i]; + curlun->prevent_medium_removal = 0; + curlun->sense_data = SS_NO_SENSE; + curlun->unit_attention_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } +- fsg->state = FSG_STATE_IDLE; ++ common->state = FSG_STATE_IDLE; + } +- spin_unlock_irq(&fsg->lock); ++ spin_unlock_irq(&common->lock); + + /* Carry out any extra actions required for the exception */ + switch (old_state) { + case FSG_STATE_ABORT_BULK_OUT: +- send_status(fsg); +- spin_lock_irq(&fsg->lock); +- if (fsg->state == FSG_STATE_STATUS_PHASE) +- fsg->state = FSG_STATE_IDLE; +- spin_unlock_irq(&fsg->lock); ++ send_status(common); ++ spin_lock_irq(&common->lock); ++ if (common->state == FSG_STATE_STATUS_PHASE) ++ common->state = FSG_STATE_IDLE; ++ spin_unlock_irq(&common->lock); + break; + + case FSG_STATE_RESET: + /* In case we were forced against our will to halt a + * bulk endpoint, clear the halt now. (The SuperH UDC + * requires this.) */ +- if (test_and_clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) +- usb_ep_clear_halt(fsg->bulk_in); ++ if (!fsg_is_set(common)) ++ break; ++ if (test_and_clear_bit(IGNORE_BULK_OUT, ++ &common->fsg->atomic_bitflags)) ++ usb_ep_clear_halt(common->fsg->bulk_in); + +- if (fsg->ep0_req_tag == exception_req_tag) +- ep0_queue(fsg); /* Complete the status stage */ ++ if (common->ep0_req_tag == exception_req_tag) ++ ep0_queue(common); /* Complete the status stage */ + + /* Technically this should go here, but it would only be + * a waste of time. Ditto for the INTERFACE_CHANGE and + * CONFIG_CHANGE cases. */ +- /* for (i = 0; i < fsg->common->nluns; ++i) */ +- /* fsg->common->luns[i].unit_attention_data = */ ++ /* for (i = 0; i < common->nluns; ++i) */ ++ /* common->luns[i].unit_attention_data = */ + /* SS_RESET_OCCURRED; */ + break; + + case FSG_STATE_CONFIG_CHANGE: +- rc = do_set_config(fsg, new_config); +- if (fsg->ep0_req_tag != exception_req_tag) ++ rc = do_set_config(common, new_config); ++ if (common->ep0_req_tag != exception_req_tag) + break; +- if (rc != 0) /* STALL on errors */ +- fsg_set_halt(fsg, fsg->ep0); +- else /* Complete the status stage */ +- ep0_queue(fsg); ++ if (rc != 0) { /* STALL on errors */ ++ DBG(common, "ep0 set halt\n"); ++ usb_ep_set_halt(common->ep0); ++ } else { /* Complete the status stage */ ++ ep0_queue(common); ++ } + break; + + case FSG_STATE_EXIT: + case FSG_STATE_TERMINATED: +- do_set_config(fsg, 0); /* Free resources */ +- spin_lock_irq(&fsg->lock); +- fsg->state = FSG_STATE_TERMINATED; /* Stop the thread */ +- spin_unlock_irq(&fsg->lock); ++ do_set_config(common, 0); /* Free resources */ ++ spin_lock_irq(&common->lock); ++ common->state = FSG_STATE_TERMINATED; /* Stop the thread */ ++ spin_unlock_irq(&common->lock); + break; + + case FSG_STATE_INTERFACE_CHANGE: +@@ -2474,9 +2545,9 @@ static void handle_exception(struct fsg_ + + /*-------------------------------------------------------------------------*/ + +-static int fsg_main_thread(void *fsg_) ++static int fsg_main_thread(void *common_) + { +- struct fsg_dev *fsg = fsg_; ++ struct fsg_common *common = common_; + + /* Allow the thread to be killed by a signal, but set the signal mask + * to block everything but INT, TERM, KILL, and USR1. */ +@@ -2494,45 +2565,45 @@ static int fsg_main_thread(void *fsg_) + set_fs(get_ds()); + + /* The main loop */ +- while (fsg->state != FSG_STATE_TERMINATED) { +- if (exception_in_progress(fsg) || signal_pending(current)) { +- handle_exception(fsg); ++ while (common->state != FSG_STATE_TERMINATED) { ++ if (exception_in_progress(common) || signal_pending(current)) { ++ handle_exception(common); + continue; + } + +- if (!fsg->running) { +- sleep_thread(fsg); ++ if (!common->running) { ++ sleep_thread(common); + continue; + } + +- if (get_next_command(fsg)) ++ if (get_next_command(common)) + continue; + +- spin_lock_irq(&fsg->lock); +- if (!exception_in_progress(fsg)) +- fsg->state = FSG_STATE_DATA_PHASE; +- spin_unlock_irq(&fsg->lock); ++ spin_lock_irq(&common->lock); ++ if (!exception_in_progress(common)) ++ common->state = FSG_STATE_DATA_PHASE; ++ spin_unlock_irq(&common->lock); + +- if (do_scsi_command(fsg) || finish_reply(fsg)) ++ if (do_scsi_command(common) || finish_reply(common)) + continue; + +- spin_lock_irq(&fsg->lock); +- if (!exception_in_progress(fsg)) +- fsg->state = FSG_STATE_STATUS_PHASE; +- spin_unlock_irq(&fsg->lock); ++ spin_lock_irq(&common->lock); ++ if (!exception_in_progress(common)) ++ common->state = FSG_STATE_STATUS_PHASE; ++ spin_unlock_irq(&common->lock); + +- if (send_status(fsg)) ++ if (send_status(common)) + continue; + +- spin_lock_irq(&fsg->lock); +- if (!exception_in_progress(fsg)) +- fsg->state = FSG_STATE_IDLE; +- spin_unlock_irq(&fsg->lock); ++ spin_lock_irq(&common->lock); ++ if (!exception_in_progress(common)) ++ common->state = FSG_STATE_IDLE; ++ spin_unlock_irq(&common->lock); + } + +- spin_lock_irq(&fsg->lock); +- fsg->thread_task = NULL; +- spin_unlock_irq(&fsg->lock); ++ spin_lock_irq(&common->lock); ++ common->thread_task = NULL; ++ spin_unlock_irq(&common->lock); + + /* XXX */ + /* If we are exiting because of a signal, unregister the +@@ -2541,7 +2612,7 @@ static int fsg_main_thread(void *fsg_) + /* usb_gadget_unregister_driver(&fsg_driver); */ + + /* Let the unbind and cleanup routines know the thread has exited */ +- complete_and_exit(&fsg->thread_notifier, 0); ++ complete_and_exit(&common->thread_notifier, 0); + } + + +@@ -2600,7 +2671,21 @@ static struct fsg_common *fsg_common_ini + memset(common, 0, sizeof common); + common->free_storage_on_release = 0; + } ++ + common->gadget = gadget; ++ common->ep0 = gadget->ep0; ++ common->ep0req = cdev->req; ++ ++ /* Maybe allocate device-global string IDs, and patch descriptors */ ++ if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { ++ rc = usb_string_id(cdev); ++ if (rc < 0) { ++ kfree(common); ++ return ERR_PTR(rc); ++ } ++ fsg_strings[FSG_STRING_INTERFACE].id = rc; ++ fsg_intf_desc.iInterface = rc; ++ } + + /* Create the LUNs, open their backing files, and register the + * LUN devices in sysfs. */ +@@ -2697,11 +2782,23 @@ static struct fsg_common *fsg_common_ini + * disable stalls. + */ + common->can_stall = cfg->can_stall && +- !(gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)); ++ !(gadget_is_sh(common->gadget) || ++ gadget_is_at91(common->gadget)); + + +- common->thread_name = OR(cfg->thread_name, "file-storage"); ++ spin_lock_init(&common->lock); + kref_init(&common->ref); ++ ++ ++ /* Tell the thread to start working */ ++ common->thread_task = ++ kthread_create(fsg_main_thread, common, ++ OR(cfg->thread_name, "file-storage")); ++ if (IS_ERR(common->thread_task)) { ++ rc = PTR_ERR(common->thread_task); ++ goto error_release; ++ } ++ init_completion(&common->thread_notifier); + #undef OR + + +@@ -2731,15 +2828,21 @@ static struct fsg_common *fsg_common_ini + } + kfree(pathbuf); + ++ DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task)); ++ ++ wake_up_process(common->thread_task); ++ + return common; + + + error_luns: + common->nluns = i + 1; + error_release: ++ common->state = FSG_STATE_TERMINATED; /* The thread is dead */ + /* Call fsg_common_release() directly, ref might be not + * initialised */ + fsg_common_release(&common->ref); ++ complete(&common->thread_notifier); + return ERR_PTR(rc); + } + +@@ -2751,6 +2854,15 @@ static void fsg_common_release(struct kr + unsigned i = common->nluns; + struct fsg_lun *lun = common->luns; + ++ /* If the thread isn't already dead, tell it to exit now */ ++ if (common->state != FSG_STATE_TERMINATED) { ++ raise_exception(common, FSG_STATE_EXIT); ++ wait_for_completion(&common->thread_notifier); ++ ++ /* The cleanup routine waits for this completion also */ ++ complete(&common->thread_notifier); ++ } ++ + /* Beware tempting for -> do-while optimization: when in error + * recovery nluns may be zero. */ + +@@ -2775,17 +2887,6 @@ static void fsg_unbind(struct usb_config + struct fsg_dev *fsg = fsg_from_func(f); + + DBG(fsg, "unbind\n"); +- clear_bit(REGISTERED, &fsg->atomic_bitflags); +- +- /* If the thread isn't already dead, tell it to exit now */ +- if (fsg->state != FSG_STATE_TERMINATED) { +- raise_exception(fsg, FSG_STATE_EXIT); +- wait_for_completion(&fsg->thread_notifier); +- +- /* The cleanup routine waits for this completion also */ +- complete(&fsg->thread_notifier); +- } +- + fsg_common_put(fsg->common); + kfree(fsg); + } +@@ -2800,8 +2901,6 @@ static int fsg_bind(struct usb_configura + struct usb_ep *ep; + + fsg->gadget = gadget; +- fsg->ep0 = gadget->ep0; +- fsg->ep0req = c->cdev->req; + + /* New interface */ + i = usb_interface_id(c, f); +@@ -2814,13 +2913,13 @@ static int fsg_bind(struct usb_configura + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; +- ep->driver_data = fsg; /* claim the endpoint */ ++ ep->driver_data = fsg->common; /* claim the endpoint */ + fsg->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; +- ep->driver_data = fsg; /* claim the endpoint */ ++ ep->driver_data = fsg->common; /* claim the endpoint */ + fsg->bulk_out = ep; + + if (gadget_is_dualspeed(gadget)) { +@@ -2832,40 +2931,12 @@ static int fsg_bind(struct usb_configura + f->hs_descriptors = fsg_hs_function; + } + +- +- /* maybe allocate device-global string IDs, and patch descriptors */ +- if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { +- i = usb_string_id(c->cdev); +- if (i < 0) +- return i; +- fsg_strings[FSG_STRING_INTERFACE].id = i; +- fsg_intf_desc.iInterface = i; +- } +- +- +- fsg->thread_task = kthread_create(fsg_main_thread, fsg, +- fsg->common->thread_name); +- if (IS_ERR(fsg->thread_task)) { +- rc = PTR_ERR(fsg->thread_task); +- goto out; +- } +- +- DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task)); +- +- set_bit(REGISTERED, &fsg->atomic_bitflags); +- +- /* Tell the thread to start working */ +- wake_up_process(fsg->thread_task); + return 0; + + autoconf_fail: + ERROR(fsg, "unable to autoconfigure all endpoints\n"); + rc = -ENOTSUPP; +- +-out: +- fsg->state = FSG_STATE_TERMINATED; /* The thread is dead */ + fsg_unbind(c, f); +- complete(&fsg->thread_notifier); + return rc; + } + +@@ -2888,10 +2959,6 @@ static int fsg_add(struct usb_composite_ + if (unlikely(!fsg)) + return -ENOMEM; + +- spin_lock_init(&fsg->lock); +- init_completion(&fsg->thread_notifier); +- +- fsg->cdev = cdev; + fsg->function.name = FSG_DRIVER_DESC; + fsg->function.strings = fsg_strings_array; + fsg->function.descriptors = fsg_fs_function; +@@ -2907,7 +2974,6 @@ static int fsg_add(struct usb_composite_ + * from this function. So instead of incrementing counter now + * and decrement in error recovery we increment it only when + * call to usb_add_function() was successful. */ +- fsg->can_stall = common->can_stall; + + rc = usb_add_function(c, &fsg->function); + diff --git a/usb/usb-g_mass_storage-thread_exits-callback-added.patch b/usb/usb-g_mass_storage-thread_exits-callback-added.patch new file mode 100644 index 00000000000000..8bd89724d5cd9c --- /dev/null +++ b/usb/usb-g_mass_storage-thread_exits-callback-added.patch @@ -0,0 +1,151 @@ +From m.nazarewicz@samsung.com Fri Nov 20 12:29:06 2009 +From: Michal Nazarewicz <m.nazarewicz@samsung.com> +Date: Mon, 09 Nov 2009 14:15:26 +0100 +Subject: USB: g_mass_storage: thread_exits callback added +To: Greg KH <greg@kroah.com> +Cc: Michal Nazarewicz <m.nazarewicz@samsung.com> +Message-ID: <1257772527-870-8-git-send-email-m.nazarewicz@samsung.com> + + +From: Michal Nazarewicz <m.nazarewicz@samsung.com> + +thread_exits callback has been added to fsg_common structure. +This callback is called when MSF's thread exits (is terminated +by a signal or function is unregistered). It's then gadget's +responsibility to unregister the gadget. + +Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/f_mass_storage.c | 23 ++++++++++++++++++----- + drivers/usb/gadget/mass_storage.c | 20 +++++++++++--------- + 2 files changed, 29 insertions(+), 14 deletions(-) + +--- a/drivers/usb/gadget/f_mass_storage.c ++++ b/drivers/usb/gadget/f_mass_storage.c +@@ -367,6 +367,11 @@ struct fsg_common { + struct completion thread_notifier; + struct task_struct *thread_task; + ++ /* Callback function to call when thread exits. */ ++ void (*thread_exits)(struct fsg_common *common); ++ /* Gadget's private data. */ ++ void *private_data; ++ + /* Vendor (8 chars), product (16 chars), release (4 + * hexadecimal digits) and NUL byte */ + char inquiry_string[8 + 16 + 4 + 1]; +@@ -387,6 +392,11 @@ struct fsg_config { + const char *lun_name_format; + const char *thread_name; + ++ /* Callback function to call when thread exits. */ ++ void (*thread_exits)(struct fsg_common *common); ++ /* Gadget's private data. */ ++ void *private_data; ++ + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + u16 release; +@@ -2605,11 +2615,8 @@ static int fsg_main_thread(void *common_ + common->thread_task = NULL; + spin_unlock_irq(&common->lock); + +- /* XXX */ +- /* If we are exiting because of a signal, unregister the +- * gadget driver. */ +- /* if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) */ +- /* usb_gadget_unregister_driver(&fsg_driver); */ ++ if (common->thread_exits) ++ common->thread_exits(common); + + /* Let the unbind and cleanup routines know the thread has exited */ + complete_and_exit(&common->thread_notifier, 0); +@@ -2672,6 +2679,8 @@ static struct fsg_common *fsg_common_ini + common->free_storage_on_release = 0; + } + ++ common->private_data = cfg->private_data; ++ + common->gadget = gadget; + common->ep0 = gadget->ep0; + common->ep0req = cdev->req; +@@ -2791,6 +2800,7 @@ static struct fsg_common *fsg_common_ini + + + /* Tell the thread to start working */ ++ common->thread_exits = cfg->thread_exits; + common->thread_task = + kthread_create(fsg_main_thread, common, + OR(cfg->thread_name, "file-storage")); +@@ -3057,6 +3067,9 @@ fsg_config_from_params(struct fsg_config + cfg->product_name = 0; + cfg->release = 0xffff; + ++ cfg->thread_exits = 0; ++ cfg->private_data = 0; ++ + /* Finalise */ + cfg->can_stall = params->stall; + } +--- a/drivers/usb/gadget/mass_storage.c ++++ b/drivers/usb/gadget/mass_storage.c +@@ -132,9 +132,13 @@ static struct fsg_module_parameters mod_ + }; + FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); + ++static unsigned long msg_registered = 0; ++static void msg_cleanup(void); ++ + static int __init msg_do_config(struct usb_configuration *c) + { + struct fsg_common *common; ++ struct fsg_config config; + int ret; + + if (gadget_is_otg(c->cdev->gadget)) { +@@ -142,7 +146,9 @@ static int __init msg_do_config(struct u + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + +- common = fsg_common_from_params(0, c->cdev, &mod_data); ++ fsg_config_from_params(&config, &mod_data); ++ config.thread_exits = (void(*)(struct fsg_common*))&msg_cleanup; ++ common = fsg_common_init(0, c->cdev, &config); + if (IS_ERR(common)) + return PTR_ERR(common); + +@@ -201,11 +207,7 @@ static int __init msg_bind(struct usb_co + return status; + + dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); +- return 0; +-} +- +-static int __exit msg_unbind(struct usb_composite_dev *cdev) +-{ ++ set_bit(0, &msg_registered); + return 0; + } + +@@ -218,7 +220,6 @@ static struct usb_composite_driver msg_d + .dev = &msg_device_desc, + .strings = dev_strings, + .bind = msg_bind, +- .unbind = __exit_p(msg_unbind), + }; + + MODULE_DESCRIPTION(DRIVER_DESC); +@@ -231,8 +232,9 @@ static int __init msg_init(void) + } + module_init(msg_init); + +-static void __exit msg_cleanup(void) ++static void msg_cleanup(void) + { +- usb_composite_unregister(&msg_driver); ++ if (test_and_clear_bit(0, &msg_registered)) ++ usb_composite_unregister(&msg_driver); + } + module_exit(msg_cleanup); diff --git a/usb/usb-g_multi-multifunction-composite-gadget-added.patch b/usb/usb-g_multi-multifunction-composite-gadget-added.patch new file mode 100644 index 00000000000000..ba93a4d6dfb96f --- /dev/null +++ b/usb/usb-g_multi-multifunction-composite-gadget-added.patch @@ -0,0 +1,459 @@ +From m.nazarewicz@samsung.com Fri Nov 20 12:31:31 2009 +From: Michal Nazarewicz <m.nazarewicz@samsung.com> +Date: Mon, 09 Nov 2009 14:15:27 +0100 +Subject: USB: g_multi: Multifunction Composite Gadget added +To: Greg KH <greg@kroah.com> +Cc: Michal Nazarewicz <m.nazarewicz@samsung.com> +Message-ID: <1257772527-870-9-git-send-email-m.nazarewicz@samsung.com> + + +From: Michal Nazarewicz <m.nazarewicz@samsung.com> + +The Multifunction Composite Gadget has two configurations +consisting of Ethernet (RNDIS in first and CDC Ethernet in +second configuration), CDC Serial and File-backed Storage +functions. + +When connected to a Windows host, the first configuration +is chosen thus gadget provides RNDIS Ethernet, serial and +mass storage whereas when connected to Linux host, second +configuration is chosen thus providing CDC Ethernet, +serial and mass storage. + +Which configurations are built can be configured via +KConfig options. + +Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +--- + drivers/usb/gadget/Kconfig | 42 +++++ + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/multi.c | 355 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 399 insertions(+) + create mode 100644 drivers/usb/gadget/multi.c + +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -812,6 +812,48 @@ config USB_CDC_COMPOSITE + Say "y" to link the driver statically, or "m" to build a + dynamically linked module. + ++config USB_G_MULTI ++ tristate "Multifunction Composite Gadget (EXPERIMENTAL)" ++ help ++ The Multifunction Composite Gadget provides Ethernet (RNDIS ++ and/or CDC Ethernet), mass storage and ACM serial link ++ interfaces. ++ ++ You will be asked too choose which of the two configurations are ++ to be available in the gadget. At least one configuration must ++ be choosen to make gadget usable. Selecting more then one ++ configuration will prevent Windows from automatically detecting ++ the gadget as a composite gadget an INF file will be needed to ++ use the gadget. ++ ++ Say "y" to link the driver statically, or "m" to build a ++ dynamically linked module called "g_multi". ++ ++config USB_G_MULTI_RNDIS ++ bool "RNDIS + CDC Serial + Storage configuration" ++ depends on USB_G_MULTI ++ default y ++ help ++ This option enables a configuration with RNDIS, CDC Serial and ++ Mass Storage functions available in the Multifunction Composite ++ Gadget. This is configuration dedicated for Windows since RNDIS ++ is Microsfot's protocol. ++ ++ If unsure, say "y". ++ ++config USB_G_MULTI_CDC ++ bool "CDC Ethernet + CDC Serial + Storage configuration" ++ depends on USB_G_MULTI ++ default n ++ help ++ This option enables a configuration with CDC Ethernet (ECM), CDC ++ Serial and Mass Storage functions available in the Multifunction ++ Composite Gadget. This is configuration dedicated for Windows ++ since RNDIS is Microsfot's protocol. ++ ++ If unsure, say "y". ++ ++ + # put drivers that need isochronous transfer support (for audio + # or video class gadget drivers), or specific hardware, here. + +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -42,6 +42,7 @@ g_file_storage-objs := file_storage.o + g_mass_storage-objs := mass_storage.o + g_printer-objs := printer.o + g_cdc-objs := cdc2.o ++g_multi-objs := multi.o + + obj-$(CONFIG_USB_ZERO) += g_zero.o + obj-$(CONFIG_USB_AUDIO) += g_audio.o +@@ -53,4 +54,5 @@ obj-$(CONFIG_USB_G_SERIAL) += g_serial.o + obj-$(CONFIG_USB_G_PRINTER) += g_printer.o + obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o + obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o ++obj-$(CONFIG_USB_G_MULTI) += g_multi.o + +--- /dev/null ++++ b/drivers/usb/gadget/multi.c +@@ -0,0 +1,355 @@ ++/* ++ * multi.c -- Multifunction Composite driver ++ * ++ * Copyright (C) 2008 David Brownell ++ * Copyright (C) 2008 Nokia Corporation ++ * Copyright (C) 2009 Samsung Electronics ++ * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++ ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++ ++ ++#if defined CONFIG_USB_G_MULTI_RNDIS ++# define CONFIG_USB_ETH_RNDIS y ++#endif ++ ++ ++#define DRIVER_DESC "Multifunction Composite Gadget" ++#define DRIVER_VERSION "2009/07/21" ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */ ++#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * kbuild is not very cooperative with respect to linking separately ++ * compiled library objects into one module. So for now we won't use ++ * separate compilation ... ensuring init/exit sections work to shrink ++ * the runtime footprint, and giving us at least some parts of what ++ * a "gcc --combine ... part1.c part2.c part3.c ... " build would. ++ */ ++ ++#include "composite.c" ++#include "usbstring.c" ++#include "config.c" ++#include "epautoconf.c" ++ ++#include "u_serial.c" ++#include "f_acm.c" ++ ++#include "f_ecm.c" ++#include "f_subset.c" ++#ifdef CONFIG_USB_ETH_RNDIS ++# include "f_rndis.c" ++# include "rndis.c" ++#endif ++#include "u_ether.c" ++ ++#undef DBG /* u_ether.c has broken idea about macros */ ++#undef VDBG /* so clean up after it */ ++#undef ERROR ++#undef INFO ++#include "f_mass_storage.c" ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_device_descriptor device_desc = { ++ .bLength = sizeof device_desc, ++ .bDescriptorType = USB_DT_DEVICE, ++ ++ .bcdUSB = cpu_to_le16(0x0200), ++ ++ /* .bDeviceClass = USB_CLASS_COMM, */ ++ /* .bDeviceSubClass = 0, */ ++ /* .bDeviceProtocol = 0, */ ++ .bDeviceClass = 0xEF, ++ .bDeviceSubClass = 2, ++ .bDeviceProtocol = 1, ++ /* .bMaxPacketSize0 = f(hardware) */ ++ ++ /* Vendor and product id can be overridden by module parameters. */ ++ .idVendor = cpu_to_le16(MULTI_VENDOR_NUM), ++ .idProduct = cpu_to_le16(MULTI_PRODUCT_NUM), ++ /* .bcdDevice = f(hardware) */ ++ /* .iManufacturer = DYNAMIC */ ++ /* .iProduct = DYNAMIC */ ++ /* NO SERIAL NUMBER */ ++ .bNumConfigurations = 1, ++}; ++ ++static struct usb_otg_descriptor otg_descriptor = { ++ .bLength = sizeof otg_descriptor, ++ .bDescriptorType = USB_DT_OTG, ++ ++ /* REVISIT SRP-only hardware is possible, although ++ * it would not be called "OTG" ... ++ */ ++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, ++}; ++ ++static const struct usb_descriptor_header *otg_desc[] = { ++ (struct usb_descriptor_header *) &otg_descriptor, ++ NULL, ++}; ++ ++ ++/* string IDs are assigned dynamically */ ++ ++#define STRING_MANUFACTURER_IDX 0 ++#define STRING_PRODUCT_IDX 1 ++ ++static char manufacturer[50]; ++ ++static struct usb_string strings_dev[] = { ++ [STRING_MANUFACTURER_IDX].s = manufacturer, ++ [STRING_PRODUCT_IDX].s = DRIVER_DESC, ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab_dev = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_dev, ++}; ++ ++static struct usb_gadget_strings *dev_strings[] = { ++ &stringtab_dev, ++ NULL, ++}; ++ ++static u8 hostaddr[ETH_ALEN]; ++ ++ ++ ++/****************************** Configurations ******************************/ ++ ++static struct fsg_module_parameters mod_data = { ++ .stall = 1 ++}; ++FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); ++ ++static struct fsg_common *fsg_common; ++ ++ ++#ifdef CONFIG_USB_ETH_RNDIS ++ ++static int __init rndis_do_config(struct usb_configuration *c) ++{ ++ int ret; ++ ++ if (gadget_is_otg(c->cdev->gadget)) { ++ c->descriptors = otg_desc; ++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ } ++ ++ ret = rndis_bind_config(c, hostaddr); ++ if (ret < 0) ++ return ret; ++ ++ ret = acm_bind_config(c, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = fsg_add(c->cdev, c, fsg_common); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static struct usb_configuration rndis_config_driver = { ++ .label = "Multifunction Composite (RNDIS + MS + ACM)", ++ .bind = rndis_do_config, ++ .bConfigurationValue = 2, ++ /* .iConfiguration = DYNAMIC */ ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++}; ++ ++#endif ++ ++#ifdef CONFIG_USB_G_MULTI_CDC ++ ++static int __init cdc_do_config(struct usb_configuration *c) ++{ ++ int ret; ++ ++ if (gadget_is_otg(c->cdev->gadget)) { ++ c->descriptors = otg_desc; ++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ } ++ ++ ret = ecm_bind_config(c, hostaddr); ++ if (ret < 0) ++ return ret; ++ ++ ret = acm_bind_config(c, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = fsg_add(c->cdev, c, fsg_common); ++ if (ret < 0) ++ return ret; ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static struct usb_configuration cdc_config_driver = { ++ .label = "Multifunction Composite (CDC + MS + ACM)", ++ .bind = cdc_do_config, ++ .bConfigurationValue = 1, ++ /* .iConfiguration = DYNAMIC */ ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++}; ++ ++#endif ++ ++ ++ ++/****************************** Gadget Bind ******************************/ ++ ++ ++static int __init multi_bind(struct usb_composite_dev *cdev) ++{ ++ struct usb_gadget *gadget = cdev->gadget; ++ int status, gcnum; ++ ++ if (!can_support_ecm(cdev->gadget)) { ++ dev_err(&gadget->dev, "controller '%s' not usable\n", ++ gadget->name); ++ return -EINVAL; ++ } ++ ++ /* set up network link layer */ ++ status = gether_setup(cdev->gadget, hostaddr); ++ if (status < 0) ++ return status; ++ ++ /* set up serial link layer */ ++ status = gserial_setup(cdev->gadget, 1); ++ if (status < 0) ++ goto fail0; ++ ++ /* set up mass storage function */ ++ fsg_common = fsg_common_from_params(0, cdev, &mod_data); ++ if (IS_ERR(fsg_common)) { ++ status = PTR_ERR(fsg_common); ++ goto fail1; ++ } ++ ++ ++ gcnum = usb_gadget_controller_number(gadget); ++ if (gcnum >= 0) ++ device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); ++ else { ++ /* We assume that can_support_ecm() tells the truth; ++ * but if the controller isn't recognized at all then ++ * that assumption is a bit more likely to be wrong. ++ */ ++ WARNING(cdev, "controller '%s' not recognized\n", ++ gadget->name); ++ device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099); ++ } ++ ++ ++ /* Allocate string descriptor numbers ... note that string ++ * contents can be overridden by the composite_dev glue. ++ */ ++ ++ /* device descriptor strings: manufacturer, product */ ++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", ++ init_utsname()->sysname, init_utsname()->release, ++ gadget->name); ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail2; ++ strings_dev[STRING_MANUFACTURER_IDX].id = status; ++ device_desc.iManufacturer = status; ++ ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail2; ++ strings_dev[STRING_PRODUCT_IDX].id = status; ++ device_desc.iProduct = status; ++ ++#ifdef CONFIG_USB_ETH_RNDIS ++ /* register our first configuration */ ++ status = usb_add_config(cdev, &rndis_config_driver); ++ if (status < 0) ++ goto fail2; ++#endif ++ ++#ifdef CONFIG_USB_G_MULTI_CDC ++ /* register our second configuration */ ++ status = usb_add_config(cdev, &cdc_config_driver); ++ if (status < 0) ++ goto fail2; ++#endif ++ ++ dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); ++ fsg_common_put(fsg_common); ++ return 0; ++ ++fail2: ++ fsg_common_put(fsg_common); ++fail1: ++ gserial_cleanup(); ++fail0: ++ gether_cleanup(); ++ return status; ++} ++ ++static int __exit multi_unbind(struct usb_composite_dev *cdev) ++{ ++ gserial_cleanup(); ++ gether_cleanup(); ++ return 0; ++} ++ ++ ++/****************************** Some noise ******************************/ ++ ++ ++static struct usb_composite_driver multi_driver = { ++ .name = "g_multi", ++ .dev = &device_desc, ++ .strings = dev_strings, ++ .bind = multi_bind, ++ .unbind = __exit_p(multi_unbind), ++}; ++ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR("Michal Nazarewicz"); ++MODULE_LICENSE("GPL"); ++ ++static int __init g_multi_init(void) ++{ ++ return usb_composite_register(&multi_driver); ++} ++module_init(g_multi_init); ++ ++static void __exit g_multi_cleanup(void) ++{ ++ usb_composite_unregister(&multi_driver); ++} ++module_exit(g_multi_cleanup); diff --git a/usb/usb-musb-add-notes-for-blackfin-anomalies.patch b/usb/usb-musb-add-notes-for-blackfin-anomalies.patch new file mode 100644 index 00000000000000..99fa72f8fa2095 --- /dev/null +++ b/usb/usb-musb-add-notes-for-blackfin-anomalies.patch @@ -0,0 +1,60 @@ +From gadiyar@ti.com Fri Nov 20 12:41:46 2009 +From: Sonic Zhang <sonic.zhang@analog.com> +Date: Mon, 16 Nov 2009 19:13:08 +0530 +Subject: USB: musb: add notes for Blackfin anomalies +To: "linux-usb@vger.kernel.org; Greg KH" <greg@kroah.com> +Cc: Sonic Zhang <sonic.zhang@analog.com>, Cliff Cai <cliff.cai@analog.com>, Mike Frysinger <vapier@gentoo.org>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258378988-20147-1-git-send-email-gadiyar@ti.com> + + +From: Sonic Zhang <sonic.zhang@analog.com> + +Add some helpful notes about how the driver works around different +anomalies that exist in the on-chip host controller. + +Signed-off-by: Sonic Zhang <sonic.zhang@analog.com> +Signed-off-by: Cliff Cai <cliff.cai@analog.com> +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/blackfin.h | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +--- a/drivers/usb/musb/blackfin.h ++++ b/drivers/usb/musb/blackfin.h +@@ -14,6 +14,33 @@ + * Blackfin specific definitions + */ + ++/* Anomalies notes: ++ * ++ * 05000450 - USB DMA Mode 1 Short Packet Data Corruption: ++ * MUSB driver is designed to transfer buffer of N * maxpacket size ++ * in DMA mode 1 and leave the rest of the data to the next ++ * transfer in DMA mode 0, so we never transmit a short packet in ++ * DMA mode 1. ++ * ++ * 05000463 - This anomaly doesn't affect this driver since it ++ * never uses L1 or L2 memory as data destination. ++ * ++ * 05000464 - This anomaly doesn't affect this driver since it ++ * never uses L1 or L2 memory as data source. ++ * ++ * 05000465 - The anomaly can be seen when SCLK is over 100 MHz, and there is ++ * no way to workaround for bulk endpoints. Since the wMaxPackSize ++ * of bulk is less than or equal to 512, while the fifo size of ++ * endpoint 5, 6, 7 is 1024, the double buffer mode is enabled ++ * automatically when these endpoints are used for bulk OUT. ++ * ++ * 05000466 - This anomaly doesn't affect this driver since it never mixes ++ * concurrent DMA and core accesses to the TX endpoint FIFOs. ++ * ++ * 05000467 - The workaround for this anomaly will introduce another ++ * anomaly - 05000465. ++ */ ++ + #undef DUMP_FIFO_DATA + #ifdef DUMP_FIFO_DATA + static void dump_fifo_data(u8 *buf, u16 len) diff --git a/usb/usb-musb-add-work-around-for-blackfin-anomaly-05000456.patch b/usb/usb-musb-add-work-around-for-blackfin-anomaly-05000456.patch new file mode 100644 index 00000000000000..f1c8c4d2f81273 --- /dev/null +++ b/usb/usb-musb-add-work-around-for-blackfin-anomaly-05000456.patch @@ -0,0 +1,45 @@ +From gadiyar@ti.com Fri Nov 20 12:45:11 2009 +From: Sonic Zhang <sonic.zhang@analog.com> +Date: Mon, 16 Nov 2009 16:19:24 +0530 +Subject: USB: musb: add work around for Blackfin anomaly 05000456 +To: "linux-usb@vger.kernel.org; Greg KH" <greg@kroah.com> +Cc: Sonic Zhang <sonic.zhang@analog.com>, Cliff Cai <cliff.cai@analog.com>, Mike Frysinger <vapier@gentoo.org>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258368569-4323-6-git-send-email-gadiyar@ti.com> + + +From: Sonic Zhang <sonic.zhang@analog.com> + +Only allow USE_MODE1 when the Blackfin part is not affected by anomaly +05000456 (USB Receive Interrupt Is Not Generated in DMA Mode 1) since we +can't support the mode in that case. + +Signed-off-by: Sonic Zhang <sonic.zhang@analog.com> +Signed-off-by: Cliff Cai <cliff.cai@analog.com> +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musb_dma.h | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/drivers/usb/musb/musb_dma.h ++++ b/drivers/usb/musb/musb_dma.h +@@ -80,6 +80,17 @@ struct musb_hw_ep; + #define tusb_dma_omap() 0 + #endif + ++/* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1 ++ * Only allow DMA mode 1 to be used when the USB will actually generate the ++ * interrupts we expect. ++ */ ++#ifdef CONFIG_BLACKFIN ++# undef USE_MODE1 ++# if !ANOMALY_05000456 ++# define USE_MODE1 ++# endif ++#endif ++ + /* + * DMA channel status ... updated by the dma controller driver whenever that + * status changes, and protected by the overall controller spinlock. diff --git a/usb/usb-musb-blackfin-code-needs-nop_usb_xceiv-too.patch b/usb/usb-musb-blackfin-code-needs-nop_usb_xceiv-too.patch new file mode 100644 index 00000000000000..f9410122cec2bb --- /dev/null +++ b/usb/usb-musb-blackfin-code-needs-nop_usb_xceiv-too.patch @@ -0,0 +1,38 @@ +From vapier@gentoo.org Fri Nov 20 12:46:12 2009 +From: Cliff Cai <cliff.cai@analog.com> +Date: Mon, 16 Nov 2009 20:05:03 -0500 +Subject: USB: musb: Blackfin code needs NOP_USB_XCEIV too +To: linux-usb@vger.kernel.org, Greg Kroah-Hartman <gregkh@suse.de> +Cc: Cliff Cai <cliff.cai@analog.com>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258419903-29677-1-git-send-email-vapier@gentoo.org> + + +From: Cliff Cai <cliff.cai@analog.com> + +Otherwise we get the link failure: +drivers/built-in.o: In function 'musb_platform_init': +drivers/usb/musb/blackfin.c:300: undefined reference to 'usb_nop_xceiv_register' +make: *** [.tmp_vmlinux1] Error 1 + +Signed-off-by: Cliff Cai <cliff.cai@analog.com> +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/Kconfig | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/usb/musb/Kconfig ++++ b/drivers/usb/musb/Kconfig +@@ -10,9 +10,8 @@ comment "Enable Host or Gadget support t + config USB_MUSB_HDRC + depends on (USB || USB_GADGET) + depends on (ARM || (BF54x && !BF544) || (BF52x && !BF522 && !BF523)) +- select NOP_USB_XCEIV if ARCH_DAVINCI ++ select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN) + select TWL4030_USB if MACH_OMAP_3430SDP +- select NOP_USB_XCEIV if MACH_OMAP3EVM + select USB_OTG_UTILS + tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' + help diff --git a/usb/usb-musb-clear-the-blackfin-interrupt-pending-bits-early-in-the-isr.patch b/usb/usb-musb-clear-the-blackfin-interrupt-pending-bits-early-in-the-isr.patch new file mode 100644 index 00000000000000..c9b772ce512c3b --- /dev/null +++ b/usb/usb-musb-clear-the-blackfin-interrupt-pending-bits-early-in-the-isr.patch @@ -0,0 +1,51 @@ +From gadiyar@ti.com Fri Nov 20 12:45:39 2009 +From: Cliff Cai <cliff.cai@analog.com> +Date: Mon, 16 Nov 2009 16:19:26 +0530 +Subject: USB: musb: clear the Blackfin interrupt pending bits early in the ISR +To: "linux-usb@vger.kernel.org; Greg KH" <greg@kroah.com> +Cc: Cliff Cai <cliff.cai@analog.com>, Mike Frysinger <vapier@gentoo.org>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258368569-4323-8-git-send-email-gadiyar@ti.com> + + +From: Cliff Cai <cliff.cai@analog.com> + +If we clear the interrupt pending bits at the end, we sometimes return too +fast and have the same interrupt assert itself. There is no way in a +Blackfin system to force a sync of this state, so the hardware manual +instructs people to clear interrupt flags early in their ISR. + +Signed-off-by: Cliff Cai <cliff.cai@analog.com> +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musbhsdma.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/usb/musb/musbhsdma.c ++++ b/drivers/usb/musb/musbhsdma.c +@@ -259,6 +259,11 @@ static irqreturn_t dma_controller_irq(in + if (!int_hsdma) + goto done; + ++#ifdef CONFIG_BLACKFIN ++ /* Clear DMA interrupt flags */ ++ musb_writeb(mbase, MUSB_HSDMA_INTR, int_hsdma); ++#endif ++ + for (bchannel = 0; bchannel < MUSB_HSDMA_CHANNELS; bchannel++) { + if (int_hsdma & (1 << bchannel)) { + musb_channel = (struct musb_dma_channel *) +@@ -324,11 +329,6 @@ static irqreturn_t dma_controller_irq(in + } + } + +-#ifdef CONFIG_BLACKFIN +- /* Clear DMA interrup flags */ +- musb_writeb(mbase, MUSB_HSDMA_INTR, int_hsdma); +-#endif +- + retval = IRQ_HANDLED; + done: + spin_unlock_irqrestore(&musb->lock, flags); diff --git a/usb/usb-musb-error-out-when-anomaly-05000380-is-applicable.patch b/usb/usb-musb-error-out-when-anomaly-05000380-is-applicable.patch new file mode 100644 index 00000000000000..5f360db66a491a --- /dev/null +++ b/usb/usb-musb-error-out-when-anomaly-05000380-is-applicable.patch @@ -0,0 +1,42 @@ +From gadiyar@ti.com Fri Nov 20 12:45:56 2009 +From: Sonic Zhang <sonic.zhang@analog.com> +Date: Mon, 16 Nov 2009 16:19:27 +0530 +Subject: USB: musb: error out when anomaly 05000380 is applicable +To: "linux-usb@vger.kernel.org; Greg KH" <greg@kroah.com> +Cc: Sonic Zhang <sonic.zhang@analog.com>, Mike Frysinger <vapier@gentoo.org>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258368569-4323-9-git-send-email-gadiyar@ti.com> + + +From: Sonic Zhang <sonic.zhang@analog.com> + +Since we can't work around anomaly 05000380, throw a build error up and +instruct the user to use a different mode. + +Signed-off-by: Sonic Zhang <sonic.zhang@analog.com> +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/blackfin.h | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/usb/musb/blackfin.h ++++ b/drivers/usb/musb/blackfin.h +@@ -41,6 +41,16 @@ + * anomaly - 05000465. + */ + ++/* The Mentor USB DMA engine on BF52x (silicon v0.0 and v0.1) seems to be ++ * unstable in host mode. This may be caused by Anomaly 05000380. After ++ * digging out the root cause, we will change this number accordingly. ++ * So, need to either use silicon v0.2+ or disable DMA mode in MUSB. ++ */ ++#if ANOMALY_05000380 && defined(CONFIG_BF52x) && \ ++ defined(CONFIG_USB_MUSB_HDRC) && !defined(CONFIG_MUSB_PIO_ONLY) ++# error "Please use PIO mode in MUSB driver on bf52x chip v0.0 and v0.1" ++#endif ++ + #undef DUMP_FIFO_DATA + #ifdef DUMP_FIFO_DATA + static void dump_fifo_data(u8 *buf, u16 len) diff --git a/usb/usb-musb-fix-musb_platform_set_mode-definition.patch b/usb/usb-musb-fix-musb_platform_set_mode-definition.patch new file mode 100644 index 00000000000000..240ac5f8b806cd --- /dev/null +++ b/usb/usb-musb-fix-musb_platform_set_mode-definition.patch @@ -0,0 +1,36 @@ +From gadiyar@ti.com Fri Nov 20 12:45:25 2009 +From: Bryan Wu <cooloney@kernel.org> +Date: Mon, 16 Nov 2009 16:19:25 +0530 +Subject: USB: musb: fix musb_platform_set_mode() definition +To: "linux-usb@vger.kernel.org; Greg KH" <greg@kroah.com> +Cc: Bryan Wu <cooloney@kernel.org>, Cliff Cai <cliff.cai@analog.com>, Mike Frysinger <vapier@gentoo.org>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258368569-4323-7-git-send-email-gadiyar@ti.com> + + +From: Bryan Wu <cooloney@kernel.org> + +Update function definition to match latest MUSB framework. + +Signed-off-by: Bryan Wu <cooloney@kernel.org> +Signed-off-by: Cliff Cai <cliff.cai@analog.com> +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/blackfin.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/usb/musb/blackfin.c ++++ b/drivers/usb/musb/blackfin.c +@@ -226,8 +226,9 @@ int musb_platform_get_vbus_status(struct + return 0; + } + +-void musb_platform_set_mode(struct musb *musb, u8 musb_mode) ++int musb_platform_set_mode(struct musb *musb, u8 musb_mode) + { ++ return -EIO; + } + + int __init musb_platform_init(struct musb *musb) diff --git a/usb/usb-musb-fix-printf-warning-in-debug-code.patch b/usb/usb-musb-fix-printf-warning-in-debug-code.patch new file mode 100644 index 00000000000000..da3cd1401cf088 --- /dev/null +++ b/usb/usb-musb-fix-printf-warning-in-debug-code.patch @@ -0,0 +1,37 @@ +From gadiyar@ti.com Fri Nov 20 12:46:28 2009 +From: Mike Frysinger <vapier@gentoo.org> +Date: Mon, 16 Nov 2009 16:19:29 +0530 +Subject: USB: musb: fix printf warning in debug code +To: "linux-usb@vger.kernel.org; Greg KH" <greg@kroah.com> +Cc: Mike Frysinger <vapier@gentoo.org>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258368569-4323-11-git-send-email-gadiyar@ti.com> + + +From: Mike Frysinger <vapier@gentoo.org> + +The debug code in the DMA ISR uses a %d for a size_t when it should be +using %zu. Otherwise gcc whines with: + +drivers/usb/musb/musbhsdma.c: In function 'dma_controller_irq': +drivers/usb/musb/musbhsdma.c:288: warning: format '%d' expects type 'int', + but argument 7 has type 'size_t' + +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musbhsdma.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/musb/musbhsdma.c ++++ b/drivers/usb/musb/musbhsdma.c +@@ -285,7 +285,7 @@ static irqreturn_t dma_controller_irq(in + channel->actual_len = addr + - musb_channel->start_addr; + +- DBG(2, "ch %p, 0x%x -> 0x%x (%d / %d) %s\n", ++ DBG(2, "ch %p, 0x%x -> 0x%x (%zu / %d) %s\n", + channel, musb_channel->start_addr, + addr, channel->actual_len, + musb_channel->len, diff --git a/usb/usb-musb-kill-compile-warning-for-blackfin-systems.patch b/usb/usb-musb-kill-compile-warning-for-blackfin-systems.patch new file mode 100644 index 00000000000000..b411d79b993833 --- /dev/null +++ b/usb/usb-musb-kill-compile-warning-for-blackfin-systems.patch @@ -0,0 +1,42 @@ +From gadiyar@ti.com Fri Nov 20 12:39:02 2009 +From: Bryan Wu <cooloney@kernel.org> +Date: Mon, 16 Nov 2009 16:19:20 +0530 +Subject: USB: musb: kill compile warning for Blackfin systems +To: "linux-usb@vger.kernel.org; Greg KH" <greg@kroah.com> +Cc: Bryan Wu <cooloney@kernel.org>, Mike Frysinger <vapier@gentoo.org>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258368569-4323-2-git-send-email-gadiyar@ti.com> + + +From: Bryan Wu <cooloney@kernel.org> + +The Blackfin version of musb_read_target_reg_base() returns a u16 when the +common code expects a (void __iomem *), so update the Blackfin function to +return the right value. This fixes the compile warning: + +drivers/usb/musb/musb_core.c: In function 'musb_core_init': +drivers/usb/musb/musb_core.c:1448: warning: assignment makes pointer from + integer without a cast + +Signed-off-by: Bryan Wu <cooloney@kernel.org> +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musb_regs.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/usb/musb/musb_regs.h ++++ b/drivers/usb/musb/musb_regs.h +@@ -465,9 +465,9 @@ static inline u16 musb_read_hwvers(void + return 0; + } + +-static inline u16 musb_read_target_reg_base(u8 i, void __iomem *mbase) ++static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) + { +- return 0; ++ return NULL; + } + + static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, diff --git a/usb/usb-musb-kill-some-useless-comments-in-blackfin-driver.patch b/usb/usb-musb-kill-some-useless-comments-in-blackfin-driver.patch new file mode 100644 index 00000000000000..1bfb3591b69546 --- /dev/null +++ b/usb/usb-musb-kill-some-useless-comments-in-blackfin-driver.patch @@ -0,0 +1,34 @@ +From gadiyar@ti.com Fri Nov 20 12:39:38 2009 +From: Bryan Wu <cooloney@kernel.org> +Date: Mon, 16 Nov 2009 16:19:21 +0530 +Subject: USB: musb: kill some useless comments in Blackfin driver +To: "linux-usb@vger.kernel.org; Greg KH" <greg@kroah.com> +Cc: Bryan Wu <cooloney@kernel.org>, Cliff Cai <cliff.cai@analog.com>, Mike Frysinger <vapier@gentoo.org>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258368569-4323-3-git-send-email-gadiyar@ti.com> + + +From: Bryan Wu <cooloney@kernel.org> + +Signed-off-by: Bryan Wu <cooloney@kernel.org> +Signed-off-by: Cliff Cai <cliff.cai@analog.com> +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/blackfin.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/drivers/usb/musb/blackfin.c ++++ b/drivers/usb/musb/blackfin.c +@@ -262,10 +262,6 @@ int __init musb_platform_init(struct mus + SSYNC(); + } + +- /* TODO +- * Set SIC-IVG register +- */ +- + /* Configure PLL oscillator register */ + bfin_write_USB_PLLOSC_CTRL(0x30a8); + SSYNC(); diff --git a/usb/usb-musb-save-hardware-revision-at-init.patch b/usb/usb-musb-save-hardware-revision-at-init.patch new file mode 100644 index 00000000000000..11836180620f90 --- /dev/null +++ b/usb/usb-musb-save-hardware-revision-at-init.patch @@ -0,0 +1,73 @@ +From gadiyar@ti.com Fri Nov 20 12:46:59 2009 +From: Anand Gadiyar <gadiyar@ti.com> +Date: Mon, 16 Nov 2009 21:09:21 +0530 +Subject: USB: MUSB: save hardware revision at init +To: "linux-usb@vger.kernel.org; Greg KH" <greg@kroah.com> +Cc: Anand Gadiyar <gadiyar@ti.com>, Ajay Kumar Gupta <ajay.gupta@ti.com>, Felipe Balbi <felipe.balbi@nokia.com>, David Brownell <dbrownell@users.sourceforge.net>, Sergei Shtylyov <sshtylyov@ru.mvista.com> +Message-ID: <1258385961-28495-1-git-send-email-gadiyar@ti.com> + + +MUSB: save hardware revision at init + +This can be used later to flag workarounds for issues affecting +particular revisions. Saving this at init avoids having to +read the HWVERS register multiple times in code. + +While at it, use macros to extract the version information +instead of using hardcoded values. + +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Cc: Ajay Kumar Gupta <ajay.gupta@ti.com> +Acked-by: Felipe Balbi <felipe.balbi@nokia.com> +Cc: David Brownell <dbrownell@users.sourceforge.net> +Cc: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musb_core.c | 10 ++++------ + drivers/usb/musb/musb_core.h | 8 ++++++++ + 2 files changed, 12 insertions(+), 6 deletions(-) + +--- a/drivers/usb/musb/musb_core.c ++++ b/drivers/usb/musb/musb_core.c +@@ -1319,7 +1319,6 @@ static int __init musb_core_init(u16 mus + #endif + u8 reg; + char *type; +- u16 hwvers, rev_major, rev_minor; + char aInfo[78], aRevision[32], aDate[12]; + void __iomem *mbase = musb->mregs; + int status = 0; +@@ -1391,11 +1390,10 @@ static int __init musb_core_init(u16 mus + } + + /* log release info */ +- hwvers = musb_read_hwvers(mbase); +- rev_major = (hwvers >> 10) & 0x1f; +- rev_minor = hwvers & 0x3ff; +- snprintf(aRevision, 32, "%d.%d%s", rev_major, +- rev_minor, (hwvers & 0x8000) ? "RC" : ""); ++ musb->hwvers = musb_read_hwvers(mbase); ++ snprintf(aRevision, 32, "%d.%d%s", MUSB_HWVERS_MAJOR(musb->hwvers), ++ MUSB_HWVERS_MINOR(musb->hwvers), ++ (musb->hwvers & MUSB_HWVERS_RC) ? "RC" : ""); + printk(KERN_DEBUG "%s: %sHDRC RTL version %s %s\n", + musb_driver_name, type, aRevision, aDate); + +--- a/drivers/usb/musb/musb_core.h ++++ b/drivers/usb/musb/musb_core.h +@@ -322,6 +322,14 @@ struct musb { + struct clk *clock; + irqreturn_t (*isr)(int, void *); + struct work_struct irq_work; ++#define MUSB_HWVERS_MAJOR(x) ((x >> 10) & 0x1f) ++#define MUSB_HWVERS_MINOR(x) (x & 0x3ff) ++#define MUSB_HWVERS_RC 0x8000 ++#define MUSB_HWVERS_1300 0x52C ++#define MUSB_HWVERS_1400 0x590 ++#define MUSB_HWVERS_1800 0x720 ++#define MUSB_HWVERS_2000 0x800 ++ u16 hwvers; + + /* this hub status bit is reserved by USB 2.0 and not seen by usbcore */ + #define MUSB_PORT_STAT_RESUME (1 << 31) diff --git a/usb/usb-musb-tweak-musb_read_fifo-to-avoid-unused-warnings.patch b/usb/usb-musb-tweak-musb_read_fifo-to-avoid-unused-warnings.patch new file mode 100644 index 00000000000000..430cd2a400af7f --- /dev/null +++ b/usb/usb-musb-tweak-musb_read_fifo-to-avoid-unused-warnings.patch @@ -0,0 +1,52 @@ +From gadiyar@ti.com Fri Nov 20 12:38:43 2009 +From: Mike Frysinger <vapier@gentoo.org> +Date: Mon, 16 Nov 2009 16:19:19 +0530 +Subject: USB: musb: tweak musb_read_fifo() to avoid unused warnings +Cc: Mike Frysinger <vapier@gentoo.org>, Bryan Wu <cooloney@kernel.org>, Cliff Cai <cliff.cai@analog.com>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258368569-4323-1-git-send-email-gadiyar@ti.com> + + +From: Mike Frysinger <vapier@gentoo.org> + +Otherwise gcc will whine about epnum/dma_reg being unused when building +for BF54x parts. + +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Bryan Wu <cooloney@kernel.org> +Signed-off-by: Cliff Cai <cliff.cai@analog.com> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Acked-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/blackfin.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/drivers/usb/musb/blackfin.c ++++ b/drivers/usb/musb/blackfin.c +@@ -53,13 +53,11 @@ void musb_write_fifo(struct musb_hw_ep * + void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) + { + void __iomem *fifo = hw_ep->fifo; ++ ++#ifdef CONFIG_BF52x + u8 epnum = hw_ep->epnum; + u16 dma_reg = 0; + +- DBG(4, "%cX ep%d fifo %p count %d buf %p\n", +- 'R', hw_ep->epnum, fifo, len, dst); +- +-#ifdef CONFIG_BF52x + invalidate_dcache_range((unsigned int)dst, + (unsigned int)(dst + len)); + +@@ -102,6 +100,9 @@ void musb_read_fifo(struct musb_hw_ep *h + len & 0x01 ? (len >> 1) + 1 : len >> 1); + #endif + ++ DBG(4, "%cX ep%d fifo %p count %d buf %p\n", ++ 'R', hw_ep->epnum, fifo, len, dst); ++ + dump_fifo_data(dst, len); + } + diff --git a/usb/usb-musb-update-blackfin-processor-dependency.patch b/usb/usb-musb-update-blackfin-processor-dependency.patch new file mode 100644 index 00000000000000..31a464438e954c --- /dev/null +++ b/usb/usb-musb-update-blackfin-processor-dependency.patch @@ -0,0 +1,36 @@ +From vapier@gentoo.org Fri Nov 20 12:41:14 2009 +From: Sonic Zhang <sonic.zhang@analog.com> +Date: Mon, 16 Nov 2009 20:04:21 -0500 +Subject: USB: musb: update Blackfin processor dependency +To: linux-usb@vger.kernel.org, Greg Kroah-Hartman <gregkh@suse.de> +Cc: Sonic Zhang <sonic.zhang@analog.com>, Bryan Wu <cooloney@kernel.org>, Cliff Cai <cliff.cai@analog.com>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258419861-29598-1-git-send-email-vapier@gentoo.org> + + +From: Sonic Zhang <sonic.zhang@analog.com> + +Do not allow MUSB driver to be selected on derivatives that don't have the +MUSB controller on them. + +Signed-off-by: Sonic Zhang <sonic.zhang@analog.com> +Signed-off-by: Bryan Wu <cooloney@kernel.org> +Signed-off-by: Cliff Cai <cliff.cai@analog.com> +Signed-off-by: Mike Frysinger <vapier@gentoo.org> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/musb/Kconfig ++++ b/drivers/usb/musb/Kconfig +@@ -9,7 +9,7 @@ comment "Enable Host or Gadget support t + # (M)HDRC = (Multipoint) Highspeed Dual-Role Controller + config USB_MUSB_HDRC + depends on (USB || USB_GADGET) +- depends on (ARM || BLACKFIN) ++ depends on (ARM || (BF54x && !BF544) || (BF52x && !BF522 && !BF523)) + select NOP_USB_XCEIV if ARCH_DAVINCI + select TWL4030_USB if MACH_OMAP_3430SDP + select NOP_USB_XCEIV if MACH_OMAP3EVM diff --git a/usb/usb-musb_gadget-implement-set_wedge-method.patch b/usb/usb-musb_gadget-implement-set_wedge-method.patch new file mode 100644 index 00000000000000..d53f6e1604e77e --- /dev/null +++ b/usb/usb-musb_gadget-implement-set_wedge-method.patch @@ -0,0 +1,104 @@ +From sshtylyov@ru.mvista.com Fri Nov 20 13:07:26 2009 +From: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Date: Wed, 18 Nov 2009 22:51:51 +0300 +Subject: USB: musb_gadget: implement set_wedge() method +To: gregkh@suse.de, <david-b@pacbell.net> +Cc: linux-usb@vger.kernel.org, davinci-linux-open-source@linux.davincidsp.com +Message-ID: <200911182251.51894.sshtylyov@ru.mvista.com> + + +Implement the driver's set_wedge() method by adding the 'wedged' flag +to the 'struct musb_ep'. + +Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musb_gadget.c | 20 +++++++++++++++++++- + drivers/usb/musb/musb_gadget.h | 2 ++ + drivers/usb/musb/musb_gadget_ep0.c | 6 +++++- + 3 files changed, 26 insertions(+), 2 deletions(-) + +--- a/drivers/usb/musb/musb_gadget.c ++++ b/drivers/usb/musb/musb_gadget.c +@@ -966,6 +966,7 @@ static int musb_gadget_enable(struct usb + + musb_ep->desc = desc; + musb_ep->busy = 0; ++ musb_ep->wedged = 0; + status = 0; + + pr_debug("%s periph: enabled %s for %s %s, %smaxpacket %d\n", +@@ -1262,7 +1263,8 @@ int musb_gadget_set_halt(struct usb_ep * + goto done; + } + } +- } ++ } else ++ musb_ep->wedged = 0; + + /* set/clear the stall and toggle bits */ + DBG(2, "%s: %s stall\n", ep->name, value ? "set" : "clear"); +@@ -1301,6 +1303,21 @@ done: + return status; + } + ++/* ++ * Sets the halt feature with the clear requests ignored ++ */ ++int musb_gadget_set_wedge(struct usb_ep *ep) ++{ ++ struct musb_ep *musb_ep = to_musb_ep(ep); ++ ++ if (!ep) ++ return -EINVAL; ++ ++ musb_ep->wedged = 1; ++ ++ return usb_ep_set_halt(ep); ++} ++ + static int musb_gadget_fifo_status(struct usb_ep *ep) + { + struct musb_ep *musb_ep = to_musb_ep(ep); +@@ -1371,6 +1388,7 @@ static const struct usb_ep_ops musb_ep_o + .queue = musb_gadget_queue, + .dequeue = musb_gadget_dequeue, + .set_halt = musb_gadget_set_halt, ++ .set_wedge = musb_gadget_set_wedge, + .fifo_status = musb_gadget_fifo_status, + .fifo_flush = musb_gadget_fifo_flush + }; +--- a/drivers/usb/musb/musb_gadget_ep0.c ++++ b/drivers/usb/musb/musb_gadget_ep0.c +@@ -273,6 +273,11 @@ __acquires(musb->lock) + if (!musb_ep->desc) + break; + ++ handled = 1; ++ /* Ignore request if endpoint is wedged */ ++ if (musb_ep->wedged) ++ break; ++ + /* REVISIT do it directly, no locking games */ + spin_unlock(&musb->lock); + musb_gadget_set_halt(&musb_ep->end_point, 0); +@@ -280,7 +285,6 @@ __acquires(musb->lock) + + /* select ep0 again */ + musb_ep_select(mbase, 0); +- handled = 1; + } break; + default: + /* class, vendor, etc ... delegate */ +--- a/drivers/usb/musb/musb_gadget.h ++++ b/drivers/usb/musb/musb_gadget.h +@@ -75,6 +75,8 @@ struct musb_ep { + /* later things are modified based on usage */ + struct list_head req_list; + ++ u8 wedged; ++ + /* true if lock must be dropped but req_list may not be advanced */ + u8 busy; + }; diff --git a/usb/usb-musb_gadget-remove-pointless-loop.patch b/usb/usb-musb_gadget-remove-pointless-loop.patch new file mode 100644 index 00000000000000..8e5aad449451b5 --- /dev/null +++ b/usb/usb-musb_gadget-remove-pointless-loop.patch @@ -0,0 +1,216 @@ +From sshtylyov@ru.mvista.com Fri Nov 20 13:08:34 2009 +From: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Date: Wed, 18 Nov 2009 22:55:28 +0300 +Subject: USB: musb_gadget: remove pointless loop +To: gregkh@suse.de, <david-b@pacbell.net> +Cc: linux-usb@vger.kernel.org, davinci-linux-open-source@linux.davincidsp.com +Message-ID: <200911182255.28785.sshtylyov@ru.mvista.com> +Content-Disposition: inline + + +Remove the pointless 'do () while (0)' loop from musb_g_tx() -- it +makes this function symmetric to musb_g_rx()... + +Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musb_gadget.c | 172 +++++++++++++++++++---------------------- + 1 file changed, 81 insertions(+), 91 deletions(-) + +--- a/drivers/usb/musb/musb_gadget.c ++++ b/drivers/usb/musb/musb_gadget.c +@@ -429,112 +429,102 @@ void musb_g_tx(struct musb *musb, u8 epn + DBG(4, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr); + + dma = is_dma_capable() ? musb_ep->dma : NULL; +- do { +- /* REVISIT for high bandwidth, MUSB_TXCSR_P_INCOMPTX +- * probably rates reporting as a host error ++ ++ /* ++ * REVISIT: for high bandwidth, MUSB_TXCSR_P_INCOMPTX ++ * probably rates reporting as a host error. ++ */ ++ if (csr & MUSB_TXCSR_P_SENTSTALL) { ++ csr |= MUSB_TXCSR_P_WZC_BITS; ++ csr &= ~MUSB_TXCSR_P_SENTSTALL; ++ musb_writew(epio, MUSB_TXCSR, csr); ++ return; ++ } ++ ++ if (csr & MUSB_TXCSR_P_UNDERRUN) { ++ /* We NAKed, no big deal... little reason to care. */ ++ csr |= MUSB_TXCSR_P_WZC_BITS; ++ csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); ++ musb_writew(epio, MUSB_TXCSR, csr); ++ DBG(20, "underrun on ep%d, req %p\n", epnum, request); ++ } ++ ++ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { ++ /* ++ * SHOULD NOT HAPPEN... has with CPPI though, after ++ * changing SENDSTALL (and other cases); harmless? + */ +- if (csr & MUSB_TXCSR_P_SENTSTALL) { +- csr |= MUSB_TXCSR_P_WZC_BITS; +- csr &= ~MUSB_TXCSR_P_SENTSTALL; +- musb_writew(epio, MUSB_TXCSR, csr); +- break; +- } ++ DBG(5, "%s dma still busy?\n", musb_ep->end_point.name); ++ return; ++ } + +- if (csr & MUSB_TXCSR_P_UNDERRUN) { +- /* we NAKed, no big deal ... little reason to care */ ++ if (request) { ++ u8 is_dma = 0; ++ ++ if (dma && (csr & MUSB_TXCSR_DMAENAB)) { ++ is_dma = 1; + csr |= MUSB_TXCSR_P_WZC_BITS; +- csr &= ~(MUSB_TXCSR_P_UNDERRUN +- | MUSB_TXCSR_TXPKTRDY); ++ csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN | ++ MUSB_TXCSR_TXPKTRDY); + musb_writew(epio, MUSB_TXCSR, csr); +- DBG(20, "underrun on ep%d, req %p\n", epnum, request); ++ /* Ensure writebuffer is empty. */ ++ csr = musb_readw(epio, MUSB_TXCSR); ++ request->actual += musb_ep->dma->actual_len; ++ DBG(4, "TXCSR%d %04x, DMA off, len %zu, req %p\n", ++ epnum, csr, musb_ep->dma->actual_len, request); + } + +- if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { +- /* SHOULD NOT HAPPEN ... has with cppi though, after +- * changing SENDSTALL (and other cases); harmless? ++ if (is_dma || request->actual == request->length) { ++ /* ++ * First, maybe a terminating short packet. Some DMA ++ * engines might handle this by themselves. + */ +- DBG(5, "%s dma still busy?\n", musb_ep->end_point.name); +- break; +- } +- +- if (request) { +- u8 is_dma = 0; ++ if ((request->zero && request->length ++ && request->length % musb_ep->packet_sz == 0) ++#ifdef CONFIG_USB_INVENTRA_DMA ++ || (is_dma && (!dma->desired_mode || ++ (request->actual & ++ (musb_ep->packet_sz - 1)))) ++#endif ++ ) { ++ /* ++ * On DMA completion, FIFO may not be ++ * available yet... ++ */ ++ if (csr & MUSB_TXCSR_TXPKTRDY) ++ return; + +- if (dma && (csr & MUSB_TXCSR_DMAENAB)) { +- is_dma = 1; +- csr |= MUSB_TXCSR_P_WZC_BITS; +- csr &= ~(MUSB_TXCSR_DMAENAB +- | MUSB_TXCSR_P_UNDERRUN ++ DBG(4, "sending zero pkt\n"); ++ musb_writew(epio, MUSB_TXCSR, MUSB_TXCSR_MODE + | MUSB_TXCSR_TXPKTRDY); +- musb_writew(epio, MUSB_TXCSR, csr); +- /* ensure writebuffer is empty */ +- csr = musb_readw(epio, MUSB_TXCSR); +- request->actual += musb_ep->dma->actual_len; +- DBG(4, "TXCSR%d %04x, dma off, " +- "len %zu, req %p\n", +- epnum, csr, +- musb_ep->dma->actual_len, +- request); ++ request->zero = 0; + } + +- if (is_dma || request->actual == request->length) { +- +- /* First, maybe a terminating short packet. +- * Some DMA engines might handle this by +- * themselves. +- */ +- if ((request->zero +- && request->length +- && (request->length +- % musb_ep->packet_sz) +- == 0) +-#ifdef CONFIG_USB_INVENTRA_DMA +- || (is_dma && +- ((!dma->desired_mode) || +- (request->actual & +- (musb_ep->packet_sz - 1)))) +-#endif +- ) { +- /* on dma completion, fifo may not +- * be available yet ... +- */ +- if (csr & MUSB_TXCSR_TXPKTRDY) +- break; +- +- DBG(4, "sending zero pkt\n"); +- musb_writew(epio, MUSB_TXCSR, +- MUSB_TXCSR_MODE +- | MUSB_TXCSR_TXPKTRDY); +- request->zero = 0; +- } +- +- /* ... or if not, then complete it */ +- musb_g_giveback(musb_ep, request, 0); ++ /* ... or if not, then complete it. */ ++ musb_g_giveback(musb_ep, request, 0); + +- /* kickstart next transfer if appropriate; +- * the packet that just completed might not +- * be transmitted for hours or days. +- * REVISIT for double buffering... +- * FIXME revisit for stalls too... +- */ +- musb_ep_select(mbase, epnum); +- csr = musb_readw(epio, MUSB_TXCSR); +- if (csr & MUSB_TXCSR_FIFONOTEMPTY) +- break; +- request = musb_ep->desc +- ? next_request(musb_ep) +- : NULL; +- if (!request) { +- DBG(4, "%s idle now\n", +- musb_ep->end_point.name); +- break; +- } +- } ++ /* ++ * Kickstart next transfer if appropriate; ++ * the packet that just completed might not ++ * be transmitted for hours or days. ++ * REVISIT for double buffering... ++ * FIXME revisit for stalls too... ++ */ ++ musb_ep_select(mbase, epnum); ++ csr = musb_readw(epio, MUSB_TXCSR); ++ if (csr & MUSB_TXCSR_FIFONOTEMPTY) ++ return; + +- txstate(musb, to_musb_request(request)); ++ if (!musb_ep->desc) { ++ DBG(4, "%s idle now\n", ++ musb_ep->end_point.name); ++ return; ++ } else ++ request = next_request(musb_ep); + } + +- } while (0); ++ txstate(musb, to_musb_request(request)); ++ } + } + + /* ------------------------------------------------------------ */ diff --git a/usb/usb-musb_gadget_ep0-fix-unhandled-endpoint-0-irqs-again.patch b/usb/usb-musb_gadget_ep0-fix-unhandled-endpoint-0-irqs-again.patch new file mode 100644 index 00000000000000..c21c19ceecc158 --- /dev/null +++ b/usb/usb-musb_gadget_ep0-fix-unhandled-endpoint-0-irqs-again.patch @@ -0,0 +1,38 @@ +From gadiyar@ti.com Fri Nov 20 12:47:28 2009 +From: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Date: Mon, 16 Nov 2009 16:24:05 +0530 +Subject: USB: musb_gadget_ep0: fix unhandled endpoint 0 IRQs, again +To: "linux-usb@vger.kernel.org; Greg KH" <greg@kroah.com> +Cc: Sergei Shtylyov <sshtylyov@ru.mvista.com>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1258368845-4913-1-git-send-email-gadiyar@ti.com> + +From: Sergei Shtylyov <sshtylyov@ru.mvista.com> + +Commit a5073b52833e4df8e16c93dc4cbb7e0c558c74a2 (musb_gadget: fix +unhandled endpoint 0 IRQs) somehow missed its key change: + +"The gadget EP0 code routinely ignores an interrupt at end of +the data phase because of musb_g_ep0_giveback() resetting the +state machine to "idle, waiting for SETUP" phase prematurely." + +So, the majority of the cases of unhandled IRQs is still unfixed... + +Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Cc: stable <stable@kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musb_gadget_ep0.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/usb/musb/musb_gadget_ep0.c ++++ b/drivers/usb/musb/musb_gadget_ep0.c +@@ -199,7 +199,6 @@ service_in_request(struct musb *musb, co + static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req) + { + musb_g_giveback(&musb->endpoints[0].ep_in, req, 0); +- musb->ep0_state = MUSB_EP0_STAGE_SETUP; + } + + /* diff --git a/usb/usb-musb_gadget_ep0-stop-abusing-musb_gadget_set_halt.patch b/usb/usb-musb_gadget_ep0-stop-abusing-musb_gadget_set_halt.patch new file mode 100644 index 00000000000000..012d23177bceb6 --- /dev/null +++ b/usb/usb-musb_gadget_ep0-stop-abusing-musb_gadget_set_halt.patch @@ -0,0 +1,141 @@ +From sshtylyov@ru.mvista.com Fri Nov 20 13:08:17 2009 +From: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Date: Wed, 18 Nov 2009 22:54:32 +0300 +Subject: USB: musb_gadget_ep0: stop abusing musb_gadget_set_halt() +To: gregkh@suse.de, <david-b@pacbell.net> +Cc: linux-usb@vger.kernel.org, davinci-linux-open-source@linux.davincidsp.com +Message-ID: <200911182254.32908.sshtylyov@ru.mvista.com> +Content-Disposition: inline + + +Stop playing with musb->lock and abusing musb_gadget_set_halt() in +the code clearing the endpoint halt feature -- instead, manipulate +the registers directly. + +While at it, get rid uf unneeded line breaks and over-indentation in +the code setting the endpoint halt feature. + +Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musb_gadget.h | 2 - + drivers/usb/musb/musb_gadget_ep0.c | 63 ++++++++++++++++++++++--------------- + 2 files changed, 38 insertions(+), 27 deletions(-) + +--- a/drivers/usb/musb/musb_gadget_ep0.c ++++ b/drivers/usb/musb/musb_gadget_ep0.c +@@ -257,19 +257,25 @@ __acquires(musb->lock) + case USB_RECIP_INTERFACE: + break; + case USB_RECIP_ENDPOINT:{ +- const u8 num = ctrlrequest->wIndex & 0x0f; +- struct musb_ep *musb_ep; ++ const u8 epnum = ++ ctrlrequest->wIndex & 0x0f; ++ struct musb_ep *musb_ep; ++ struct musb_hw_ep *ep; ++ void __iomem *regs; ++ int is_in; ++ u16 csr; + +- if (num == 0 +- || num >= MUSB_C_NUM_EPS +- || ctrlrequest->wValue +- != USB_ENDPOINT_HALT) ++ if (epnum == 0 || epnum >= MUSB_C_NUM_EPS || ++ ctrlrequest->wValue != USB_ENDPOINT_HALT) + break; + +- if (ctrlrequest->wIndex & USB_DIR_IN) +- musb_ep = &musb->endpoints[num].ep_in; ++ ep = musb->endpoints + epnum; ++ regs = ep->regs; ++ is_in = ctrlrequest->wIndex & USB_DIR_IN; ++ if (is_in) ++ musb_ep = &ep->ep_in; + else +- musb_ep = &musb->endpoints[num].ep_out; ++ musb_ep = &ep->ep_out; + if (!musb_ep->desc) + break; + +@@ -278,10 +284,23 @@ __acquires(musb->lock) + if (musb_ep->wedged) + break; + +- /* REVISIT do it directly, no locking games */ +- spin_unlock(&musb->lock); +- musb_gadget_set_halt(&musb_ep->end_point, 0); +- spin_lock(&musb->lock); ++ musb_ep_select(mbase, epnum); ++ if (is_in) { ++ csr = musb_readw(regs, MUSB_TXCSR); ++ csr |= MUSB_TXCSR_CLRDATATOG | ++ MUSB_TXCSR_P_WZC_BITS; ++ csr &= ~(MUSB_TXCSR_P_SENDSTALL | ++ MUSB_TXCSR_P_SENTSTALL | ++ MUSB_TXCSR_TXPKTRDY); ++ musb_writew(regs, MUSB_TXCSR, csr); ++ } else { ++ csr = musb_readw(regs, MUSB_RXCSR); ++ csr |= MUSB_RXCSR_CLRDATATOG | ++ MUSB_RXCSR_P_WZC_BITS; ++ csr &= ~(MUSB_RXCSR_P_SENDSTALL | ++ MUSB_RXCSR_P_SENTSTALL); ++ musb_writew(regs, MUSB_RXCSR, csr); ++ } + + /* select ep0 again */ + musb_ep_select(mbase, 0); +@@ -377,10 +396,8 @@ stall: + int is_in; + u16 csr; + +- if (epnum == 0 +- || epnum >= MUSB_C_NUM_EPS +- || ctrlrequest->wValue +- != USB_ENDPOINT_HALT) ++ if (epnum == 0 || epnum >= MUSB_C_NUM_EPS || ++ ctrlrequest->wValue != USB_ENDPOINT_HALT) + break; + + ep = musb->endpoints + epnum; +@@ -395,24 +412,20 @@ stall: + + musb_ep_select(mbase, epnum); + if (is_in) { +- csr = musb_readw(regs, +- MUSB_TXCSR); ++ csr = musb_readw(regs, MUSB_TXCSR); + if (csr & MUSB_TXCSR_FIFONOTEMPTY) + csr |= MUSB_TXCSR_FLUSHFIFO; + csr |= MUSB_TXCSR_P_SENDSTALL + | MUSB_TXCSR_CLRDATATOG + | MUSB_TXCSR_P_WZC_BITS; +- musb_writew(regs, MUSB_TXCSR, +- csr); ++ musb_writew(regs, MUSB_TXCSR, csr); + } else { +- csr = musb_readw(regs, +- MUSB_RXCSR); ++ csr = musb_readw(regs, MUSB_RXCSR); + csr |= MUSB_RXCSR_P_SENDSTALL + | MUSB_RXCSR_FLUSHFIFO + | MUSB_RXCSR_CLRDATATOG + | MUSB_RXCSR_P_WZC_BITS; +- musb_writew(regs, MUSB_RXCSR, +- csr); ++ musb_writew(regs, MUSB_RXCSR, csr); + } + + /* select ep0 again */ +--- a/drivers/usb/musb/musb_gadget.h ++++ b/drivers/usb/musb/musb_gadget.h +@@ -105,6 +105,4 @@ extern void musb_gadget_cleanup(struct m + + extern void musb_g_giveback(struct musb_ep *, struct usb_request *, int); + +-extern int musb_gadget_set_halt(struct usb_ep *ep, int value); +- + #endif /* __MUSB_GADGET_H */ diff --git a/usb/usb-r8a66597-clean-up.-remove-unneeded-null-checks.patch b/usb/usb-r8a66597-clean-up.-remove-unneeded-null-checks.patch new file mode 100644 index 00000000000000..dfbef5d04085eb --- /dev/null +++ b/usb/usb-r8a66597-clean-up.-remove-unneeded-null-checks.patch @@ -0,0 +1,45 @@ +From error27@gmail.com Fri Nov 20 11:54:00 2009 +From: Dan Carpenter <error27@gmail.com> +Date: Tue, 10 Nov 2009 10:53:58 +0200 (SAST) +Subject: USB: r8a66597: clean up. remove unneeded null checks +To: shimoda.yoshihiro@renesas.com +Cc: linux-usb@vger.kernel.org +Message-ID: <alpine.DEB.2.00.0911082025580.12333@bicker> + + +td and dev can not be null. + +Also they are dereferenced in list_for_each_entry_safe and list_for_each +before the check happens so we would have an oops if it were possible +for them to be null. + +Found using the smatch static checker. + +Signed-off-by: Dan Carpenter <error27@gmail.com> +Acked-by: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/r8a66597-hcd.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/drivers/usb/host/r8a66597-hcd.c ++++ b/drivers/usb/host/r8a66597-hcd.c +@@ -822,8 +822,6 @@ static void force_dequeue(struct r8a6659 + return; + + list_for_each_entry_safe(td, next, list, queue) { +- if (!td) +- continue; + if (td->address != address) + continue; + +@@ -2025,8 +2023,6 @@ static struct r8a66597_device *get_r8a66 + struct list_head *list = &r8a66597->child_device; + + list_for_each_entry(dev, list, device_list) { +- if (!dev) +- continue; + if (dev->usb_address != addr) + continue; + diff --git a/usb/usb-remove-the-auto_pm-flag.patch b/usb/usb-remove-the-auto_pm-flag.patch new file mode 100644 index 00000000000000..61aa1da70b1964 --- /dev/null +++ b/usb/usb-remove-the-auto_pm-flag.patch @@ -0,0 +1,188 @@ +From stern@rowland.harvard.edu Fri Nov 20 11:52:44 2009 +From: Alan Stern <stern@rowland.harvard.edu> +Date: Fri, 13 Nov 2009 11:53:59 -0500 (EST) +Subject: USB: remove the auto_pm flag +To: Greg KH <greg@kroah.com> +Message-ID: <Pine.LNX.4.44L0.0911131151140.2654-100000@iolanthe.rowland.org> + + +This patch (as1302) removes the auto_pm flag from struct usb_device. +The flag's only purpose was to distinguish between autosuspends and +external suspends, but that information is now available in the +pm_message_t argument passed to suspend methods. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/usb/power-management.txt | 9 +++++---- + drivers/bluetooth/btusb.c | 2 +- + drivers/hid/usbhid/hid-core.c | 8 ++++---- + drivers/net/wimax/i2400m/usb.c | 4 ++-- + drivers/usb/core/driver.c | 4 ---- + drivers/usb/serial/option.c | 2 +- + drivers/usb/serial/sierra.c | 2 +- + include/linux/usb.h | 2 -- + 8 files changed, 14 insertions(+), 19 deletions(-) + +--- a/Documentation/usb/power-management.txt ++++ b/Documentation/usb/power-management.txt +@@ -423,15 +423,16 @@ an URB had completed too recently. + + External suspend calls should never be allowed to fail in this way, + only autosuspend calls. The driver can tell them apart by checking +-udev->auto_pm; this flag will be set to 1 for internal PM events +-(autosuspend or autoresume) and 0 for external PM events. ++the PM_EVENT_AUTO bit in the message.event argument to the suspend ++method; this bit will be set for internal PM events (autosuspend) and ++clear for external PM events. + + Many of the ingredients in the autosuspend framework are oriented + towards interfaces: The usb_interface structure contains the + pm_usage_cnt field, and the usb_autopm_* routines take an interface + pointer as their argument. But somewhat confusingly, a few of the +-pieces (usb_mark_last_busy() and udev->auto_pm) use the usb_device +-structure instead. Drivers need to keep this straight; they can call ++pieces (i.e., usb_mark_last_busy()) use the usb_device structure ++instead. Drivers need to keep this straight; they can call + interface_to_usbdev() to find the device structure for a given + interface. + +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -1066,7 +1066,7 @@ static int btusb_suspend(struct usb_inte + return 0; + + spin_lock_irq(&data->txlock); +- if (!(interface_to_usbdev(intf)->auto_pm && data->tx_in_flight)) { ++ if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) { + set_bit(BTUSB_SUSPENDING, &data->flags); + spin_unlock_irq(&data->txlock); + } else { +--- a/drivers/hid/usbhid/hid-core.c ++++ b/drivers/hid/usbhid/hid-core.c +@@ -1254,10 +1254,9 @@ static int hid_suspend(struct usb_interf + { + struct hid_device *hid = usb_get_intfdata(intf); + struct usbhid_device *usbhid = hid->driver_data; +- struct usb_device *udev = interface_to_usbdev(intf); + int status; + +- if (udev->auto_pm) { ++ if (message.event & PM_EVENT_AUTO) { + spin_lock_irq(&usbhid->lock); /* Sync with error handler */ + if (!test_bit(HID_RESET_PENDING, &usbhid->iofl) + && !test_bit(HID_CLEAR_HALT, &usbhid->iofl) +@@ -1282,7 +1281,7 @@ static int hid_suspend(struct usb_interf + return -EIO; + } + +- if (!ignoreled && udev->auto_pm) { ++ if (!ignoreled && (message.event & PM_EVENT_AUTO)) { + spin_lock_irq(&usbhid->lock); + if (test_bit(HID_LED_ON, &usbhid->iofl)) { + spin_unlock_irq(&usbhid->lock); +@@ -1295,7 +1294,8 @@ static int hid_suspend(struct usb_interf + hid_cancel_delayed_stuff(usbhid); + hid_cease_io(usbhid); + +- if (udev->auto_pm && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) { ++ if ((message.event & PM_EVENT_AUTO) && ++ test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) { + /* lost race against keypresses */ + status = hid_start_in(hid); + if (status < 0) +--- a/drivers/net/wimax/i2400m/usb.c ++++ b/drivers/net/wimax/i2400m/usb.c +@@ -498,7 +498,7 @@ void i2400mu_disconnect(struct usb_inter + * + * As well, the device might refuse going to sleep for whichever + * reason. In this case we just fail. For system suspend/hibernate, +- * we *can't* fail. We look at usb_dev->auto_pm to see if the ++ * we *can't* fail. We check PM_EVENT_AUTO to see if the + * suspend call comes from the USB stack or from the system and act + * in consequence. + * +@@ -517,7 +517,7 @@ int i2400mu_suspend(struct usb_interface + struct i2400m *i2400m = &i2400mu->i2400m; + + #ifdef CONFIG_PM +- if (usb_dev->auto_pm > 0) ++ if (pm_msg.event & PM_EVENT_AUTO) + is_autosuspend = 1; + #endif + +--- a/drivers/usb/core/driver.c ++++ b/drivers/usb/core/driver.c +@@ -1341,7 +1341,6 @@ static int usb_autopm_do_device(struct u + int status = 0; + + usb_pm_lock(udev); +- udev->auto_pm = 1; + udev->pm_usage_cnt += inc_usage_cnt; + WARN_ON(udev->pm_usage_cnt < 0); + if (inc_usage_cnt) +@@ -1473,7 +1472,6 @@ static int usb_autopm_do_interface(struc + if (intf->condition == USB_INTERFACE_UNBOUND) + status = -ENODEV; + else { +- udev->auto_pm = 1; + atomic_add(inc_usage_cnt, &intf->pm_usage_cnt); + udev->last_busy = jiffies; + if (inc_usage_cnt >= 0 && +@@ -1707,7 +1705,6 @@ int usb_external_suspend_device(struct u + + do_unbind_rebind(udev, DO_UNBIND); + usb_pm_lock(udev); +- udev->auto_pm = 0; + status = usb_suspend_both(udev, msg); + usb_pm_unlock(udev); + return status; +@@ -1730,7 +1727,6 @@ int usb_external_resume_device(struct us + int status; + + usb_pm_lock(udev); +- udev->auto_pm = 0; + status = usb_resume_both(udev, msg); + udev->last_busy = jiffies; + usb_pm_unlock(udev); +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -1308,7 +1308,7 @@ static int option_suspend(struct usb_ser + + dbg("%s entered", __func__); + +- if (serial->dev->auto_pm) { ++ if (message.event & PM_EVENT_AUTO) { + spin_lock_irq(&intfdata->susp_lock); + b = intfdata->in_flight; + spin_unlock_irq(&intfdata->susp_lock); +--- a/drivers/usb/serial/sierra.c ++++ b/drivers/usb/serial/sierra.c +@@ -1005,7 +1005,7 @@ static int sierra_suspend(struct usb_ser + struct sierra_intf_private *intfdata; + int b; + +- if (serial->dev->auto_pm) { ++ if (message.event & PM_EVENT_AUTO) { + intfdata = serial->private; + spin_lock_irq(&intfdata->susp_lock); + b = intfdata->in_flight; +--- a/include/linux/usb.h ++++ b/include/linux/usb.h +@@ -429,7 +429,6 @@ struct usb_tt; + * @last_busy: time of last use + * @autosuspend_delay: in jiffies + * @connect_time: time device was first connected +- * @auto_pm: autosuspend/resume in progress + * @do_remote_wakeup: remote wakeup should be enabled + * @reset_resume: needs reset instead of resume + * @autosuspend_disabled: autosuspend disabled by the user +@@ -514,7 +513,6 @@ struct usb_device { + int autosuspend_delay; + unsigned long connect_time; + +- unsigned auto_pm:1; + unsigned do_remote_wakeup:1; + unsigned reset_resume:1; + unsigned autosuspend_disabled:1; diff --git a/usb/usb-twl4030-enable-usb-regulators-before-enabling-usb-charging.patch b/usb/usb-twl4030-enable-usb-regulators-before-enabling-usb-charging.patch new file mode 100644 index 00000000000000..092f3ac78a6ba4 --- /dev/null +++ b/usb/usb-twl4030-enable-usb-regulators-before-enabling-usb-charging.patch @@ -0,0 +1,52 @@ +From leed.aguilar@ti.com Fri Nov 20 13:10:53 2009 +From: "Aguilar Pena, Leed" <leed.aguilar@ti.com> +Date: Fri, 20 Nov 2009 11:32:53 -0600 +Subject: USB: twl4030: Enable USB regulators before enabling USB charging +To: "linux-usb@vger.kernel.org" <linux-usb@vger.kernel.org> +Cc: "dbrownell@users.sourceforge.net" <dbrownell@users.sourceforge.net>, "gregkh@suse.de" <gregkh@suse.de> +Message-ID: <23D0CDE7C386D144A4F26EB0F427F8C074D70BE5@dlee04.ent.ti.com> + + +For TWL family of power management ICs, USB charging works only +when USB regulators are in enabled state: 3v1, 1v5, 1v8 + +On a USB cable attach, twl4030_phy_resume(twl) function enables +the regulators. Enable USB charging, only after all regulators +are enabled. + +Its observed that enabling USB charging before regulators are +enabled, causes USB charging to fail. + +Tested on: Zoom2: omap3430: ES3.1 + TWL5030 +Needs T2-MADC and T2-BCI drivers which are still not upstreamed. + +Signed-off-by: Moiz Sonasath <m-sonasath@ti.com> +Signed-off-by: Leed Aguilar <leed.aguilar@ti.com> +Acked-by: Vikram Pandita <vikram.pandita@ti.com> +Acked-by: Anand Gadiyar <gadiyar@ti.com> +Acked-by: Nishanth Menon <nm@ti.com> +Acked-by: Felipe Balbi <felipe.balbi@nokia.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/usb/otg/twl4030-usb.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/usb/otg/twl4030-usb.c ++++ b/drivers/usb/otg/twl4030-usb.c +@@ -598,12 +598,12 @@ static irqreturn_t twl4030_usb_irq(int i + * USB_LINK_VBUS state. musb_hdrc won't care until it + * starts to handle softconnect right. + */ +- twl4030charger_usb_en(status == USB_LINK_VBUS); +- + if (status == USB_LINK_NONE) + twl4030_phy_suspend(twl, 0); + else + twl4030_phy_resume(twl); ++ ++ twl4030charger_usb_en(status == USB_LINK_VBUS); + } + sysfs_notify(&twl->dev->kobj, NULL, "vbus"); + diff --git a/usb/usb-usbtmc-repeat-usb_bulk_msg-until-whole-message-is-transfered.patch b/usb/usb-usbtmc-repeat-usb_bulk_msg-until-whole-message-is-transfered.patch new file mode 100644 index 00000000000000..00159221d7df71 --- /dev/null +++ b/usb/usb-usbtmc-repeat-usb_bulk_msg-until-whole-message-is-transfered.patch @@ -0,0 +1,49 @@ +From andre.herms@tec-venture.de Fri Nov 20 13:10:31 2009 +From: Andre Herms <andre.herms@tec-venture.de> +Date: Thu, 19 Nov 2009 18:14:49 +0100 +Subject: USB: usbtmc: repeat usb_bulk_msg until whole message is transfered +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org +Message-ID: <4B057D09.4060207@tec-venture.de> + + +usb_bulk_msg() transfers only bytes up to the maximum packet size. +It must be repeated by the usbtmc driver until all bytes of a TMC message +are transfered. + +Without this patch, ETIMEDOUT is reported when writing TMC messages +larger than the maximum USB bulk size and the transfer remains incomplete. +The user will notice that the device hangs and must be reset by either closing +the application or pulling the plug. + +Signed-off-by: Andre Herms <andre.herms@tec-venture.de> +Cc: stable <stable@kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/class/usbtmc.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +--- a/drivers/usb/class/usbtmc.c ++++ b/drivers/usb/class/usbtmc.c +@@ -562,10 +562,16 @@ static ssize_t usbtmc_write(struct file + n_bytes = roundup(12 + this_part, 4); + memset(buffer + 12 + this_part, 0, n_bytes - (12 + this_part)); + +- retval = usb_bulk_msg(data->usb_dev, +- usb_sndbulkpipe(data->usb_dev, +- data->bulk_out), +- buffer, n_bytes, &actual, USBTMC_TIMEOUT); ++ do { ++ retval = usb_bulk_msg(data->usb_dev, ++ usb_sndbulkpipe(data->usb_dev, ++ data->bulk_out), ++ buffer, n_bytes, ++ &actual, USBTMC_TIMEOUT); ++ if (retval != 0) ++ break; ++ n_bytes -= actual; ++ } while (n_bytes); + + data->bTag_last_write = data->bTag; + data->bTag++; diff --git a/usb/usb-xhci-add-tests-for-trb-address-translation.patch b/usb/usb-xhci-add-tests-for-trb-address-translation.patch new file mode 100644 index 00000000000000..4aed87476671d3 --- /dev/null +++ b/usb/usb-xhci-add-tests-for-trb-address-translation.patch @@ -0,0 +1,227 @@ +From sarah.a.sharp@linux.intel.com Fri Nov 20 11:59:18 2009 +From: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Date: Mon, 9 Nov 2009 13:35:23 -0800 +Subject: USB: xhci: Add tests for TRB address translation. +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org +Message-ID: <20091109213523.GA2864@xanatos> +Content-Disposition: inline + + +It's not surprising that the transfer request buffer (TRB) physical to +virtual address translation function has bugs in it, since I wrote most of +it at 4am last October. Add a test suite to check the TRB math. This +runs at memory initialization time, and causes the driver to fail to load +if the TRB math fails. + +Please excuse the excessively long lines in the test vectors; they can't +really be made shorter and still be readable. + +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 | 159 +++++++++++++++++++++++++++++++++++++++++++ + drivers/usb/host/xhci-ring.c | 3 + drivers/usb/host/xhci.h | 3 + 3 files changed, 163 insertions(+), 2 deletions(-) + +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -1268,6 +1268,9 @@ void xhci_reset_bandwidth(struct usb_hcd + + /* xHCI ring, segment, TRB, and TD functions */ + dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); ++struct xhci_segment *trb_in_td(struct xhci_segment *start_seg, ++ union xhci_trb *start_trb, union xhci_trb *end_trb, ++ dma_addr_t suspect_dma); + void xhci_ring_cmd_db(struct xhci_hcd *xhci); + void *xhci_setup_one_noop(struct xhci_hcd *xhci); + void xhci_handle_event(struct xhci_hcd *xhci); +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -859,6 +859,163 @@ void xhci_mem_cleanup(struct xhci_hcd *x + xhci->page_shift = 0; + } + ++static int xhci_test_trb_in_td(struct xhci_hcd *xhci, ++ struct xhci_segment *input_seg, ++ union xhci_trb *start_trb, ++ union xhci_trb *end_trb, ++ dma_addr_t input_dma, ++ struct xhci_segment *result_seg, ++ char *test_name, int test_number) ++{ ++ unsigned long long start_dma; ++ unsigned long long end_dma; ++ struct xhci_segment *seg; ++ ++ start_dma = xhci_trb_virt_to_dma(input_seg, start_trb); ++ end_dma = xhci_trb_virt_to_dma(input_seg, end_trb); ++ ++ seg = trb_in_td(input_seg, start_trb, end_trb, input_dma); ++ if (seg != result_seg) { ++ xhci_warn(xhci, "WARN: %s TRB math test %d failed!\n", ++ test_name, test_number); ++ xhci_warn(xhci, "Tested TRB math w/ seg %p and " ++ "input DMA 0x%llx\n", ++ input_seg, ++ (unsigned long long) input_dma); ++ xhci_warn(xhci, "starting TRB %p (0x%llx DMA), " ++ "ending TRB %p (0x%llx DMA)\n", ++ start_trb, start_dma, ++ end_trb, end_dma); ++ xhci_warn(xhci, "Expected seg %p, got seg %p\n", ++ result_seg, seg); ++ return -1; ++ } ++ return 0; ++} ++ ++/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */ ++static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags) ++{ ++ struct { ++ dma_addr_t input_dma; ++ struct xhci_segment *result_seg; ++ } simple_test_vector [] = { ++ /* A zeroed DMA field should fail */ ++ { 0, NULL }, ++ /* One TRB before the ring start should fail */ ++ { xhci->event_ring->first_seg->dma - 16, NULL }, ++ /* One byte before the ring start should fail */ ++ { xhci->event_ring->first_seg->dma - 1, NULL }, ++ /* Starting TRB should succeed */ ++ { xhci->event_ring->first_seg->dma, xhci->event_ring->first_seg }, ++ /* Ending TRB should succeed */ ++ { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16, ++ xhci->event_ring->first_seg }, ++ /* One byte after the ring end should fail */ ++ { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16 + 1, NULL }, ++ /* One TRB after the ring end should fail */ ++ { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT)*16, NULL }, ++ /* An address of all ones should fail */ ++ { (dma_addr_t) (~0), NULL }, ++ }; ++ struct { ++ struct xhci_segment *input_seg; ++ union xhci_trb *start_trb; ++ union xhci_trb *end_trb; ++ dma_addr_t input_dma; ++ struct xhci_segment *result_seg; ++ } complex_test_vector [] = { ++ /* Test feeding a valid DMA address from a different ring */ ++ { .input_seg = xhci->event_ring->first_seg, ++ .start_trb = xhci->event_ring->first_seg->trbs, ++ .end_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], ++ .input_dma = xhci->cmd_ring->first_seg->dma, ++ .result_seg = NULL, ++ }, ++ /* Test feeding a valid end TRB from a different ring */ ++ { .input_seg = xhci->event_ring->first_seg, ++ .start_trb = xhci->event_ring->first_seg->trbs, ++ .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], ++ .input_dma = xhci->cmd_ring->first_seg->dma, ++ .result_seg = NULL, ++ }, ++ /* Test feeding a valid start and end TRB from a different ring */ ++ { .input_seg = xhci->event_ring->first_seg, ++ .start_trb = xhci->cmd_ring->first_seg->trbs, ++ .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], ++ .input_dma = xhci->cmd_ring->first_seg->dma, ++ .result_seg = NULL, ++ }, ++ /* TRB in this ring, but after this TD */ ++ { .input_seg = xhci->event_ring->first_seg, ++ .start_trb = &xhci->event_ring->first_seg->trbs[0], ++ .end_trb = &xhci->event_ring->first_seg->trbs[3], ++ .input_dma = xhci->event_ring->first_seg->dma + 4*16, ++ .result_seg = NULL, ++ }, ++ /* TRB in this ring, but before this TD */ ++ { .input_seg = xhci->event_ring->first_seg, ++ .start_trb = &xhci->event_ring->first_seg->trbs[3], ++ .end_trb = &xhci->event_ring->first_seg->trbs[6], ++ .input_dma = xhci->event_ring->first_seg->dma + 2*16, ++ .result_seg = NULL, ++ }, ++ /* TRB in this ring, but after this wrapped TD */ ++ { .input_seg = xhci->event_ring->first_seg, ++ .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], ++ .end_trb = &xhci->event_ring->first_seg->trbs[1], ++ .input_dma = xhci->event_ring->first_seg->dma + 2*16, ++ .result_seg = NULL, ++ }, ++ /* TRB in this ring, but before this wrapped TD */ ++ { .input_seg = xhci->event_ring->first_seg, ++ .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], ++ .end_trb = &xhci->event_ring->first_seg->trbs[1], ++ .input_dma = xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 4)*16, ++ .result_seg = NULL, ++ }, ++ /* TRB not in this ring, and we have a wrapped TD */ ++ { .input_seg = xhci->event_ring->first_seg, ++ .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], ++ .end_trb = &xhci->event_ring->first_seg->trbs[1], ++ .input_dma = xhci->cmd_ring->first_seg->dma + 2*16, ++ .result_seg = NULL, ++ }, ++ }; ++ ++ unsigned int num_tests; ++ int i, ret; ++ ++ num_tests = sizeof(simple_test_vector) / sizeof(simple_test_vector[0]); ++ for (i = 0; i < num_tests; i++) { ++ ret = xhci_test_trb_in_td(xhci, ++ xhci->event_ring->first_seg, ++ xhci->event_ring->first_seg->trbs, ++ &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], ++ simple_test_vector[i].input_dma, ++ simple_test_vector[i].result_seg, ++ "Simple", i); ++ if (ret < 0) ++ return ret; ++ } ++ ++ num_tests = sizeof(complex_test_vector) / sizeof(complex_test_vector[0]); ++ for (i = 0; i < num_tests; i++) { ++ ret = xhci_test_trb_in_td(xhci, ++ complex_test_vector[i].input_seg, ++ complex_test_vector[i].start_trb, ++ complex_test_vector[i].end_trb, ++ complex_test_vector[i].input_dma, ++ complex_test_vector[i].result_seg, ++ "Complex", i); ++ if (ret < 0) ++ return ret; ++ } ++ xhci_dbg(xhci, "TRB math tests passed.\n"); ++ return 0; ++} ++ ++ + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) + { + dma_addr_t dma; +@@ -962,6 +1119,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, + xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, flags); + if (!xhci->event_ring) + goto fail; ++ if (xhci_check_trb_in_td_math(xhci, flags) < 0) ++ goto fail; + + xhci->erst.entries = pci_alloc_consistent(to_pci_dev(dev), + sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS, &dma); +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -987,8 +987,7 @@ static void handle_port_status(struct xh + * TRB in this TD, this function returns that TRB's segment. Otherwise it + * returns 0. + */ +-static struct xhci_segment *trb_in_td( +- struct xhci_segment *start_seg, ++struct xhci_segment *trb_in_td(struct xhci_segment *start_seg, + union xhci_trb *start_trb, + union xhci_trb *end_trb, + dma_addr_t suspect_dma) diff --git a/usb/usb-xhci-handle-errors-that-cause-endpoint-halts.patch b/usb/usb-xhci-handle-errors-that-cause-endpoint-halts.patch new file mode 100644 index 00000000000000..e6e8b420df3aec --- /dev/null +++ b/usb/usb-xhci-handle-errors-that-cause-endpoint-halts.patch @@ -0,0 +1,150 @@ +From sarah.a.sharp@linux.intel.com Fri Nov 20 12:34:57 2009 +From: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Date: Wed, 11 Nov 2009 10:28:44 -0800 +Subject: USB: xhci: Handle errors that cause endpoint halts. +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org +Message-ID: <20091111182844.GA4519@xanatos> +Content-Disposition: inline + + +The xHCI 0.95 and 0.96 specification defines several transfer buffer +request completion codes that indicate a USB transaction error occurred. +When a stall, babble, transaction, or split transaction error completion code +is set, the xHCI has halted that endpoint ring. Software must issue a +Reset Endpoint command and a Set Transfer Ring Dequeue Pointer command +to clean up the halted ring. + +The USB device driver is supposed to call into usb_reset_endpoint() when +an endpoint stalls. That calls into the xHCI driver to issue the proper +commands. However, drivers don't call that function for the other +errors that cause the xHC to halt the endpoint ring. If a babble, +transaction, or split transaction error occurs, check if the endpoint +context reports a halted condition, and clean up the endpoint ring if it +does. + +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-ring.c | 79 ++++++++++++++++++++++++++++++++----------- + 1 file changed, 60 insertions(+), 19 deletions(-) + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1037,6 +1037,45 @@ struct xhci_segment *trb_in_td(struct xh + return 0; + } + ++static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci, ++ unsigned int slot_id, unsigned int ep_index, ++ 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; ++ xhci_queue_reset_ep(xhci, slot_id, ep_index); ++ xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index); ++ xhci_ring_cmd_db(xhci); ++} ++ ++/* Check if an error has halted the endpoint ring. The class driver will ++ * cleanup the halt for a non-default control endpoint if we indicate a stall. ++ * However, a babble and other errors also halt the endpoint ring, and the class ++ * driver won't clear the halt in that case, so we need to issue a Set Transfer ++ * Ring Dequeue Pointer command manually. ++ */ ++static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci, ++ struct xhci_ep_ctx *ep_ctx, ++ unsigned int trb_comp_code) ++{ ++ /* TRB completion codes that may require a manual halt cleanup */ ++ if (trb_comp_code == COMP_TX_ERR || ++ trb_comp_code == COMP_BABBLE || ++ trb_comp_code == COMP_SPLIT_ERR) ++ /* The 0.96 spec says a babbling control endpoint ++ * is not halted. The 0.96 spec says it is. Some HW ++ * claims to be 0.95 compliant, but it halts the control ++ * endpoint anyway. Check if a babble halted the ++ * endpoint. ++ */ ++ if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_HALTED) ++ return 1; ++ ++ return 0; ++} ++ + /* + * If this function returns an error condition, it means it got a Transfer + * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. +@@ -1191,15 +1230,14 @@ static int handle_tx_event(struct xhci_h + else + status = 0; + break; +- case COMP_BABBLE: +- /* The 0.96 spec says a babbling control endpoint +- * is not halted. The 0.96 spec says it is. Some HW +- * claims to be 0.95 compliant, but it halts the control +- * endpoint anyway. Check if a babble halted the +- * endpoint. +- */ +- if (ep_ctx->ep_info != EP_STATE_HALTED) ++ ++ default: ++ if (!xhci_requires_manual_halt_cleanup(xhci, ++ ep_ctx, trb_comp_code)) + break; ++ xhci_dbg(xhci, "TRB error code %u, " ++ "halted endpoint index = %u\n", ++ trb_comp_code, ep_index); + /* else fall through */ + case COMP_STALL: + /* Did we transfer part of the data (middle) phase? */ +@@ -1211,15 +1249,9 @@ static int handle_tx_event(struct xhci_h + else + td->urb->actual_length = 0; + +- ep->stopped_td = td; +- ep->stopped_trb = event_trb; +- xhci_queue_reset_ep(xhci, slot_id, ep_index); +- xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index); +- xhci_ring_cmd_db(xhci); ++ xhci_cleanup_halted_endpoint(xhci, ++ slot_id, ep_index, td, event_trb); + goto td_cleanup; +- default: +- /* Others already handled above */ +- break; + } + /* + * Did we transfer any data, despite the errors that might have +@@ -1357,16 +1389,25 @@ static int handle_tx_event(struct xhci_h + ep->stopped_td = td; + ep->stopped_trb = event_trb; + } else { +- if (trb_comp_code == COMP_STALL || +- trb_comp_code == COMP_BABBLE) { ++ if (trb_comp_code == COMP_STALL) { + /* The transfer is completed from the driver's + * perspective, but we need to issue a set dequeue + * command for this stalled endpoint to move the dequeue + * pointer past the TD. We can't do that here because +- * the halt condition must be cleared first. ++ * the halt condition must be cleared first. Let the ++ * USB class driver clear the stall later. + */ + ep->stopped_td = td; + ep->stopped_trb = event_trb; ++ } else if (xhci_requires_manual_halt_cleanup(xhci, ++ ep_ctx, trb_comp_code)) { ++ /* Other types of errors halt the endpoint, but the ++ * class driver doesn't call usb_reset_endpoint() unless ++ * the error is -EPIPE. Clear the halted status in the ++ * xHCI hardware manually. ++ */ ++ xhci_cleanup_halted_endpoint(xhci, ++ slot_id, ep_index, td, event_trb); + } else { + /* Update ring dequeue pointer */ + while (ep_ring->dequeue != td->last_trb) diff --git a/usb/usb-xhci-return-eproto-on-a-split-transaction-error.patch b/usb/usb-xhci-return-eproto-on-a-split-transaction-error.patch new file mode 100644 index 00000000000000..f6f99ad1a9a818 --- /dev/null +++ b/usb/usb-xhci-return-eproto-on-a-split-transaction-error.patch @@ -0,0 +1,30 @@ +From sarah.a.sharp@linux.intel.com Fri Nov 20 12:33:23 2009 +From: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Date: Wed, 11 Nov 2009 10:28:36 -0800 +Subject: USB: xhci: Return -EPROTO on a split transaction error. +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org +Message-ID: <20091111182836.GA4439@xanatos> +Content-Disposition: inline + + +When the xHCI hardware says a transfer completed with a split +transaction error, set the URB status to -EPROTO. + +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-ring.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1139,6 +1139,7 @@ static int handle_tx_event(struct xhci_h + xhci_warn(xhci, "WARN: TRB error on endpoint\n"); + status = -EILSEQ; + break; ++ case COMP_SPLIT_ERR: + case COMP_TX_ERR: + xhci_warn(xhci, "WARN: transfer error on endpoint\n"); + status = -EPROTO; diff --git a/usb/usb-xhci-return-success-for-vendor-specific-info-codes.patch b/usb/usb-xhci-return-success-for-vendor-specific-info-codes.patch new file mode 100644 index 00000000000000..179ca6d90bcf64 --- /dev/null +++ b/usb/usb-xhci-return-success-for-vendor-specific-info-codes.patch @@ -0,0 +1,40 @@ +From sarah.a.sharp@linux.intel.com Fri Nov 20 12:34:43 2009 +From: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Date: Wed, 11 Nov 2009 10:28:40 -0800 +Subject: USB: xhci: Return success for vendor-specific info codes. +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org +Message-ID: <20091111182840.GA4479@xanatos> +Content-Disposition: inline + + +An xHCI host controller manufacturer can choose to implement several +vendor-specific informational completion codes. These are all to be +treated like a successful transfer completion. + +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-ring.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1153,6 +1153,16 @@ static int handle_tx_event(struct xhci_h + status = -ENOSR; + break; + default: ++ if (trb_comp_code >= 224 && trb_comp_code <= 255) { ++ /* Vendor defined "informational" completion code, ++ * treat as not-an-error. ++ */ ++ xhci_dbg(xhci, "Vendor defined info completion code %u\n", ++ trb_comp_code); ++ xhci_dbg(xhci, "Treating code as success.\n"); ++ status = 0; ++ break; ++ } + xhci_warn(xhci, "ERROR Unknown event condition, HC probably busted\n"); + urb = NULL; + goto cleanup; diff --git a/usb/usb-xhci-set-transfer-descriptor-size-field-correctly.patch b/usb/usb-xhci-set-transfer-descriptor-size-field-correctly.patch new file mode 100644 index 00000000000000..b0d41eccf8c36a --- /dev/null +++ b/usb/usb-xhci-set-transfer-descriptor-size-field-correctly.patch @@ -0,0 +1,120 @@ +From sarah.a.sharp@linux.intel.com Fri Nov 20 12:33:07 2009 +From: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Date: Wed, 11 Nov 2009 10:28:30 -0800 +Subject: USB: xhci: Set transfer descriptor size field correctly. +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org +Message-ID: <20091111182830.GA4399@xanatos> +Content-Disposition: inline + + +The transfer descriptor (TD) is a series of transfer request buffers +(TRBs) that describe the buffer pointer, length, and other +characteristics. The xHCI controllers want to know an estimate of how +long the TD is, for caching reasons. In each TRB, there is a "TD size" +field that provides a rough estimate of the remaining buffers to be +transmitted, including the buffer pointed to by that TRB. + +The TD size is 5 bits long, and contains the remaining size in bytes, +right shifted by 10 bits. So a remaining TD size less than 1024 would get +a zero in the TD size field, and a remaining size greater than 32767 would +get 31 in the field. + +This patches fixes a bug in the TD_REMAINDER macro that is triggered when +the URB has a scatter gather list with a size bigger than 32767 bytes. +Not all host controllers pay attention to the TD size field, so the bug +will not appear on all USB 3.0 hosts. + +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-ring.c | 27 ++++++++++++++++++++++++--- + drivers/usb/host/xhci.h | 3 --- + 2 files changed, 24 insertions(+), 6 deletions(-) + +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -828,9 +828,6 @@ struct xhci_event_cmd { + /* Normal TRB fields */ + /* transfer_len bitmasks - bits 0:16 */ + #define TRB_LEN(p) ((p) & 0x1ffff) +-/* TD size - number of bytes remaining in the TD (including this TRB): +- * bits 17 - 21. Shift the number of bytes by 10. */ +-#define TD_REMAINDER(p) ((((p) >> 10) & 0x1f) << 17) + /* Interrupter Target - which MSI-X vector to target the completion event at */ + #define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) + #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1699,6 +1699,21 @@ int xhci_queue_intr_tx(struct xhci_hcd * + return xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); + } + ++/* ++ * The TD size is the number of bytes remaining in the TD (including this TRB), ++ * right shifted by 10. ++ * It must fit in bits 21:17, so it can't be bigger than 31. ++ */ ++static u32 xhci_td_remainder(unsigned int remainder) ++{ ++ u32 max = (1 << (21 - 17 + 1)) - 1; ++ ++ if ((remainder >> 10) >= max) ++ return max << 17; ++ else ++ return (remainder >> 10) << 17; ++} ++ + static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + struct urb *urb, int slot_id, unsigned int ep_index) + { +@@ -1756,6 +1771,7 @@ static int queue_bulk_sg_tx(struct xhci_ + do { + u32 field = 0; + u32 length_field = 0; ++ u32 remainder = 0; + + /* Don't change the cycle bit of the first TRB until later */ + if (first_trb) +@@ -1785,8 +1801,10 @@ static int queue_bulk_sg_tx(struct xhci_ + (unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1), + (unsigned int) addr + trb_buff_len); + } ++ remainder = xhci_td_remainder(urb->transfer_buffer_length - ++ running_total) ; + length_field = TRB_LEN(trb_buff_len) | +- TD_REMAINDER(urb->transfer_buffer_length - running_total) | ++ remainder | + TRB_INTR_TARGET(0); + queue_trb(xhci, ep_ring, false, + lower_32_bits(addr), +@@ -1899,6 +1917,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd * + + /* Queue the first TRB, even if it's zero-length */ + do { ++ u32 remainder = 0; + field = 0; + + /* Don't change the cycle bit of the first TRB until later */ +@@ -1917,8 +1936,10 @@ int xhci_queue_bulk_tx(struct xhci_hcd * + td->last_trb = ep_ring->enqueue; + field |= TRB_IOC; + } ++ remainder = xhci_td_remainder(urb->transfer_buffer_length - ++ running_total); + length_field = TRB_LEN(trb_buff_len) | +- TD_REMAINDER(urb->transfer_buffer_length - running_total) | ++ remainder | + TRB_INTR_TARGET(0); + queue_trb(xhci, ep_ring, false, + lower_32_bits(addr), +@@ -2006,7 +2027,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd * + /* If there's data, queue data TRBs */ + field = 0; + length_field = TRB_LEN(urb->transfer_buffer_length) | +- TD_REMAINDER(urb->transfer_buffer_length) | ++ xhci_td_remainder(urb->transfer_buffer_length) | + TRB_INTR_TARGET(0); + if (urb->transfer_buffer_length > 0) { + if (setup->bRequestType & USB_DIR_IN) diff --git a/usb/usbtest-make-module-param-pattern-writeable.patch b/usb/usbtest-make-module-param-pattern-writeable.patch new file mode 100644 index 00000000000000..bc99d4b26e6d27 --- /dev/null +++ b/usb/usbtest-make-module-param-pattern-writeable.patch @@ -0,0 +1,44 @@ +From vikram.pandita@ti.com Fri Nov 20 11:56:34 2009 +From: Vikram Pandita <vikram.pandita@ti.com> +Date: Mon, 9 Nov 2009 21:24:32 -0600 +Subject: usbtest: make module param pattern writeable +To: linux-usb@vger.kernel.org, <dbrownell@users.sourceforge.net> +Cc: linux-omap@vger.kernel.org, Vikram Pandita <vikram.pandita@ti.com>, Anand Gadiyar <gadiyar@ti.com> +Message-ID: <1257823472-19369-1-git-send-email-vikram.pandita@ti.com> + + +Allow module_param to be writeable. This allows us to change +the parameter if usbtest is built-in in the kernel. + +Signed-off-by: Vikram Pandita <vikram.pandita@ti.com> +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +Cc: David Brownell <david-b@pacbell.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/misc/usbtest.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/usb/misc/usbtest.c ++++ b/drivers/usb/misc/usbtest.c +@@ -213,8 +213,9 @@ static struct urb *simple_alloc_urb ( + } + + static unsigned pattern = 0; +-module_param (pattern, uint, S_IRUGO); +-MODULE_PARM_DESC(pattern, "i/o pattern (0 == zeroes)"); ++static unsigned mod_pattern = 0; ++module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(mod_pattern, "i/o pattern (0 == zeroes)"); + + static inline void simple_fill_buf (struct urb *urb) + { +@@ -1567,6 +1568,8 @@ usbtest_ioctl (struct usb_interface *int + + // FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is. + ++ pattern = mod_pattern; ++ + if (code != USBTEST_REQUEST) + return -EOPNOTSUPP; + |
