usb: gadget: f_tcm: convert to new function interface with backward compatibility
authorAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Fri, 11 Dec 2015 15:06:21 +0000 (16:06 +0100)
committerNicholas Bellinger <nab@linux-iscsi.org>
Mon, 21 Dec 2015 03:40:34 +0000 (19:40 -0800)
Converting tcm to the new function interface requires converting
USB tcm's function code and its users.

This patch converts the f_tcm.c to the new function interface.

The file can be now compiled into a separate module usb_f_tcm.ko.

The old function interface is provided by means of preprocessor conditional
directives. After all users are converted, the old interface can be
removed.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Acked-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
drivers/usb/gadget/Kconfig
drivers/usb/gadget/function/Makefile
drivers/usb/gadget/function/f_tcm.c
drivers/usb/gadget/function/tcm.h
drivers/usb/gadget/function/u_tcm.h [new file with mode: 0644]
drivers/usb/gadget/legacy/tcm_usb_gadget.c

index 33834aa09ed43be03b0dd0ad418febf0ca25432d..5bf50db692cd6b7476c20062a81cdf8854e8b71c 100644 (file)
@@ -199,6 +199,9 @@ config USB_F_HID
 config USB_F_PRINTER
        tristate
 
+config USB_F_TCM
+       tristate
+
 choice
        tristate "USB Gadget Drivers"
        default USB_ETH
index bd7def576955d825bf1651508fd715c964bd33c2..cb8c225e854925ec3d7b66c621f359539b350e46 100644 (file)
@@ -44,3 +44,5 @@ usb_f_hid-y                   := f_hid.o
 obj-$(CONFIG_USB_F_HID)                += usb_f_hid.o
 usb_f_printer-y                        := f_printer.o
 obj-$(CONFIG_USB_F_PRINTER)    += usb_f_printer.o
+usb_f_tcm-y                    := f_tcm.o
+obj-$(CONFIG_USB_F_TCM)                += usb_f_tcm.o
index ce246bc2ed079f330a1857d5895e0a41854bcfca..3b1ba893ecfc20655959f6905f747479ea9cf673 100644 (file)
 #include <asm/unaligned.h>
 
 #include "tcm.h"
+#include "u_tcm.h"
+
+#ifndef USBF_TCM_INCLUDED
+
+#define TPG_INSTANCES          1
+
+struct tpg_instance {
+       struct usb_function_instance    *func_inst;
+       struct usbg_tpg                 *tpg;
+};
+
+static struct tpg_instance tpg_instances[TPG_INSTANCES];
+
+static DEFINE_MUTEX(tpg_instances_lock);
+#endif
 
 static inline struct f_uas *to_f_uas(struct usb_function *f)
 {
@@ -1371,6 +1386,10 @@ static struct se_portal_group *usbg_make_tpg(
        struct usbg_tpg *tpg;
        unsigned long tpgt;
        int ret;
+#ifndef USBF_TCM_INCLUDED
+       struct f_tcm_opts *opts;
+       unsigned i;
+#endif
 
        if (strstr(name, "tpgt_") != name)
                return ERR_PTR(-EINVAL);
@@ -1381,14 +1400,40 @@ static struct se_portal_group *usbg_make_tpg(
                pr_err("gadgets, you can't do this here.\n");
                return ERR_PTR(-EBUSY);
        }
+#ifndef USBF_TCM_INCLUDED
+       ret = -ENODEV;
+       mutex_lock(&tpg_instances_lock);
+       for (i = 0; i < TPG_INSTANCES; ++i)
+               if (tpg_instances[i].func_inst && !tpg_instances[i].tpg)
+                       break;
+       if (i == TPG_INSTANCES)
+               goto unlock_inst;
+
+       opts = container_of(tpg_instances[i].func_inst, struct f_tcm_opts,
+               func_inst);
+       mutex_lock(&opts->dep_lock);
+       if (!opts->ready)
+               goto unlock_dep;
+
+       if (opts->has_dep && !try_module_get(opts->dependent))
+               goto unlock_dep;
+#endif
 
        tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL);
+       ret = -ENOMEM;
        if (!tpg)
+#ifdef USBF_TCM_INCLUDED
                return ERR_PTR(-ENOMEM);
+#else
+               goto unref_dep;
+#endif
        mutex_init(&tpg->tpg_mutex);
        atomic_set(&tpg->tpg_port_count, 0);
        tpg->workqueue = alloc_workqueue("tcm_usb_gadget", 0, 1);
        if (!tpg->workqueue) {
+#ifndef USBF_TCM_INCLUDED
+               goto free_tpg;
+#endif
                kfree(tpg);
                return NULL;
        }
@@ -1402,12 +1447,35 @@ static struct se_portal_group *usbg_make_tpg(
         */
        ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_SAS);
        if (ret < 0) {
+#ifndef USBF_TCM_INCLUDED
+               goto free_workqueue;
+#endif
                destroy_workqueue(tpg->workqueue);
                kfree(tpg);
                return NULL;
        }
+#ifndef USBF_TCM_INCLUDED
+       tpg_instances[i].tpg = tpg;
+       tpg->fi = tpg_instances[i].func_inst;
+       mutex_unlock(&opts->dep_lock);
+       mutex_unlock(&tpg_instances_lock);
+#endif
        the_only_tpg_I_currently_have = tpg;
        return &tpg->se_tpg;
+#ifndef USBF_TCM_INCLUDED
+free_workqueue:
+       destroy_workqueue(tpg->workqueue);
+free_tpg:
+       kfree(tpg);
+unref_dep:
+       module_put(opts->dependent);
+unlock_dep:
+       mutex_unlock(&opts->dep_lock);
+unlock_inst:
+       mutex_unlock(&tpg_instances_lock);
+
+       return ERR_PTR(ret);
+#endif
 }
 
 static int tcm_usbg_drop_nexus(struct usbg_tpg *);
