[RFC 6/6] target: add uasp fabric module [WIP]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



As of now it supports HS only (SS is not yet supported).
Only commands are passed (read/write requests are handled but TASK
MANAGEMENT functions and others from this category are not).

Temporary, it requires the transfer direction to be hardcoded.
I need to figure out how to bypass informations to the gadget framework.
Until then, enjoy the_only_tpg_I_currently_have.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>
---
 drivers/target/Kconfig           |    1 +
 drivers/target/Makefile          |    1 +
 drivers/target/uasp/Kconfig      |    6 +
 drivers/target/uasp/Makefile     |    5 +
 drivers/target/uasp/base.h       |   39 ++
 drivers/target/uasp/configfs.c   |  518 +++++++++++++++++++++++++
 drivers/target/uasp/configfs.h   |    7 +
 drivers/target/uasp/fabric.c     |  407 ++++++++++++++++++++
 drivers/target/uasp/fabric.h     |   39 ++
 drivers/target/uasp/gadget.c     |  770 ++++++++++++++++++++++++++++++++++++++
 drivers/target/uasp/gadget.h     |   81 ++++
 drivers/target/uasp/gadget_ops.h |   13 +
 drivers/usb/gadget/Kconfig       |    3 +
 13 files changed, 1890 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/uasp/Kconfig
 create mode 100644 drivers/target/uasp/Makefile
 create mode 100644 drivers/target/uasp/base.h
 create mode 100644 drivers/target/uasp/configfs.c
 create mode 100644 drivers/target/uasp/configfs.h
 create mode 100644 drivers/target/uasp/fabric.c
 create mode 100644 drivers/target/uasp/fabric.h
 create mode 100644 drivers/target/uasp/gadget.c
 create mode 100644 drivers/target/uasp/gadget.h
 create mode 100644 drivers/target/uasp/gadget_ops.h

diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig
index 7a7e442..d1ebc58 100644
--- a/drivers/target/Kconfig
+++ b/drivers/target/Kconfig
@@ -43,3 +43,4 @@ source "drivers/target/tcm_qla2xxx/Kconfig"
 source "drivers/target/tcm_vhost/Kconfig"
 
 endif
+source "drivers/target/uasp/Kconfig"
diff --git a/drivers/target/Makefile b/drivers/target/Makefile
index 8ecfaae..5e1cd20 100644
--- a/drivers/target/Makefile
+++ b/drivers/target/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_TCM_FC)		+= tcm_fc/
 obj-$(CONFIG_ISCSI_TARGET)	+= iscsi/
 obj-$(CONFIG_TCM_QLA2XXX)	+= tcm_qla2xxx/
 obj-$(CONFIG_TCM_VHOST)		+= tcm_vhost/
