usb: gadget: f_sourcesink: add configfs support
authorAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Thu, 7 Nov 2013 07:41:28 +0000 (08:41 +0100)
committerFelipe Balbi <balbi@ti.com>
Tue, 26 Nov 2013 19:47:41 +0000 (13:47 -0600)
Add support for using the sourcesink function in gadgets composed with
configfs.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Documentation/ABI/testing/configfs-usb-gadget-sourcesink [new file with mode: 0644]
drivers/usb/gadget/Kconfig
drivers/usb/gadget/f_sourcesink.c
drivers/usb/gadget/g_zero.h
drivers/usb/gadget/zero.c

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-sourcesink b/Documentation/ABI/testing/configfs-usb-gadget-sourcesink
new file mode 100644 (file)
index 0000000..a30f309
--- /dev/null
@@ -0,0 +1,12 @@
+What:          /config/usb-gadget/gadget/functions/SourceSink.name
+Date:          Nov 2013
+KenelVersion:  3.13
+Description:
+               The attributes:
+
+               pattern         - 0 (all zeros), 1 (mod63), 2 (none)
+               isoc_interval   - 1..16
+               isoc_maxpacket  - 0 - 1023 (fs), 0 - 1024 (hs/ss)
+               isoc_mult       - 0..2 (hs/ss only)
+               isoc_maxburst   - 0..15 (ss only)
+               qlen            - buffer length
index 26fe8769be458fa929518d0f62aef7c38fa25cd9..5f1d4443aa0cf09f6cf029f5fd88ddc2e0252ce6 100644 (file)
@@ -689,12 +689,13 @@ config USB_CONFIGFS_MASS_STORAGE
          device (in much the same way as the "loop" device driver),
          specified as a module parameter or sysfs option.
 
-config USB_CONFIGFS_F_LB
-       boolean "Loopback function (for testing)"
+config USB_CONFIGFS_F_LB_SS
+       boolean "Loopback and sourcesink function (for testing)"
        depends on USB_CONFIGFS
        select USB_F_SS_LB
        help
-         It loops back a configurable number of transfers.
+         Loopback function loops back a configurable number of transfers.
+         Sourcesink function either sinks and sources bulk data.
          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
index c5ad4a1fa3c70c3b0a9bd578def7cd3e3734a33b..5d4251e7a377fd851d03ce5eb26d475e1b566a1e 100644 (file)
@@ -477,6 +477,14 @@ no_iso:
 static void
 sourcesink_free_func(struct usb_function *f)
 {
+       struct f_ss_opts *opts;
+
+       opts = container_of(f->fi, struct f_ss_opts, func_inst);
+
+       mutex_lock(&opts->lock);
+       opts->refcnt--;
+       mutex_unlock(&opts->lock);
+
        usb_free_all_descriptors(f);
        kfree(func_to_ss(f));
 }
