usb: gadget: f_sourcesink: add configfs support

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>
This commit is contained in:
Andrzej Pietrasiewicz 2013-11-07 08:41:28 +01:00 committed by Felipe Balbi
parent c0501f47c6
commit 25d8015177
5 changed files with 347 additions and 5 deletions

View File

@ -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

View 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

View 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,

View 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 {

View 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,
};