@@ -1416,10 +1484,29 @@ static void usbg_drop_tpg(struct se_portal_group *se_tpg)
 {
        struct usbg_tpg *tpg = container_of(se_tpg,
                                struct usbg_tpg, se_tpg);
+#ifndef USBF_TCM_INCLUDED
+       unsigned i;
+       struct f_tcm_opts *opts;
+#endif
 
        tcm_usbg_drop_nexus(tpg);
        core_tpg_deregister(se_tpg);
        destroy_workqueue(tpg->workqueue);
+
+#ifndef USBF_TCM_INCLUDED
+       mutex_lock(&tpg_instances_lock);
+       for (i = 0; i < TPG_INSTANCES; ++i)
+               if (tpg_instances[i].tpg == tpg)
+                       break;
+       if (i < TPG_INSTANCES)
+               tpg_instances[i].tpg = NULL;
+       opts = container_of(tpg_instances[i].func_inst,
+               struct f_tcm_opts, func_inst);
+       mutex_lock(&opts->dep_lock);
+       module_put(opts->dependent);
+       mutex_unlock(&opts->dep_lock);
+       mutex_unlock(&tpg_instances_lock);
+#endif
        kfree(tpg);
        the_only_tpg_I_currently_have = NULL;
 }
@@ -1440,6 +1527,7 @@ static struct se_wwn *usbg_make_tport(
        tport = kzalloc(sizeof(struct usbg_tport), GFP_KERNEL);
        if (!(tport))
                return ERR_PTR(-ENOMEM);
+
        tport->tport_wwpn = wwpn;
        snprintf(tport->tport_name, sizeof(tport->tport_name), "%s", wnn_name);
        return &tport->tport_wwn;
@@ -1978,9 +2066,32 @@ static int tcm_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;
+#ifndef USBF_TCM_INCLUDED
+       struct f_tcm_opts       *opts;
+#endif
        int                     iface;
        int                     ret;
 
+#ifndef USBF_TCM_INCLUDED
+       opts = container_of(f->fi, struct f_tcm_opts, func_inst);
+
+       mutex_lock(&opts->dep_lock);
+       if (!opts->can_attach) {
+               mutex_unlock(&opts->dep_lock);
+               return -ENODEV;
+       }
+       mutex_unlock(&opts->dep_lock);
+#endif
+       if (tcm_us_strings[0].id == 0) {
+               ret = usb_string_ids_tab(c->cdev, tcm_us_strings);
+               if (ret < 0)
+                       return ret;
+
+               bot_intf_desc.iInterface = tcm_us_strings[USB_G_STR_INT_BBB].id;
+               uasp_intf_desc.iInterface =
+                       tcm_us_strings[USB_G_STR_INT_UAS].id;
+       }
+
        iface = usb_interface_id(c, f);
        if (iface < 0)
                return iface;
@@ -2038,7 +2149,9 @@ ep_fail:
        return -ENOTSUPP;
 }
 