@@ -865,6 +873,11 @@ static struct usb_function *source_sink_alloc_func(
                return NULL;
 
        ss_opts =  container_of(fi, struct f_ss_opts, func_inst);
+
+       mutex_lock(&ss_opts->lock);
+       ss_opts->refcnt++;
+       mutex_unlock(&ss_opts->lock);
+
        pattern = ss_opts->pattern;
        isoc_interval = ss_opts->isoc_interval;
        isoc_maxpacket = ss_opts->isoc_maxpacket;
@@ -885,6 +898,303 @@ static struct usb_function *source_sink_alloc_func(
        return &ss->function;
 }
 
+static inline struct f_ss_opts *to_f_ss_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_ss_opts,
+                           func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_ss_opts);
+CONFIGFS_ATTR_OPS(f_ss_opts);
+
+static void ss_attr_release(struct config_item *item)
+{
+       struct f_ss_opts *ss_opts = to_f_ss_opts(item);
+
+       usb_put_function_instance(&ss_opts->func_inst);
+}
+
+static struct configfs_item_operations ss_item_ops = {
+       .release                = ss_attr_release,
+       .show_attribute         = f_ss_opts_attr_show,
+       .store_attribute        = f_ss_opts_attr_store,
+};
+
+static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->pattern);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_pattern_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u8 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou8(page, 0, &num);
+       if (ret)
+               goto end;
+
+       if (num != 0 && num != 1 && num != 2) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       opts->pattern = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_pattern =
+       __CONFIGFS_ATTR(pattern, S_IRUGO | S_IWUSR,
+                       f_ss_opts_pattern_show,
+                       f_ss_opts_pattern_store);
+
+static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->isoc_interval);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_isoc_interval_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u8 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou8(page, 0, &num);
+       if (ret)
+               goto end;
+
+       if (num > 16) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       opts->isoc_interval = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_interval =
+       __CONFIGFS_ATTR(isoc_interval, S_IRUGO | S_IWUSR,
+                       f_ss_opts_isoc_interval_show,
+                       f_ss_opts_isoc_interval_store);
+
+static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->isoc_maxpacket);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_isoc_maxpacket_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u16 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou16(page, 0, &num);
+       if (ret)
+               goto end;
+
+       if (num > 1024) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       opts->isoc_maxpacket = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_maxpacket =
+       __CONFIGFS_ATTR(isoc_maxpacket, S_IRUGO | S_IWUSR,
+                       f_ss_opts_isoc_maxpacket_show,
+                       f_ss_opts_isoc_maxpacket_store);
+
+static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->isoc_mult);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_isoc_mult_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u8 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou8(page, 0, &num);
+       if (ret)
+               goto end;
+
+       if (num > 2) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       opts->isoc_mult = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_mult =
+       __CONFIGFS_ATTR(isoc_mult, S_IRUGO | S_IWUSR,
+                       f_ss_opts_isoc_mult_show,
+                       f_ss_opts_isoc_mult_store);
+
+static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->isoc_maxburst);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_isoc_maxburst_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u8 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou8(page, 0, &num);
+       if (ret)
+               goto end;
+
+       if (num > 15) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       opts->isoc_maxburst = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_maxburst =
+       __CONFIGFS_ATTR(isoc_maxburst, S_IRUGO | S_IWUSR,
+                       f_ss_opts_isoc_maxburst_show,
+                       f_ss_opts_isoc_maxburst_store);
+
+static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_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_ss_opts_bulk_buflen_store(struct f_ss_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_ss_opts_attribute f_ss_opts_bulk_buflen =
+       __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR,
+                       f_ss_opts_bulk_buflen_show,
+                       f_ss_opts_bulk_buflen_store);
+
+static struct configfs_attribute *ss_attrs[] = {
+       &f_ss_opts_pattern.attr,
+       &f_ss_opts_isoc_interval.attr,
+       &f_ss_opts_isoc_maxpacket.attr,
+       &f_ss_opts_isoc_mult.attr,
+       &f_ss_opts_isoc_maxburst.attr,
+       &f_ss_opts_bulk_buflen.attr,
+       NULL,
+};
+
+static struct config_item_type ss_func_type = {
+       .ct_item_ops    = &ss_item_ops,
+       .ct_attrs       = ss_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
 static void source_sink_free_instance(struct usb_function_instance *fi)
 {
        struct f_ss_opts *ss_opts;
@@ -900,7 +1210,15 @@ static struct usb_function_instance *source_sink_alloc_inst(void)
        ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL);
        if (!ss_opts)
                return ERR_PTR(-ENOMEM);
+       mutex_init(&ss_opts->lock);
        ss_opts->func_inst.free_func_inst = source_sink_free_instance;
+       ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
+       ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
+       ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
+
+       config_group_init_type_name(&ss_opts->func_inst.group, "",
+                                   &ss_func_type);
+
        return &ss_opts->func_inst;
 }
 DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst,
index 19ec50a42a74c65f247df28ef30e2a357aa2b93f..15f180904f8a3980e2845c38bf327e5174b8df30 100644 (file)
@@ -8,6 +8,8 @@
 
 #define GZERO_BULK_BUFLEN      4096
 #define GZERO_QLEN             32
+#define GZERO_ISOC_INTERVAL    4
+#define GZERO_ISOC_MAXPACKET   1024
 
 struct usb_zero_options {
        unsigned pattern;
@@ -27,6 +29,15 @@ struct f_ss_opts {
        unsigned isoc_mult;
        unsigned isoc_maxburst;
        unsigned bulk_buflen;
+
+       /*
+        * 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;
 };
 
 struct f_lb_opts {
index d954bba7b40504405fd858041b23a37275e2bf8a..00b401906a326cf87c152d8d2be23f7a698c1c4f 100644 (file)
@@ -64,8 +64,8 @@ static bool loopdefault = 0;
 module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
 
 static struct usb_zero_options gzero_options = {
-       .isoc_interval = 4,
-       .isoc_maxpacket = 1024,
+       .isoc_interval = GZERO_ISOC_INTERVAL,
+       .isoc_maxpacket = GZERO_ISOC_MAXPACKET,
        .bulk_buflen = GZERO_BULK_BUFLEN,
        .qlen = GZERO_QLEN,
 };