Skip to content

Commit

Permalink
usb: gadget: f_sourcesink: add configfs support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Andrzej Pietrasiewicz authored and Felipe Balbi committed Nov 26, 2013
1 parent c0501f4 commit 25d8015
Show file tree
Hide file tree
Showing 5 changed files with 347 additions and 5 deletions.
12 changes: 12 additions & 0 deletions Documentation/ABI/testing/configfs-usb-gadget-sourcesink
@@ -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
7 changes: 4 additions & 3 deletions drivers/usb/gadget/Kconfig
Expand Up @@ -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
Expand Down
318 changes: 318 additions & 0 deletions drivers/usb/gadget/f_sourcesink.c
Expand Up @@ -477,6 +477,14 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
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));
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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,
Expand Down

0 comments on commit 25d8015

Please sign in to comment.