-static void tcm_unbind(struct usb_configuration *c, struct usb_function *f)
+#ifdef USBF_TCM_INCLUDED
+
+static void tcm_old_unbind(struct usb_configuration *c, struct usb_function *f)
 {
        struct f_uas *fu = to_f_uas(f);
 
@@ -2046,6 +2159,8 @@ static void tcm_unbind(struct usb_configuration *c, struct usb_function *f)
        kfree(fu);
 }
 
+#endif
+
 struct guas_setup_wq {
        struct work_struct work;
        struct f_uas *fu;
@@ -2114,6 +2229,8 @@ static int tcm_setup(struct usb_function *f,
        return usbg_bot_setup(f, ctrl);
 }
 
+#ifdef USBF_TCM_INCLUDED
+
 static int tcm_bind_config(struct usb_configuration *c)
 {
        struct f_uas *fu;
@@ -2124,16 +2241,13 @@ static int tcm_bind_config(struct usb_configuration *c)
                return -ENOMEM;
        fu->function.name = "Target Function";
        fu->function.bind = tcm_bind;
-       fu->function.unbind = tcm_unbind;
+       fu->function.unbind = tcm_old_unbind;
        fu->function.set_alt = tcm_set_alt;
        fu->function.setup = tcm_setup;
        fu->function.disable = tcm_disable;
        fu->function.strings = tcm_strings;
        fu->tpg = the_only_tpg_I_currently_have;
 
-       bot_intf_desc.iInterface = tcm_us_strings[USB_G_STR_INT_BBB].id;
-       uasp_intf_desc.iInterface = tcm_us_strings[USB_G_STR_INT_UAS].id;
-
        ret = usb_add_function(c, &fu->function);
        if (ret)
                goto err;
@@ -2143,3 +2257,165 @@ err:
        kfree(fu);
        return ret;
 }
+
+#else
+
+static void tcm_free_inst(struct usb_function_instance *f)
+{
+       struct f_tcm_opts *opts;
+       unsigned i;
+
+       opts = container_of(f, struct f_tcm_opts, func_inst);
+
+       mutex_lock(&tpg_instances_lock);
+       for (i = 0; i < TPG_INSTANCES; ++i)
+               if (tpg_instances[i].func_inst == f)
+                       break;
+       if (i < TPG_INSTANCES)
+               tpg_instances[i].func_inst = NULL;
+       mutex_unlock(&tpg_instances_lock);
+
+       kfree(opts);
+}
+
+static int usbg_attach(struct usbg_tpg *tpg)
+{
+       struct usb_function_instance *f = tpg->fi;
+       struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
+
+       if (opts->tcm_register_callback)
+               return opts->tcm_register_callback(f);
+
+       return 0;
+}
+
+static void usbg_detach(struct usbg_tpg *tpg)
+{
+       struct usb_function_instance *f = tpg->fi;
+       struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
+
+       if (opts->tcm_unregister_callback)
+               opts->tcm_unregister_callback(f);
+}
+
+static int tcm_set_name(struct usb_function_instance *f, const char *name)
+{
+       struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
+
+       pr_debug("tcm: Activating %s\n", name);
+
+       mutex_lock(&opts->dep_lock);
+       opts->ready = true;
+       mutex_unlock(&opts->dep_lock);
+
+       return 0;
+}
+
+static struct usb_function_instance *tcm_alloc_inst(void)
+{
+       struct f_tcm_opts *opts;
+       int i;
+
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+
+       mutex_lock(&tpg_instances_lock);
+       for (i = 0; i < TPG_INSTANCES; ++i)
+               if (!tpg_instances[i].func_inst)
+                       break;
+
+       if (i == TPG_INSTANCES) {
+               mutex_unlock(&tpg_instances_lock);
+               kfree(opts);
+               return ERR_PTR(-EBUSY);
+       }
+       tpg_instances[i].func_inst = &opts->func_inst;
+       mutex_unlock(&tpg_instances_lock);
+
+       mutex_init(&opts->dep_lock);
+       opts->func_inst.set_inst_name = tcm_set_name;
+       opts->func_inst.free_func_inst = tcm_free_inst;
+
+       return &opts->func_inst;
+}
+
+static void tcm_free(struct usb_function *f)
+{
+       struct f_uas *tcm = to_f_uas(f);
+
+       kfree(tcm);
+}
+
+static void tcm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       usb_free_all_descriptors(f);
+}
+
+static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
+{
+       struct f_uas *fu;
+       struct f_tcm_opts *opts;
+       unsigned i;
+
+       mutex_lock(&tpg_instances_lock);
+       for (i = 0; i < TPG_INSTANCES; ++i)
+               if (tpg_instances[i].func_inst == fi)
+                       break;
+       if (i == TPG_INSTANCES) {
+               mutex_unlock(&tpg_instances_lock);
+               return ERR_PTR(-ENODEV);
+       }
+
+       opts = container_of(fi, struct f_tcm_opts, func_inst);
+
+       fu = kzalloc(sizeof(*fu), GFP_KERNEL);
+       if (!fu) {
+               mutex_unlock(&tpg_instances_lock);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       fu->function.name = "Target Function";
+       fu->function.bind = tcm_bind;
+       fu->function.unbind = tcm_unbind;
+       fu->function.set_alt = tcm_set_alt;
+       fu->function.setup = tcm_setup;
+       fu->function.disable = tcm_disable;
+       fu->function.strings = tcm_strings;
+       fu->function.free_func = tcm_free;
+       fu->tpg = tpg_instances[i].tpg;
+       mutex_unlock(&tpg_instances_lock);
+
+       return &fu->function;
+}
+
+DECLARE_USB_FUNCTION(tcm, tcm_alloc_inst, tcm_alloc);
+
+static int tcm_init(void)
+{
+       int ret;
+
+       ret = usb_function_register(&tcmusb_func);
+       if (ret)
+               return ret;
+
+       ret = target_register_template(&usbg_ops);
+       if (ret)
+               usb_function_unregister(&tcmusb_func);
+
+       return ret;
+}
+module_init(tcm_init);
+
+static void tcm_exit(void)
+{
+       target_unregister_template(&usbg_ops);
+       usb_function_unregister(&tcmusb_func);
+}
+module_exit(tcm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sebastian Andrzej Siewior");
+
+#endif
index c2c3a0fd4a1358bddb0b9125e48322920e9feb02..0b8ff6dc63ee2b251851fc19ced2529ca6f8fe76 100644 (file)
@@ -39,6 +39,8 @@ struct usbg_tpg {
        u32 gadget_connect;
        struct tcm_usbg_nexus *tpg_nexus;
        atomic_t tpg_port_count;
+
+       struct usb_function_instance *fi;
 };
 
 struct usbg_tport {
diff --git a/drivers/usb/gadget/function/u_tcm.h b/drivers/usb/gadget/function/u_tcm.h
new file mode 100644 (file)
index 0000000..0bd751e
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * u_tcm.h
+ *
+ * Utility definitions for the tcm function
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef U_TCM_H
+#define U_TCM_H
+
+#include <linux/usb/composite.h>
+
+/**
+ * @dependent: optional dependent module. Meant for legacy gadget.
+ * If non-null its refcount will be increased when a tpg is created and
+ * decreased when tpg is dropped.
+ * @dep_lock: lock for dependent module operations.
+ * @ready: true if the dependent module information is set.
+ * @can_attach: true a function can be bound to gadget
+ * @has_dep: true if there is a dependent module
+ *
+ */
+struct f_tcm_opts {
+       struct usb_function_instance    func_inst;
+       struct module                   *dependent;
+       struct mutex                    dep_lock;
+       bool                            ready;
+       bool                            can_attach;
+       bool                            has_dep;
+
+       /*
+        * Callbacks to be removed when legacy tcm gadget disappears.
+        *
+        * If you use the new function registration interface
+        * programmatically, you MUST set these callbacks to
+        * something sensible (e.g. probe/remove the composite).
+        */
+       int (*tcm_register_callback)(struct usb_function_instance *);
+       void (*tcm_unregister_callback)(struct usb_function_instance *);
+};
+
+#endif /* U_TCM_H */
index e6c601ab212df5422492cbc2aa02d31ed62425ab..f042df490ee32d4cbd52f72fef04c8f026c4e393 100644 (file)
@@ -24,6 +24,7 @@
 USB_GADGET_COMPOSITE_OPTIONS();
 
 /* #include to be removed when new function registration interface is used  */
+#define USBF_TCM_INCLUDED
 #include "../function/f_tcm.c"
 
 #define UAS_VENDOR_ID  0x0525  /* NetChip */