Add support for using the loopback USB function in gadgets composed with
configfs.

Signed-off-by: Andrzej Pietrasiewicz <andrze...@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 .../ABI/testing/configfs-usb-gadget-loopback       |    8 ++
 drivers/usb/gadget/Kconfig                         |   12 ++
 drivers/usb/gadget/f_loopback.c                    |  132 ++++++++++++++++++++
 drivers/usb/gadget/g_zero.h                        |   12 ++
 drivers/usb/gadget/zero.c                          |    4 +-
 5 files changed, 166 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-loopback

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-loopback 
b/Documentation/ABI/testing/configfs-usb-gadget-loopback
new file mode 100644
index 0000000..852b236
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-loopback
@@ -0,0 +1,8 @@
+What:          /config/usb-gadget/gadget/functions/Loopback.name
+Date:          Nov 2013
+KenelVersion:  3.13
+Description:
+               The attributes:
+
+               qlen            - depth of loopback queue
+               bulk_buflen     - buffer length
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 8da2b1d..d6e00af 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -701,6 +701,18 @@ config USB_CONFIGFS_F_FS
          implemented in kernel space (for instance Ethernet, serial or
          mass storage) and other are implemented in user space.
 
+config USB_CONFIGFS_F_LB
+       boolean "Loopback function (for testing)"
+       depends on USB_CONFIGFS
+       select USB_F_SS_LB
+       help
+         It loops back a configurable number of transfers.
+         It also implements control requests, for "chapter 9" conformance.
+         Make this be the first driver you try using on top of any new
+         USB peripheral controller driver.  Then you can use host-side
+         test software, like the "usbtest" driver, to put your hardware
+         and its driver through a basic set of functional tests.
+
 config USB_ZERO
        tristate "Gadget Zero (DEVELOPMENT)"
        select USB_LIBCOMPOSITE
diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
index b790653..c35bb40 100644
--- a/drivers/usb/gadget/f_loopback.c
+++ b/drivers/usb/gadget/f_loopback.c
@@ -231,6 +231,14 @@ autoconf_fail:
 
 static void lb_free_func(struct usb_function *f)
 {
+       struct f_lb_opts *opts;
+
+       opts = container_of(f->fi, struct f_lb_opts, func_inst);
+
+       mutex_lock(&opts->lock);
+       opts->refcnt--;
+       mutex_unlock(&opts->lock);
+
        usb_free_all_descriptors(f);
        kfree(func_to_loop(f));
 }
@@ -386,6 +394,11 @@ static struct usb_function *loopback_alloc(struct 
usb_function_instance *fi)
                return ERR_PTR(-ENOMEM);
 
        lb_opts = container_of(fi, struct f_lb_opts, func_inst);
+
+       mutex_lock(&lb_opts->lock);
+       lb_opts->refcnt++;
+       mutex_unlock(&lb_opts->lock);
+
        buflen = lb_opts->bulk_buflen;
        qlen = lb_opts->qlen;
        if (!qlen)
@@ -402,6 +415,118 @@ static struct usb_function *loopback_alloc(struct 
usb_function_instance *fi)
        return &loop->function;
 }
 
+static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_lb_opts,
+                           func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_lb_opts);
+CONFIGFS_ATTR_OPS(f_lb_opts);
+
+static void lb_attr_release(struct config_item *item)
+{
+       struct f_lb_opts *lb_opts = to_f_lb_opts(item);
+
+       usb_put_function_instance(&lb_opts->func_inst);
+}
+
+static struct configfs_item_operations lb_item_ops = {
+       .release                = lb_attr_release,
+       .show_attribute         = f_lb_opts_attr_show,
+       .store_attribute        = f_lb_opts_attr_store,
+};
+
+static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->qlen);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_lb_opts_qlen_store(struct f_lb_opts *opts,
+                                   const char *page, size_t len)
+{
+       int ret;
+       u32 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou32(page, 0, &num);
+       if (ret)
+               goto end;
+
+       opts->qlen = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_lb_opts_attribute f_lb_opts_qlen =
+       __CONFIGFS_ATTR(qlen, S_IRUGO | S_IWUSR,
+                       f_lb_opts_qlen_show,
+                       f_lb_opts_qlen_store);
+
+static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->bulk_buflen);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_lb_opts_bulk_buflen_store(struct f_lb_opts *opts,
+                                   const char *page, size_t len)
+{
+       int ret;
+       u32 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou32(page, 0, &num);
+       if (ret)
+               goto end;
+
+       opts->bulk_buflen = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_lb_opts_attribute f_lb_opts_bulk_buflen =
+       __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR,
+                       f_lb_opts_bulk_buflen_show,
+                       f_lb_opts_bulk_buflen_store);
+
+static struct configfs_attribute *lb_attrs[] = {
+       &f_lb_opts_qlen.attr,
+       &f_lb_opts_bulk_buflen.attr,
+       NULL,
+};
+
+static struct config_item_type lb_func_type = {
+       .ct_item_ops    = &lb_item_ops,
+       .ct_attrs       = lb_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
 static void lb_free_instance(struct usb_function_instance *fi)
 {
        struct f_lb_opts *lb_opts;
@@ -417,7 +542,14 @@ static struct usb_function_instance 
*loopback_alloc_instance(void)
        lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
        if (!lb_opts)
                return ERR_PTR(-ENOMEM);
+       mutex_init(&lb_opts->lock);
        lb_opts->func_inst.free_func_inst = lb_free_instance;
+       lb_opts->bulk_buflen = GZERO_BULK_BUFLEN;
+       lb_opts->qlen = GZERO_QLEN;
+
+       config_group_init_type_name(&lb_opts->func_inst.group, "",
+                                   &lb_func_type);
+
        return  &lb_opts->func_inst;
 }
 DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h
index a1c1a97..19ec50a 100644
--- a/drivers/usb/gadget/g_zero.h
+++ b/drivers/usb/gadget/g_zero.h
@@ -6,6 +6,9 @@
 #ifndef __G_ZERO_H
 #define __G_ZERO_H
 
+#define GZERO_BULK_BUFLEN      4096
+#define GZERO_QLEN             32
+
 struct usb_zero_options {
        unsigned pattern;
        unsigned isoc_interval;
@@ -30,6 +33,15 @@ struct f_lb_opts {
        struct usb_function_instance func_inst;
        unsigned bulk_buflen;
        unsigned qlen;
+
+       /*
+        * Read/write access to configfs attributes is handled by configfs.
+        *
+        * This is to protect the data from concurrent access by read/write
+        * and create symlink/remove symlink.
+        */
+       struct mutex                    lock;
+       int                             refcnt;
 };
 
 void lb_modexit(void);
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 0dd07ae..d954bba 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -66,8 +66,8 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
 static struct usb_zero_options gzero_options = {
        .isoc_interval = 4,
        .isoc_maxpacket = 1024,
-       .bulk_buflen = 4096,
-       .qlen = 32,
+       .bulk_buflen = GZERO_BULK_BUFLEN,
+       .qlen = GZERO_QLEN,
 };
 
 /*-------------------------------------------------------------------------*/
-- 
1.7.0.4

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

Reply via email to