+obj-$(CONFIG_TARGET_UASP)	+= uasp/
diff --git a/drivers/target/uasp/Kconfig b/drivers/target/uasp/Kconfig
new file mode 100644
index 0000000..0d48a58
--- /dev/null
+++ b/drivers/target/uasp/Kconfig
@@ -0,0 +1,6 @@
+config TARGET_UASP
+	tristate "UASP fabric module"
+	depends on TARGET_CORE && CONFIGFS_FS
+	depends on USB_GADGET
+	---help---
+	Say Y here to enable the UASP fabric module
diff --git a/drivers/target/uasp/Makefile b/drivers/target/uasp/Makefile
new file mode 100644
index 0000000..e7237b9
--- /dev/null
+++ b/drivers/target/uasp/Makefile
@@ -0,0 +1,5 @@
+CFLAGS_gadget.o			:= -I$(srctree)/drivers/usb/gadget
+tcm_uasp-objs			:= fabric.o \
+					gadget.o \
+					   configfs.o
+obj-$(CONFIG_TARGET_UASP)	+= tcm_uasp.o
diff --git a/drivers/target/uasp/base.h b/drivers/target/uasp/base.h
new file mode 100644
index 0000000..cfea496d
--- /dev/null
+++ b/drivers/target/uasp/base.h
@@ -0,0 +1,39 @@
+#define UASP_VERSION  "v0.1"
+#define UASP_NAMELEN 32
+
+struct uasp_nacl {
+	/* Binary World Wide unique Port Name for SAS Initiator port */
+	u64 iport_wwpn;
+	/* ASCII formatted WWPN for Sas Initiator port */
+	char iport_name[UASP_NAMELEN];
+	/* Returned by uasp_make_nodeacl() */
+	struct se_node_acl se_node_acl;
+};
+
+struct tcm_uasp_nexus {
+	struct se_session *tvn_se_sess;
+};
+
+struct uasp_tpg {
+	struct mutex tpg_mutex;
+	/* SAS port target portal group tag for TCM */
+	u16 tport_tpgt;
+	/* Pointer back to uasp_tport */
+	struct uasp_tport *tport;
+	/* Returned by uasp_make_tpg() */
+	struct se_portal_group se_tpg;
+	u32 gadget_connect;
+	struct tcm_uasp_nexus *tpg_nexus;
+	atomic_t tpg_port_count;
+};
+
+struct uasp_tport {
+	/* SCSI protocol the tport is providing */
+	u8 tport_proto_id;
+	/* Binary World Wide unique Port Name for SAS Target port */
+	u64 tport_wwpn;
+	/* ASCII formatted WWPN for SAS Target port */
+	char tport_name[UASP_NAMELEN];
+	/* Returned by uasp_make_tport() */
+	struct se_wwn tport_wwn;
+};
diff --git a/drivers/target/uasp/configfs.c b/drivers/target/uasp/configfs.c
new file mode 100644
index 0000000..01160ba
--- /dev/null
+++ b/drivers/target/uasp/configfs.c
@@ -0,0 +1,518 @@
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <asm/unaligned.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "base.h"
+#include "fabric.h"
+#include "gadget_ops.h"
+
+/* Local pointer to allocated TCM configfs fabric module */
+struct target_fabric_configfs *uasp_fabric_configfs;
+
+static const char *uasp_check_wwn(const char *name)
+{
+	const char *n;
+	unsigned int len;
+
+	n = strstr(name, "naa.");
+	if (!n)
+		return NULL;
+	n += 4;
+	len = strlen(n);
+	if (len == 0 || len > UASP_NAMELEN - 1)
+		return NULL;
+	return n;
+}
+
+static struct se_node_acl *uasp_make_nodeacl(
+	struct se_portal_group *se_tpg,
+	struct config_group *group,
+	const char *name)
+{
+	struct se_node_acl *se_nacl, *se_nacl_new;
+	struct uasp_nacl *nacl;
+	u64 wwpn = 0;
+	u32 nexus_depth;
+	const char *wnn_name;
+
+	wnn_name = uasp_check_wwn(name);
+	if (!wnn_name)
+		return ERR_PTR(-EINVAL);
+	se_nacl_new = uasp_alloc_fabric_acl(se_tpg);
+	if (!(se_nacl_new))
+		return ERR_PTR(-ENOMEM);
+//#warning FIXME: Hardcoded nexus depth in uasp_make_nodeacl()
+	nexus_depth = 1;
+	/*
+	 * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+	 * when converting a NodeACL from demo mode -> explict
+	 */
+	se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+				name, nexus_depth);
+	if (IS_ERR(se_nacl)) {
+		uasp_release_fabric_acl(se_tpg, se_nacl_new);
+		return se_nacl;
+	}
+	/*
+	 * Locate our struct uasp_nacl and set the FC Nport WWPN
+	 */
+	nacl = container_of(se_nacl, struct uasp_nacl, se_node_acl);
+	nacl->iport_wwpn = wwpn;
+	snprintf(nacl->iport_name, sizeof(nacl->iport_name), "%s", name);
+	return se_nacl;
+}
+
+static void uasp_drop_nodeacl(struct se_node_acl *se_acl)
+{
+	struct uasp_nacl *nacl = container_of(se_acl,
+				struct uasp_nacl, se_node_acl);
+	core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+	kfree(nacl);
+}
+
+struct uasp_tpg *the_only_tpg_I_currently_have;
+
+static struct se_portal_group *uasp_make_tpg(
+	struct se_wwn *wwn,
+	struct config_group *group,
+	const char *name)
+{
+	struct uasp_tport *tport = container_of(wwn, struct uasp_tport,
+			tport_wwn);
+	struct uasp_tpg *tpg;
+	unsigned long tpgt;
+	int ret;
+
+	if (strstr(name, "tpgt_") != name)
+		return ERR_PTR(-EINVAL);
+	if (kstrtoul(name + 5, 0, &tpgt) || tpgt > UINT_MAX)
+		return ERR_PTR(-EINVAL);
+
+	tpg = kzalloc(sizeof(struct uasp_tpg), GFP_KERNEL);
+	if (!tpg) {
+		printk(KERN_ERR "Unable to allocate struct uasp_tpg");
+		return ERR_PTR(-ENOMEM);
+	}
+	mutex_init(&tpg->tpg_mutex);
+	atomic_set(&tpg->tpg_port_count, 0);
+	tpg->tport = tport;
+	tpg->tport_tpgt = tpgt;
+
+	ret = core_tpg_register(&uasp_fabric_configfs->tf_ops, wwn,
+				&tpg->se_tpg, tpg,
+				TRANSPORT_TPG_TYPE_NORMAL);
+	if (ret < 0) {
+		kfree(tpg);
+		return NULL;
+	}
+	if (the_only_tpg_I_currently_have)
+		BUG();
+	the_only_tpg_I_currently_have = tpg;
+	return &tpg->se_tpg;
+}
+
+static void uasp_drop_tpg(struct se_portal_group *se_tpg)
+{
+	struct uasp_tpg *tpg = container_of(se_tpg,
+				struct uasp_tpg, se_tpg);
+
+	core_tpg_deregister(se_tpg);
+	kfree(tpg);
+	BUG_ON(!the_only_tpg_I_currently_have);
+	the_only_tpg_I_currently_have = NULL;
+}
+
+static struct se_wwn *uasp_make_tport(
+	struct target_fabric_configfs *tf,
+	struct config_group *group,
+	const char *name)
+{
+	struct uasp_tport *tport;
+	const char *wnn_name;
+	u64 wwpn = 0;
+
+	wnn_name = uasp_check_wwn(name);
+	if (!wnn_name)
+		return ERR_PTR(-EINVAL);
+
+	tport = kzalloc(sizeof(struct uasp_tport), GFP_KERNEL);
+	if (!(tport)) {
+		printk(KERN_ERR "Unable to allocate struct uasp_tport");
+		return ERR_PTR(-ENOMEM);
+	}
+	tport->tport_wwpn = wwpn;
+	snprintf(tport->tport_name, sizeof(tport->tport_name), wnn_name);
+	return &tport->tport_wwn;
+}
+
+static void uasp_drop_tport(struct se_wwn *wwn)
+{
+	struct uasp_tport *tport = container_of(wwn,
+				struct uasp_tport, tport_wwn);
+	kfree(tport);
+}
+
+static ssize_t uasp_wwn_show_attr_version(
+	struct target_fabric_configfs *tf,
+	char *page)
+{
+	return sprintf(page, "UASP fabric module %s on %s/%s"
+		"on "UTS_RELEASE"\n", UASP_VERSION, utsname()->sysname,
+		utsname()->machine);
+}
+
+TF_WWN_ATTR_RO(uasp, version);
+
+static struct configfs_attribute *uasp_wwn_attrs[] = {
+	&uasp_wwn_version.attr,
+	NULL,
+};
+
+static ssize_t tcm_uasp_tpg_show_gadget_connect(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct uasp_tpg  *tpg = container_of(se_tpg, struct uasp_tpg, se_tpg);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", tpg->gadget_connect);
+}
+
+static ssize_t tcm_uasp_tpg_store_gadget_connect(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct uasp_tpg  *tpg = container_of(se_tpg, struct uasp_tpg, se_tpg);
+	unsigned int op;
+	ssize_t ret;
+
+	ret = kstrtoul(page, 0, &op);
+	if (ret < 0)
+		return -EINVAL;
+	if (op > 1)
+		return -EINVAL;
+
+	if (op && tpg->gadget_connect)
+		goto out;
+	if (!op && !tpg->gadget_connect)
+		goto out;
+
+	if (op) {
+		ret = gadget_attach();
+		if (ret)
+			goto out;
+		ret = count;
+	} else {
+		gadget_detach();
+	}
+	tpg->gadget_connect = op;
+out:
+	return ret;
+}
+
+TF_TPG_BASE_ATTR(tcm_uasp, gadget_connect, S_IRUGO | S_IWUSR);
+
+static ssize_t tcm_uasp_tpg_show_nexus(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct uasp_tpg *tpg = container_of(se_tpg, struct uasp_tpg, se_tpg);
+	struct tcm_uasp_nexus *tv_nexus;
+	ssize_t ret;
+
+	mutex_lock(&tpg->tpg_mutex);
+	tv_nexus = tpg->tpg_nexus;
+	if (!tv_nexus) {
+		ret = -ENODEV;
+		goto out;
+	}
+	ret = snprintf(page, PAGE_SIZE, "%s\n",
+			tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+out:
+	mutex_unlock(&tpg->tpg_mutex);
+	return ret;
+}
+
+static int tcm_uasp_make_nexus(struct uasp_tpg *tpg, char *name)
+{
+	struct se_portal_group *se_tpg;
+	struct tcm_uasp_nexus *tv_nexus;
+	int ret;
+
+	mutex_lock(&tpg->tpg_mutex);
+	if (tpg->tpg_nexus) {
+		ret = -EEXIST;
+		pr_debug("tpg->tpg_nexus already exists\n");
+		goto err_unlock;
+	}
+	se_tpg = &tpg->se_tpg;
+
+	ret = -ENOMEM;
+	tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL);
+	if (!tv_nexus) {
+		pr_err("Unable to allocate struct tcm_vhost_nexus\n");
+		goto err_unlock;
+	}
+	tv_nexus->tvn_se_sess = transport_init_session();
+	if (IS_ERR(tv_nexus->tvn_se_sess))
+		goto err_free;
+
+	/*
+	 * Since we are running in 'demo mode' this call with generate a
+	 * struct se_node_acl for the tcm_vhost struct se_portal_group with
+	 * the SCSI Initiator port name of the passed configfs group 'name'.
+	 */
+	tv_nexus->tvn_se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
+			se_tpg, name);
+	if (!tv_nexus->tvn_se_sess->se_node_acl) {
+		pr_debug("core_tpg_check_initiator_node_acl() failed"
+				" for %s\n", name);
+		goto err_session;
+	}
+	/*
+	 * Now register the TCM vHost virtual I_T Nexus as active with the
+	 * call to __transport_register_session()
+	 */
+	__transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
+			tv_nexus->tvn_se_sess, tv_nexus);
+	tpg->tpg_nexus = tv_nexus;
+	mutex_unlock(&tpg->tpg_mutex);
+	return 0;
+
+err_session:
+	transport_free_session(tv_nexus->tvn_se_sess);
+err_free:
+	kfree(tv_nexus);
+err_unlock:
+	mutex_unlock(&tpg->tpg_mutex);
+	return ret;
+}
+
+static int tcm_uasp_drop_nexus(struct uasp_tpg *tpg)
+{
+	struct se_session *se_sess;
+	struct tcm_uasp_nexus *tv_nexus;
+	int ret = -ENODEV;
+
+	mutex_lock(&tpg->tpg_mutex);
+	tv_nexus = tpg->tpg_nexus;
+	if (!tv_nexus)
+		goto out;
+
+	se_sess = tv_nexus->tvn_se_sess;
+	if (!se_sess)
+		goto out;
+
+	if (atomic_read(&tpg->tpg_port_count)) {
+		ret = -EPERM;
+		pr_err("Unable to remove Host I_T Nexus with"
+				" active TPG port count: %d\n",
+				atomic_read(&tpg->tpg_port_count));
+		goto out;
+	}
+
+	pr_debug("Removing I_T Nexus to Initiator Port: %s\n",
+			tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+	/*
+	 * Release the SCSI I_T Nexus to the emulated vHost Target Port
+	 */
+	transport_deregister_session(tv_nexus->tvn_se_sess);
+	tpg->tpg_nexus = NULL;
+
+	kfree(tv_nexus);
+out:
+	mutex_unlock(&tpg->tpg_mutex);
+	return 0;
+}
+
+static ssize_t tcm_uasp_tpg_store_nexus(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct uasp_tpg *tpg = container_of(se_tpg, struct uasp_tpg, se_tpg);
+	unsigned char i_port[UASP_NAMELEN], *ptr;
+	int ret;
+
+	if (!strncmp(page, "NULL", 4)) {
+		ret = tcm_uasp_drop_nexus(tpg);
+		return (!ret) ? count : ret;
+	}
+	if (strlen(page) > UASP_NAMELEN) {
+		pr_err("Emulated NAA Sas Address: %s, exceeds"
+				" max: %d\n", page, UASP_NAMELEN);
+		return -EINVAL;
+	}
+	snprintf(i_port, UASP_NAMELEN, "%s", page);
+
+	ptr = strstr(i_port, "naa.");
+	if (!ptr) {
+		pr_err("Missing 'naa.' prefix\n");
+		return -EINVAL;
+	}
+
+	if (i_port[strlen(i_port) - 1] == '\n')
+		i_port[strlen(i_port) - 1] = '\0';
+
+	ret = tcm_uasp_make_nexus(tpg, &i_port[4]);
+	if (ret < 0)
+		return ret;
+	return count;
+}
+
+TF_TPG_BASE_ATTR(tcm_uasp, nexus, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *uasp_base_attrs[] = {
+	&tcm_uasp_tpg_gadget_connect.attr,
+	&tcm_uasp_tpg_nexus.attr,
+	NULL,
+};
+
+static int uasp_port_link(struct se_portal_group *se_tpg, struct se_lun *lun)
+{
+	struct uasp_tpg *tpg = container_of(se_tpg, struct uasp_tpg, se_tpg);
+
+	atomic_inc(&tpg->tpg_port_count);
+	smp_mb__after_atomic_inc();
+	return 0;
+}
+
+static void uasp_port_unlink(struct se_portal_group *se_tpg,
+		struct se_lun *se_lun)
+{
+	struct uasp_tpg *tpg = container_of(se_tpg, struct uasp_tpg, se_tpg);
+
+	atomic_dec(&tpg->tpg_port_count);
+	smp_mb__after_atomic_dec();
+}
+
+int uasp_check_stop_free(struct se_cmd *se_cmd);
+
+static struct target_core_fabric_ops uasp_ops = {
+	.get_fabric_name		= uasp_get_fabric_name,
+	.get_fabric_proto_ident		= uasp_get_fabric_proto_ident,
+	.tpg_get_wwn			= uasp_get_fabric_wwn,
+	.tpg_get_tag			= uasp_get_tag,
+	.tpg_get_default_depth		= uasp_get_default_depth,
+	.tpg_get_pr_transport_id	= uasp_get_pr_transport_id,
+	.tpg_get_pr_transport_id_len	= uasp_get_pr_transport_id_len,
+	.tpg_parse_pr_out_transport_id	= uasp_parse_pr_out_transport_id,
+	.tpg_check_demo_mode		= uasp_check_true,
+	.tpg_check_demo_mode_cache	= uasp_check_false,
+	.tpg_check_demo_mode_write_protect = uasp_check_false,
+	.tpg_check_prod_mode_write_protect = uasp_check_false,
+	.tpg_alloc_fabric_acl		= uasp_alloc_fabric_acl,
+	.tpg_release_fabric_acl		= uasp_release_fabric_acl,
+	.tpg_get_inst_index		= uasp_tpg_get_inst_index,
+	.cmd_queue_supported		= uasp_cmd_queue_supported,
+	.new_cmd_map			= uasp_new_cmd,
+	.release_cmd			= uasp_release_cmd,
+	.shutdown_session		= uasp_shutdown_session,
+	.close_session			= uasp_close_session,
+	.stop_session			= uasp_stop_session,
+	.fall_back_to_erl0		= uasp_reset_nexus,
+	.sess_logged_in			= uasp_sess_logged_in,
+	.sess_get_index			= uasp_sess_get_index,
+	.sess_get_initiator_sid		= NULL,
+	.write_pending			= uasp_write_pending,
+	.write_pending_status		= uasp_write_pending_status,
+	.set_default_node_attributes	= uasp_set_default_node_attrs,
+	.get_task_tag			= uasp_get_task_tag,
+	.get_cmd_state			= uasp_get_cmd_state,
+	.queue_data_in			= uasp_queue_data_in,
+	.queue_status			= uasp_queue_status,
+	.queue_tm_rsp			= uasp_queue_tm_rsp,
+	.get_fabric_sense_len		= uasp_get_fabric_sense_len,
+	.set_fabric_sense_len		= uasp_set_fabric_sense_len,
+	.is_state_remove		= uasp_is_state_remove,
+	.check_stop_free		= uasp_check_stop_free,
+
+	/*
+	 * Setup function pointers for generic logic in target_core_fabric_configfs.c
+	 */
+	.fabric_make_wwn		= uasp_make_tport,
+	.fabric_drop_wwn		= uasp_drop_tport,
+	.fabric_make_tpg		= uasp_make_tpg,
+	.fabric_drop_tpg		= uasp_drop_tpg,
+	.fabric_post_link		= uasp_port_link,
+	.fabric_pre_unlink		= uasp_port_unlink,
+	.fabric_make_np			= NULL,
+	.fabric_drop_np			= NULL,
+	.fabric_make_nodeacl		= uasp_make_nodeacl,
+	.fabric_drop_nodeacl		= uasp_drop_nodeacl,
+};
+
+int uasp_register_configfs(void)
+{
+	struct target_fabric_configfs *fabric;
+	int ret;
+
+	printk(KERN_INFO "UASP fabric module %s on %s/%s"
+		" on " UTS_RELEASE "\n", UASP_VERSION, utsname()->sysname,
+		utsname()->machine);
+	/*
+	 * Register the top level struct config_item_type with TCM core
+	 */
+	fabric = target_fabric_configfs_init(THIS_MODULE, "uasp");
+	if (IS_ERR(fabric)) {
+		printk(KERN_ERR "target_fabric_configfs_init() failed\n");
+		return PTR_ERR(fabric);
+	}
+	/*
+	 * Setup fabric->tf_ops from our local uasp_ops
+	 */
+	fabric->tf_ops = uasp_ops;
+	/*
+	 * Setup default attribute lists for various fabric->tf_cit_tmpl
+	 */
+	TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = uasp_wwn_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = uasp_base_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+	/*
+	 * Register the fabric for use within TCM
+	 */
+	ret = target_fabric_configfs_register(fabric);
+	if (ret < 0) {
+		printk(KERN_ERR "target_fabric_configfs_register() failed"
+				" for UASP\n");
+		return ret;
+	}
+	/*
+	 * Setup our local pointer to *fabric
+	 */
+	uasp_fabric_configfs = fabric;
+	printk(KERN_INFO "UASP[0] - Set fabric -> uasp_fabric_configfs\n");
+	return 0;
+};
+
+void uasp_deregister_configfs(void)
+{
+	if (!(uasp_fabric_configfs))
+		return;
+
+	target_fabric_configfs_deregister(uasp_fabric_configfs);
+	uasp_fabric_configfs = NULL;
+	printk(KERN_INFO "UASP[0] - Cleared uasp_fabric_configfs\n");
+};
diff --git a/drivers/target/uasp/configfs.h b/drivers/target/uasp/configfs.h
new file mode 100644
index 0000000..646c096
--- /dev/null
+++ b/drivers/target/uasp/configfs.h
@@ -0,0 +1,7 @@
+#ifndef __UASP_CONFIGFS_H_
+#define __UASP_CONFIGFS_H_
+
+int uasp_register_configfs(void);
+void uasp_deregister_configfs(void);
+
+#endif
diff --git a/drivers/target/uasp/fabric.c b/drivers/target/uasp/fabric.c
new file mode 100644
index 0000000..7f16709
--- /dev/null
+++ b/drivers/target/uasp/fabric.c
@@ -0,0 +1,407 @@
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+#include <scsi/scsi_tcq.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+
+#include "base.h"
+#include "fabric.h"
+#include "gadget_ops.h"
+#include "gadget.h"
+
+#define UAS_SIMPLE_TAG		0
+#define UAS_HEAD_TAG		1
+#define UAS_ORDERED_TAG		2
+#define UAS_ACA			4
+
+extern struct uasp_tpg *the_only_tpg_I_currently_have;
+
+void uasp_submit_command(struct f_uas *fu, void *cmdbuf, unsigned int len)
+{
+	struct command_iu *cmd_iu = cmdbuf;
+	struct uasp_cmd *cmd;
+	struct uasp_tpg *tpg;
+	struct se_session *se_sess;
+	struct se_cmd *se_cmd;
+	struct tcm_uasp_nexus *tv_nexus;
+	u32 cmd_len;
+	u32 prio;
+	u32 unpacked_lun;
+	int ret;
+
+	if (cmd_iu->iu_id != IU_ID_COMMAND) {
+		pr_err("Unsupported type %d\n", cmd_iu->iu_id);
+		return;
+	}
+
+	cmd = kzalloc(sizeof *cmd, GFP_ATOMIC);
+	if (!cmd)
+		return;
+
+	cmd->fu = fu;
+
+	/* XXX until I figure out why I can't free in on complete */
+	kref_init(&cmd->ref);
+	kref_get(&cmd->ref);
+
+	tpg = the_only_tpg_I_currently_have;
+
+	cmd_len = cmd_iu->len + 16;
+	if (cmd_len > UASP_MAX_CMD) {
+		pr_err("%s() CDB too long\n", __func__);
+		goto err;
+	}
+
+	memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len);
+
+	cmd->tag = le16_to_cpup(&cmd_iu->tag);
+	se_cmd = &cmd->tvc_se_cmd;
+
+	tv_nexus = tpg->tpg_nexus;
+	if (!tv_nexus) {
+		pr_err("Missing nexus, ignoring command\n");
+		goto err;
+	}
+
+	se_sess = tv_nexus->tvn_se_sess;
+
+	/* XXX HS */
+	if (0) {
+		switch (cmd_iu->prio_attr) {
+		case UAS_HEAD_TAG:
+			prio = MSG_HEAD_TAG;
+			break;
+		case UAS_ORDERED_TAG:
+			prio = MSG_ORDERED_TAG;
+			break;
+
+		case UAS_ACA:
+			prio = MSG_ACA_TAG;
+			break;
+
+		default:
+			pr_debug_once("Unsupported prio_attr: %02x.\n",
+					cmd_iu->prio_attr);
+		case UAS_SIMPLE_TAG:
+			prio = MSG_SIMPLE_TAG;
+			break;
+		}
+	} else
+		prio = MSG_SIMPLE_TAG;
+
+	cmd->data_dir = cmd_iu->rsvd5;
+	se_cmd->unknown_data_length = 1;
+	transport_init_se_cmd(se_cmd, tpg->se_tpg.se_tpg_tfo, se_sess,
+			0, cmd->data_dir,
+			prio, cmd->sense_iu.sense);
+
+	unpacked_lun = scsilun_to_int(&cmd_iu->lun);
+	ret = transport_lookup_cmd_lun(se_cmd, unpacked_lun);
+	if (ret) {
+		pr_err("ERR: %s(%d)\n", __func__, __LINE__);
+		goto err;
+	}
+
+	ret = transport_generic_handle_cdb_map(&cmd->tvc_se_cmd);
+	if (ret) {
+		pr_err("ERR: %s(%d)\n", __func__, __LINE__);
+		goto err;
+	}
+	return;
+err:
+	kfree(cmd);
+}
+
+int uasp_check_true(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+int uasp_check_false(struct se_portal_group *se_tpg)
+{
+	return 0;
+}
+
+char *uasp_get_fabric_name(void)
+{
+	return "uasp";
+}
+
+u8 uasp_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+	struct uasp_tpg *tpg = container_of(se_tpg,
+				struct uasp_tpg, se_tpg);
+	struct uasp_tport *tport = tpg->tport;
+	u8 proto_id;
+
+	switch (tport->tport_proto_id) {
+	case SCSI_PROTOCOL_SAS:
+	default:
+		proto_id = sas_get_fabric_proto_ident(se_tpg);
+		break;
+	}
+
+	return proto_id;
+}
+
+char *uasp_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+	struct uasp_tpg *tpg = container_of(se_tpg,
+				struct uasp_tpg, se_tpg);
+	struct uasp_tport *tport = tpg->tport;
+
+	return &tport->tport_name[0];
+}
+
+u16 uasp_get_tag(struct se_portal_group *se_tpg)
+{
+	struct uasp_tpg *tpg = container_of(se_tpg,
+				struct uasp_tpg, se_tpg);
+	return tpg->tport_tpgt;
+}
+
+u32 uasp_get_default_depth(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+u32 uasp_get_pr_transport_id(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl,
+	struct t10_pr_registration *pr_reg,
+	int *format_code,
+	unsigned char *buf)
+{
+	struct uasp_tpg *tpg = container_of(se_tpg,
+				struct uasp_tpg, se_tpg);
+	struct uasp_tport *tport = tpg->tport;
+	int ret = 0;
+
+	switch (tport->tport_proto_id) {
+	case SCSI_PROTOCOL_SAS:
+	default:
+		ret = sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+					format_code, buf);
+		break;
+	}
+
+	return ret;
+}
+
+u32 uasp_get_pr_transport_id_len(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl,
+	struct t10_pr_registration *pr_reg,
+	int *format_code)
+{
+	struct uasp_tpg *tpg = container_of(se_tpg,
+				struct uasp_tpg, se_tpg);
+	struct uasp_tport *tport = tpg->tport;
+	int ret = 0;
+
+	switch (tport->tport_proto_id) {
+	case SCSI_PROTOCOL_SAS:
+	default:
+		ret = sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+					format_code);
+		break;
+	}
+
+	return ret;
+}
+
+char *uasp_parse_pr_out_transport_id(
+	struct se_portal_group *se_tpg,
+	const char *buf,
+	u32 *out_tid_len,
+	char **port_nexus_ptr)
+{
+	struct uasp_tpg *tpg = container_of(se_tpg,
+				struct uasp_tpg, se_tpg);
+	struct uasp_tport *tport = tpg->tport;
+	char *tid = NULL;
+
+	switch (tport->tport_proto_id) {
+	case SCSI_PROTOCOL_SAS:
+	default:
+		tid = sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+					port_nexus_ptr);
+	}
+
+	return tid;
+}
+
+struct se_node_acl *uasp_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+	struct uasp_nacl *nacl;
+
+	nacl = kzalloc(sizeof(struct uasp_nacl), GFP_KERNEL);
+	if (!nacl) {
+		printk(KERN_ERR "Unable to alocate struct uasp_nacl\n");
+		return NULL;
+	}
+
+	return &nacl->se_node_acl;
+}
+
+void uasp_release_fabric_acl(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl)
+{
+	struct uasp_nacl *nacl = container_of(se_nacl,
+			struct uasp_nacl, se_node_acl);
+	kfree(nacl);
+}
+
+u32 uasp_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+int uasp_cmd_queue_supported(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+int uasp_new_cmd(struct se_cmd *se_cmd)
+{
+	struct uasp_cmd *cmd = container_of(se_cmd, struct uasp_cmd,
+			tvc_se_cmd);
+	int ret;
+
+	ret = transport_generic_allocate_tasks(se_cmd, cmd->cmd_buf);
+	if (ret)
+		return ret;
+
+	return transport_generic_map_mem_to_cmd(se_cmd, NULL, 0, 0, 0);
+}
+
+void uasp_cmd_release(struct kref *ref)
+{
+	struct uasp_cmd *cmd = container_of(ref, struct uasp_cmd,
+			ref);
+
+	kfree(cmd->data_buf);
+	transport_generic_free_cmd(&cmd->tvc_se_cmd, 0);
+}
+
+void uasp_release_cmd(struct se_cmd *se_cmd)
+{
+	struct uasp_cmd *cmd = container_of(se_cmd, struct uasp_cmd,
+			tvc_se_cmd);
+	kfree(cmd);
+	return;
+}
+
+int uasp_shutdown_session(struct se_session *se_sess)
+{
+	return 0;
+}
+
+void uasp_close_session(struct se_session *se_sess)
+{
+	return;
+}
+
+void uasp_stop_session(struct se_session *se_sess, int sess_sleep,
+		int conn_sleep)
+{
+	return;
+}
+
+void uasp_reset_nexus(struct se_session *se_sess)
+{
+	return;
+}
+
+int uasp_sess_logged_in(struct se_session *se_sess)
+{
+	return 0;
+}
+
+u32 uasp_sess_get_index(struct se_session *se_sess)
+{
+	return 0;
+}
+
+int uasp_write_pending(struct se_cmd *se_cmd)
+{
+	guas_send_write_request(se_cmd);
+	return 0;
+}
+
+int uasp_write_pending_status(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+void uasp_set_default_node_attrs(struct se_node_acl *nacl)
+{
+	return;
+}
+
+u32 uasp_get_task_tag(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+int uasp_get_cmd_state(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+#include <target/target_core_backend.h>
+
+int uasp_queue_data_in(struct se_cmd *se_cmd)
+{
+	guas_send_read_response(se_cmd);
+	return 0;
+}
+
+int uasp_queue_status(struct se_cmd *se_cmd)
+{
+	guas_send_status_response(se_cmd);
+	return 0;
+}
+
+int uasp_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+u16 uasp_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)
+{
+	return 0;
+}
+
+u16 uasp_get_fabric_sense_len(void)
+{
+	return 0;
+}
+
+int uasp_is_state_remove(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+int uasp_check_stop_free(struct se_cmd *se_cmd)
+{
+	struct uasp_cmd *cmd = container_of(se_cmd, struct uasp_cmd,
+			tvc_se_cmd);
+
+	kref_put(&cmd->ref, uasp_cmd_release);
+	return 1;
+}
diff --git a/drivers/target/uasp/fabric.h b/drivers/target/uasp/fabric.h
new file mode 100644
index 0000000..5cb982e
--- /dev/null
+++ b/drivers/target/uasp/fabric.h
@@ -0,0 +1,39 @@
+int uasp_check_true(struct se_portal_group *);
+int uasp_check_false(struct se_portal_group *);
+char *uasp_get_fabric_name(void);
+u8 uasp_get_fabric_proto_ident(struct se_portal_group *);
+char *uasp_get_fabric_wwn(struct se_portal_group *);
+u16 uasp_get_tag(struct se_portal_group *);
+u32 uasp_get_default_depth(struct se_portal_group *);
+u32 uasp_get_pr_transport_id(struct se_portal_group *,
+			struct se_node_acl *, struct t10_pr_registration *,
+			int *, unsigned char *);
+u32 uasp_get_pr_transport_id_len(struct se_portal_group *,
+			struct se_node_acl *, struct t10_pr_registration *,
+			int *);
+char *uasp_parse_pr_out_transport_id(struct se_portal_group *,
+			const char *, u32 *, char **);
+struct se_node_acl *uasp_alloc_fabric_acl(struct se_portal_group *);
+void uasp_release_fabric_acl(struct se_portal_group *,
+			struct se_node_acl *);
+u32 uasp_tpg_get_inst_index(struct se_portal_group *);
+int uasp_new_cmd(struct se_cmd *se_cmd);
+int uasp_cmd_queue_supported(struct se_cmd *se_cmd);
+void uasp_release_cmd(struct se_cmd *);
+int uasp_shutdown_session(struct se_session *);
+void uasp_close_session(struct se_session *);
+void uasp_stop_session(struct se_session *, int, int);
+void uasp_reset_nexus(struct se_session *);
+int uasp_sess_logged_in(struct se_session *);
+u32 uasp_sess_get_index(struct se_session *);
+int uasp_write_pending(struct se_cmd *);
+int uasp_write_pending_status(struct se_cmd *);
+void uasp_set_default_node_attrs(struct se_node_acl *);
+u32 uasp_get_task_tag(struct se_cmd *);
+int uasp_get_cmd_state(struct se_cmd *);
+int uasp_queue_data_in(struct se_cmd *);
+int uasp_queue_status(struct se_cmd *);
+int uasp_queue_tm_rsp(struct se_cmd *);
+u16 uasp_set_fabric_sense_len(struct se_cmd *, u32);
+u16 uasp_get_fabric_sense_len(void);
+int uasp_is_state_remove(struct se_cmd *);
diff --git a/drivers/target/uasp/gadget.c b/drivers/target/uasp/gadget.c
new file mode 100644
index 0000000..5b7ccaf
--- /dev/null
+++ b/drivers/target/uasp/gadget.c
@@ -0,0 +1,770 @@
+
+#include <linux/kernel.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/storage.h>
+
+#include <target/target_core_base.h>
+
+#include "gadget.h"
+#include "gadget_ops.h"
+#include "configfs.h"
+
+#include "usbstring.c"
+#include "epautoconf.c"
+#include "config.c"
+#include "composite.c"
+
+#define UAS_G_STR_MANUFACTOR	1
+#define UAS_G_STR_PRODUCT	2
+#define UAS_G_STR_SERIAL	3
+#define UAS_G_STR_CONFIG	4
+#define UAS_G_STR_INTERFACE	5
+
+#define UASP_SS_EP_COMP_NUM_STREAMS 0
+
+struct f_uas {
+	struct usb_function     function;
+	u16			iface;
+	struct usb_ep		*ep_in;
+	struct usb_ep		*ep_out;
+	struct usb_ep		*ep_status;
+	struct usb_ep		*ep_cmd;
+
+	struct usb_request	*req_in;
+	struct usb_request	*req_out;
+	struct usb_request	*req_status;
+	struct usb_request	*req_cmd;
+
+	void			*cmd_buff;
+	u32			flags;
+#define UASP_ACTIVE		(1 << 0)
+#define UASP_SS_MODE		(1 << 1)
+};
+
+static struct usb_interface_descriptor uasp_intf_desc = {
+	.bLength =		sizeof(uasp_intf_desc),
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	4,
+	.bInterfaceClass =	USB_CLASS_MASS_STORAGE,
+	.bInterfaceSubClass =	USB_SC_SCSI,
+	.bInterfaceProtocol =	USB_PR_UAS,
+	.iInterface =		UAS_G_STR_INTERFACE,
+};
+
+static struct usb_endpoint_descriptor uasp_bi_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_pipe_usage_descriptor uasp_bi_pipe_desc = {
+	.bLength =		sizeof(uasp_bi_pipe_desc),
+	.bDescriptorType =	USB_DT_PIPE_USAGE,
+	.bPipeID =		PIPE_ID_DATA_IN,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_bi_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = {
+	.bLength =		sizeof(uasp_bi_ep_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+	.bMaxBurst =		0, /*
+				    * Doesn't support burst. Maybe update later?
+				    * Should it be HW dependent?
+				    */
+	.bmAttributes =		UASP_SS_EP_COMP_NUM_STREAMS,
+	.wBytesPerInterval =	0,
+};
+
+static struct usb_endpoint_descriptor uasp_bo_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_pipe_usage_descriptor uasp_bo_pipe_desc = {
+	.bLength =		sizeof(uasp_bo_pipe_desc),
+	.bDescriptorType =	USB_DT_PIPE_USAGE,
+	.bPipeID =		PIPE_ID_DATA_OUT,
+	.Reserved =		0,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_bo_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(0x400),
+};
+
+static struct usb_ss_ep_comp_descriptor uasp_bulk_out_ep_comp_desc = {
+	.bLength =		sizeof(uasp_bulk_out_ep_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+	.bmAttributes =		UASP_SS_EP_COMP_NUM_STREAMS,
+};
+
+static struct usb_endpoint_descriptor uasp_status_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_pipe_usage_descriptor uasp_status_pipe_desc = {
+	.bLength =		sizeof(uasp_status_pipe_desc),
+	.bDescriptorType =	USB_DT_PIPE_USAGE,
+	.bPipeID =		PIPE_ID_STS,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_status_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor uasp_status_in_ep_comp_desc = {
+	.bLength =		sizeof(uasp_status_in_ep_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+	.bmAttributes =		UASP_SS_EP_COMP_NUM_STREAMS,
+};
+
+static struct usb_endpoint_descriptor uasp_cmd_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_pipe_usage_descriptor uasp_cmd_pipe_desc = {
+	.bLength =		sizeof(uasp_cmd_pipe_desc),
+	.bDescriptorType =	USB_DT_PIPE_USAGE,
+	.bPipeID =		PIPE_ID_CMD,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_cmd_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor uasp_cmd_comp_desc = {
+	.bLength =		sizeof(uasp_cmd_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *uasp_hs_function_desc[] = {
+	(struct usb_descriptor_header *) &uasp_intf_desc,
+	(struct usb_descriptor_header *) &uasp_bi_desc,
+	(struct usb_descriptor_header *) &uasp_bi_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_bo_desc,
+	(struct usb_descriptor_header *) &uasp_bo_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_status_desc,
+	(struct usb_descriptor_header *) &uasp_status_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_cmd_desc,
+	(struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
+	NULL,
+};
+
+static struct usb_descriptor_header *uasp_ss_function_desc[] = {
+	(struct usb_descriptor_header *) &uasp_intf_desc,
+	(struct usb_descriptor_header *) &uasp_ss_bi_desc,
+	(struct usb_descriptor_header *) &uasp_bi_ep_comp_desc,
+	(struct usb_descriptor_header *) &uasp_bi_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_ss_bo_desc,
+	(struct usb_descriptor_header *) &uasp_bulk_out_ep_comp_desc,
+	(struct usb_descriptor_header *) &uasp_bo_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_ss_status_desc,
+	(struct usb_descriptor_header *) &uasp_status_in_ep_comp_desc,
+	(struct usb_descriptor_header *) &uasp_status_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_ss_cmd_desc,
+	(struct usb_descriptor_header *) &uasp_cmd_comp_desc,
+	(struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
+	NULL,
+};
+
+#define UAS_VENDOR_ID	0x0525	/* NetChip */
+#define UAS_PRODUCT_ID	0xa4a5	/* Linux-USB File-backed Storage Gadget */
+
+static struct usb_device_descriptor uas_device_desc = {
+	.bLength =              sizeof(uas_device_desc),
+	.bDescriptorType =      USB_DT_DEVICE,
+	.bcdUSB =               cpu_to_le16(0x0200),
+	.bDeviceClass =         USB_CLASS_PER_INTERFACE,
+	.idVendor =             cpu_to_le16(UAS_VENDOR_ID),
+	.idProduct =            cpu_to_le16(UAS_PRODUCT_ID),
+	.iManufacturer =        UAS_G_STR_MANUFACTOR,
+	.iProduct =             UAS_G_STR_PRODUCT,
+	.iSerialNumber =        UAS_G_STR_SERIAL,
+
+	.bNumConfigurations =   1,
+};
+
+static struct usb_string	uas_us_strings[] = {
+	{ UAS_G_STR_MANUFACTOR,	"UAS Manufactor"},
+	{ UAS_G_STR_PRODUCT,	"UAS Product"},
+	{ UAS_G_STR_SERIAL,	"UAS Serial"},
+	{ UAS_G_STR_CONFIG,	"UAS Config"},
+	{ UAS_G_STR_INTERFACE,	"UAS Interface"},
+	{ },
+};
+
+static struct usb_gadget_strings uas_stringtab = {
+	.language = 0x0409,
+	.strings = uas_us_strings,
+};
+
+static struct usb_gadget_strings *uas_strings[] = {
+	&uas_stringtab,
+	NULL,
+};
+
+static int guas_unbind(struct usb_composite_dev *cdev)
+{
+	return 0;
+}
+
+static struct usb_configuration uasp_config_driver = {
+	.label                  = "Linux UASP Storage",
+	.bConfigurationValue    = 1,
+	.iConfiguration		= UAS_G_STR_CONFIG,
+	.bmAttributes           = USB_CONFIG_ATT_SELFPOWER,
+};
+
+static struct f_uas *to_f_uas(struct usb_function *f)
+{
+	return container_of(f, struct f_uas, function);
+}
+
+static void give_back_ep(struct usb_ep **pep)
+{
+	struct usb_ep *ep = *pep;
+	if (!ep)
+		return;
+	ep->driver_data = NULL;
+}
+
+static int uasp_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_uas		*fu = to_f_uas(f);
+	struct usb_gadget	*gadget = c->cdev->gadget;
+	struct usb_ep		*ep;
+	int			iface;
+
+	iface = usb_interface_id(c, f);
+	if (iface < 0)
+		return iface;
+
+	uasp_intf_desc.bInterfaceNumber = iface;
+	fu->iface = iface;
+	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bi_desc,
+			&uasp_bi_ep_comp_desc);
+	if (!ep)
+		goto ep_fail;
+
+	ep->driver_data = fu;
+	fu->ep_in = ep;
+
+	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bo_desc,
+			&uasp_bulk_out_ep_comp_desc);
+	if (!ep)
+		goto ep_fail;
+	ep->driver_data = fu;
+	fu->ep_out = ep;
+
+	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_desc,
+			&uasp_status_in_ep_comp_desc);
+	if (!ep)
+		goto ep_fail;
+	ep->driver_data = fu;
+	fu->ep_status = ep;
+
+	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_cmd_desc,
+			&uasp_cmd_comp_desc);
+	if (!ep)
+		goto ep_fail;
+	ep->driver_data = fu;
+	fu->ep_cmd = ep;
+
+	/* Assume endpoint addresses are the same for both speeds */
+	uasp_bi_desc.bEndpointAddress =	uasp_ss_bi_desc.bEndpointAddress;
+	uasp_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress;
+	uasp_status_desc.bEndpointAddress =
+		uasp_ss_status_desc.bEndpointAddress;
+	uasp_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress;
+	f->ss_descriptors = uasp_ss_function_desc;
+	return 0;
+ep_fail:
+	pr_err("Can't claim all required eps\n");
+
+	give_back_ep(&fu->ep_in);
+	give_back_ep(&fu->ep_out);
+	give_back_ep(&fu->ep_status);
+	give_back_ep(&fu->ep_cmd);
+	return -ENOTSUPP;
+}
+
+static void uasp_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_uas *fu = to_f_uas(f);
+
+	kfree(fu);
+}
+
+static void uasp_cleanup_old_alt(struct f_uas *fu)
+{
+	if (!(fu->flags & UASP_ACTIVE))
+		return;
+	fu->flags = 0;
+
+	usb_ep_disable(fu->ep_in);
+	usb_ep_disable(fu->ep_out);
+	usb_ep_disable(fu->ep_status);
+	usb_ep_disable(fu->ep_cmd);
+
+	usb_ep_free_request(fu->ep_in, fu->req_in);
+	usb_ep_free_request(fu->ep_out, fu->req_out);
+	usb_ep_free_request(fu->ep_status, fu->req_status);
+	usb_ep_free_request(fu->ep_cmd, fu->req_cmd);
+
+	fu->req_in = NULL;
+	fu->req_out = NULL;
+	fu->req_status = NULL;
+	fu->req_cmd = NULL;
+
+	kfree(fu->cmd_buff);
+	fu->cmd_buff = NULL;
+}
+
+struct delayed_queue_item {
+	struct usb_ep *ep;
+	struct usb_request *req;
+	struct list_head lh;
+};
+
+void uasp_cmd_release(struct kref *ref);
+
+static void guas_cleanup_cmd(struct uasp_cmd *cmd)
+{
+	kref_put(&cmd->ref, uasp_cmd_release);
+}
+
+static void guas_status_data_cmpl(struct usb_ep *ep, struct usb_request *req);
+static int guas_prepare_r_request(struct uasp_cmd *cmd)
+{
+	struct f_uas *fu = cmd->fu;
+	struct se_cmd *se_cmd = &cmd->tvc_se_cmd;
+
+	if (1) {
+		/* XXX no SG support */
+		cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+		if (!cmd->data_buf)
+			return -ENOMEM;
+
+		sg_copy_to_buffer(se_cmd->t_data_sg,
+				se_cmd->t_data_nents,
+				cmd->data_buf,
+				se_cmd->data_length);
+
+		fu->req_in->buf = cmd->data_buf;
+	} else {
+		/* XXX setup sgl */
+	}
+
+	fu->req_in->complete = guas_status_data_cmpl;
+	fu->req_in->length = se_cmd->data_length;
+	fu->req_in->context = cmd;
+
+	cmd->state = UASP_SEND_STATUS;
+	return 0;
+}
+
+static void guas_data_write_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+	struct uasp_cmd *cmd = req->context;
+	struct se_cmd *se_cmd = &cmd->tvc_se_cmd;
+	struct f_uas *fu = cmd->fu;
+
+	if (req->status < 0) {
+		pr_err("%s() state %d transfer failed\n", __func__, cmd->state);
+		goto cleanup;
+	}
+
+	sg_copy_from_buffer(se_cmd->t_data_sg,
+			se_cmd->t_data_nents,
+			cmd->data_buf,
+			se_cmd->data_length);
+
+	complete(&cmd->write_complete);
+	return;
+
+	/* Sync with next command */
+cleanup:
+	guas_cleanup_cmd(cmd);
+	usb_ep_queue(fu->ep_cmd, fu->req_cmd, GFP_ATOMIC);
+}
+
+static int guas_prepare_w_request(struct uasp_cmd *cmd)
+{
+	struct f_uas *fu = cmd->fu;
+	struct se_cmd *se_cmd = &cmd->tvc_se_cmd;
+
+	if (1) {
+		/* XXX no SG support */
+		cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+		if (!cmd->data_buf)
+			return -ENOMEM;
+
+		fu->req_out->buf = cmd->data_buf;
+	} else {
+		/* XXX setup sgl */
+	}
+
+	fu->req_out->complete = guas_data_write_cmpl;
+	fu->req_out->length = se_cmd->data_length;
+	fu->req_out->context = cmd;
+	return 0;
+}
+
+static void guas_prepare_status(struct uasp_cmd *cmd)
+{
+	struct f_uas *fu = cmd->fu;
+	struct se_cmd *se_cmd = &cmd->tvc_se_cmd;
+	struct sense_iu *iu = &cmd->sense_iu;
+
+	cmd->state = UASP_QUEUE_COMMAND;
+	iu->iu_id = IU_ID_STATUS;
+	/* we recycle tag from last transfer */
+
+	/* iu->status_qual = cpu_to_be16(STATUS QUALIFIER SAM-4. Where are you?); */
+	iu->len = cpu_to_be16(se_cmd->scsi_sense_length);
+	iu->status = se_cmd->scsi_status;
+	fu->req_status->length = se_cmd->scsi_sense_length + 16;
+	fu->req_status->buf = iu;
+}
+
+static void guas_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+	struct uasp_cmd *cmd = req->context;
+	struct f_uas *fu = cmd->fu;
+	int ret;
+
+	if (req->status < 0) {
+		pr_err("%s() state %d transfer failed\n", __func__, cmd->state);
+		goto cleanup;
+	}
+
+	switch (cmd->state) {
+	case UASP_SEND_DATA:
+		ret = guas_prepare_r_request(cmd);
+		if (ret)
+			goto cleanup;
+		ret = usb_ep_queue(fu->ep_in, fu->req_in, GFP_ATOMIC);
+		break;
+
+	case UASP_RECEIVE_DATA:
+		ret = guas_prepare_w_request(cmd);
+		if (ret)
+			goto cleanup;
+		ret = usb_ep_queue(fu->ep_out, fu->req_out, GFP_ATOMIC);
+		break;
+
+	case UASP_SEND_STATUS:
+		guas_prepare_status(cmd);
+		ret = usb_ep_queue(fu->ep_status, fu->req_status, GFP_ATOMIC);
+		break;
+
+	case UASP_QUEUE_COMMAND:
+		guas_cleanup_cmd(cmd);
+		usb_ep_queue(fu->ep_cmd, fu->req_cmd, GFP_ATOMIC);
+		break;
+
+	default:
+		BUG();
+	};
+	return;
+
+	/* Sync with next command */
+cleanup:
+	pr_err("%s() cleanup\n", __func__);
+	guas_cleanup_cmd(cmd);
+	usb_ep_queue(fu->ep_cmd, fu->req_cmd, GFP_ATOMIC);
+}
+
+void guas_send_status_response(struct se_cmd *se_cmd)
+{
+	struct uasp_cmd *cmd = container_of(se_cmd, struct uasp_cmd,
+			tvc_se_cmd);
+	struct f_uas *fu = cmd->fu;
+	int ret;
+
+	fu->req_status->complete = guas_status_data_cmpl;
+	fu->req_status->context = cmd;
+	cmd->fu = fu;
+	guas_prepare_status(cmd);
+	ret = usb_ep_queue(fu->ep_status, fu->req_status, GFP_ATOMIC);
+}
+
+void guas_send_read_response(struct se_cmd *se_cmd)
+{
+	struct uasp_cmd *cmd = container_of(se_cmd, struct uasp_cmd,
+			tvc_se_cmd);
+	struct f_uas *fu = cmd->fu;
+	struct sense_iu *iu = &cmd->sense_iu;
+
+	fu->req_status->context = cmd;
+	cmd->fu = fu;
+
+	iu->tag = cpu_to_be16(cmd->tag);
+	/* On HS to send a READ request while on SS we can send the data */
+	if (1) {
+		int ret;
+
+		/* HS */
+		iu->iu_id = IU_ID_READ_READY;
+		fu->req_status->complete = guas_status_data_cmpl;
+
+		cmd->state = UASP_SEND_DATA;
+		fu->req_status->buf = iu;
+		fu->req_status->length = sizeof(struct read_write_iu);
+
+		ret = usb_ep_queue(fu->ep_status, fu->req_status, GFP_ATOMIC);
+	} else {
+		/* SS XXX */
+	}
+}
+
+void guas_send_write_request(struct se_cmd *se_cmd)
+{
+	struct uasp_cmd *cmd = container_of(se_cmd, struct uasp_cmd,
+			tvc_se_cmd);
+	struct f_uas *fu = cmd->fu;
+	struct sense_iu *iu = &cmd->sense_iu;
+
+	init_completion(&cmd->write_complete);
+	fu->req_status->context = cmd;
+	cmd->fu = fu;
+
+	iu->tag = cpu_to_be16(cmd->tag);
+	/* On HS to send a WRITE request while on SS we can read the data */
+
+	if (1) {
+		int ret;
+
+		iu->iu_id = IU_ID_WRITE_READY;
+		fu->req_status->complete = guas_status_data_cmpl;
+
+		cmd->state = UASP_RECEIVE_DATA;
+		fu->req_status->buf = iu;
+		fu->req_status->length = sizeof(struct read_write_iu);
+
+		ret = usb_ep_queue(fu->ep_status, fu->req_status, GFP_ATOMIC);
+	} else {
+		/* SS XXX */
+	}
+
+	wait_for_completion(&cmd->write_complete);
+	transport_generic_process_write(se_cmd);
+}
+
+static void guas_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_uas *fu = req->context;
+
+	if (req->status < 0) {
+		pr_err("%s() bad status\n", __func__);
+		return;
+	}
+
+	uasp_submit_command(fu, req->buf, req->actual);
+}
+
+static int uasp_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_uas *fu = to_f_uas(f);
+	struct usb_gadget *gadget = f->config->cdev->gadget;
+	int ret;
+
+	uasp_cleanup_old_alt(fu);
+
+	if (gadget->speed == USB_SPEED_SUPER)
+			fu->flags |= UASP_SS_MODE;
+
+	config_ep_by_speed(gadget, f, fu->ep_in);
+	ret = usb_ep_enable(fu->ep_in);
+	if (ret)
+		goto err_b_in;
+
+	config_ep_by_speed(gadget, f, fu->ep_out);
+	ret = usb_ep_enable(fu->ep_out);
+	if (ret)
+		goto err_b_out;
+
+	config_ep_by_speed(gadget, f, fu->ep_cmd);
+	ret = usb_ep_enable(fu->ep_cmd);
+	if (ret)
+		goto err_cmd;
+	config_ep_by_speed(gadget, f, fu->ep_status);
+	ret = usb_ep_enable(fu->ep_status);
+	if (ret)
+		goto err_status;
+
+	ret = -ENOMEM;
+	fu->req_in = usb_ep_alloc_request(fu->ep_in, GFP_ATOMIC);
+	if (!fu->req_in)
+		goto err_req_bi;
+
+	fu->req_out = usb_ep_alloc_request(fu->ep_out, GFP_ATOMIC);
+	if (!fu->req_out)
+		goto err_req_bo;
+
+	fu->req_status = usb_ep_alloc_request(fu->ep_status, GFP_ATOMIC);
+	if (!fu->req_status)
+		goto err_req_status;
+
+	fu->req_cmd = usb_ep_alloc_request(fu->ep_cmd, GFP_ATOMIC);
+	if (!fu->req_cmd)
+		goto err_req_cmd;
+
+	fu->cmd_buff = kmalloc(fu->ep_cmd->maxpacket, GFP_ATOMIC);
+	if (!fu->cmd_buff)
+		goto err_cmd_buf;
+
+	fu->req_cmd->complete = guas_cmd_complete;
+	fu->req_cmd->buf = fu->cmd_buff;
+	fu->req_cmd->length = fu->ep_cmd->maxpacket;
+	fu->req_cmd->context = fu;
+
+	/*
+	 * XXX On HS one should be enough but on SS we need to setup one req
+	 * for each stream....
+	 */
+	ret = usb_ep_queue(fu->ep_cmd, fu->req_cmd, GFP_ATOMIC);
+	if (ret)
+		goto err_enqueue;
+
+	fu->flags |= UASP_ACTIVE;
+	return 0;
+
+err_enqueue:
+	kfree(fu->cmd_buff);
+	fu->cmd_buff = NULL;
+err_cmd_buf:
+	usb_ep_free_request(fu->ep_cmd, fu->req_cmd);
+	fu->req_cmd = NULL;
+err_req_cmd:
+	usb_ep_free_request(fu->ep_status, fu->req_status);
+	fu->req_status = NULL;
+err_req_status:
+	usb_ep_free_request(fu->ep_out, fu->req_out);
+	fu->req_out = NULL;
+err_req_bo:
+	usb_ep_free_request(fu->ep_in, fu->req_in);
+	fu->req_in = NULL;
+err_req_bi:
+	usb_ep_disable(fu->ep_status);
+err_status:
+	usb_ep_disable(fu->ep_cmd);
+err_cmd:
+	usb_ep_disable(fu->ep_out);
+err_b_out:
+	usb_ep_disable(fu->ep_in);
+err_b_in:
+	fu->flags = 0;
+	return ret;
+}
+
+static void uasp_disable(struct usb_function *f)
+{
+	struct f_uas *fu = to_f_uas(f);
+
+	uasp_cleanup_old_alt(fu);
+}
+
+static int uas_cfg_bind(struct usb_configuration *c)
+{
+	struct f_uas *fu;
+	int ret;
+
+	fu = kzalloc(sizeof(*fu), GFP_KERNEL);
+	if (!fu)
+		return -ENOMEM;
+	fu->function.name = "UASP Function";
+	fu->function.descriptors = uasp_hs_function_desc;
+	fu->function.hs_descriptors = uasp_hs_function_desc;
+	fu->function.bind = uasp_bind;
+	fu->function.unbind = uasp_unbind;
+	fu->function.set_alt = uasp_set_alt;
+	fu->function.disable = uasp_disable;
+
+	ret = usb_add_function(c, &fu->function);
+	if (ret)
+		goto err;
+	return 0;
+err:
+	kfree(fu);
+	return ret;
+}
+
+static int guas_bind(struct usb_composite_dev *cdev)
+{
+	int ret;
+
+	ret = usb_add_config(cdev, &uasp_config_driver,
+			uas_cfg_bind);
+	return 0;
+}
+
+static struct usb_composite_driver uas_driver = {
+	.name           = "g_uas",
+	.dev            = &uas_device_desc,
+	.strings        = uas_strings,
+	.max_speed      = USB_SPEED_SUPER,
+	.unbind         = guas_unbind,
+};
+
+int gadget_attach(void)
+{
+	return usb_composite_probe(&uas_driver, guas_bind);
+}
+
+void gadget_detach(void)
+{
+	usb_composite_unregister(&uas_driver);
+}
+
+static int __init guas_init(void)
+{
+	int ret;
+
+	ret = uasp_register_configfs();
+	return ret;
+}
+module_init(guas_init);
+
+static void __exit guas_exit(void)
+{
+	uasp_deregister_configfs();
+}
+module_exit(guas_exit);
+
+MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("UAS faabric");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/target/uasp/gadget.h b/drivers/target/uasp/gadget.h
new file mode 100644
index 0000000..cb5965e
--- /dev/null
+++ b/drivers/target/uasp/gadget.h
@@ -0,0 +1,81 @@
+#ifndef __GTARGET_UAS_H__
+#define __GTARGET_UAS_H__
+
+#include <scsi/scsi.h>
+#include <target/target_core_fabric.h>
+
+#define IU_ID_COMMAND		0x01
+#define IU_ID_STATUS		0x03
+#define IU_ID_RESPONSE		0x04
+#define IU_ID_TASK_MGMT		0x05
+#define IU_ID_READ_READY	0x06
+#define IU_ID_WRITE_READY	0x07
+
+struct usb_pipe_usage_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u8  bPipeID;
+	/* Pipe ID defenitions: Table 9 from UAS spec*/
+#define PIPE_ID_CMD             0x01    /* Command pipe */
+#define PIPE_ID_STS             0x02    /* Status pipe */
+#define PIPE_ID_DATA_IN         0x03    /* Data-in piep */
+#define PIPE_ID_DATA_OUT        0x04    /* Data-out pipe */
+	__u8  Reserved;
+} __attribute__((__packed__));
+
+struct command_iu {
+	__u8 iu_id;
+	__u8 rsvd1;
+	__be16 tag;
+	__u8 prio_attr;
+	__u8 rsvd5;
+	__u8 len;
+	__u8 rsvd7;
+	struct scsi_lun lun;
+	__u8 cdb[16];
+} __packed;
+
+struct sense_iu {
+	__u8 iu_id;
+	__u8 rsvd1;
+	__be16 tag;
+	__be16 status_qual;
+	__u8 status;
+	__u8 rsvd7[7];
+	__be16 len;
+	__u8 sense[SCSI_SENSE_BUFFERSIZE];
+} __packed;
+
+struct read_write_iu {
+	__u8 iu_id;
+	__u8 rsvd1;
+	__be16 tag;
+} __packed;
+
+enum uasp_state {
+	UASP_SEND_DATA,
+	UASP_RECEIVE_DATA,
+	UASP_SEND_STATUS,
+	UASP_QUEUE_COMMAND,
+};
+
+#include <linux/kref.h>
+
+#define UASP_MAX_CMD    64
+struct uasp_cmd {
+	u8 cmd_buf[UASP_MAX_CMD];
+	u16 tag;
+	u16 data_dir;
+	u32 cmd_len;
+	atomic_t users;
+	struct se_cmd tvc_se_cmd;
+	struct sense_iu sense_iu;
+	void *data_buf;
+	enum uasp_state state;
+	struct f_uas *fu;
+	struct completion write_complete;
+	struct kref ref;
+};
+
+#endif
diff --git a/drivers/target/uasp/gadget_ops.h b/drivers/target/uasp/gadget_ops.h
new file mode 100644
index 0000000..a839004
--- /dev/null
+++ b/drivers/target/uasp/gadget_ops.h
@@ -0,0 +1,13 @@
+#ifndef __GADGET_OPS_H__
+#define __GADGET_OPS_H__
+
+struct f_uas;
+
+int gadget_attach(void);
+void gadget_detach(void);
+void uasp_submit_command(struct f_uas *fu, void *cmdbuf, unsigned int len);
+void guas_send_read_response(struct se_cmd *se_cmd);
+void guas_send_status_response(struct se_cmd *se_cmd);
+void guas_send_write_request(struct se_cmd *se_cmd);
+
+#endif
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 23a4473..99beba9 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -802,6 +802,9 @@ config USB_MASS_STORAGE
 	  Say "y" to link the driver statically, or "m" to build
 	  a dynamically linked module called "g_mass_storage".
 
+comment "You need to go to the target framework for UASP support"
+	depends on TARGET_UASP=n
+
 config USB_G_SERIAL
 	tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
 	help
-- 
1.7.7.3

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux