Skip to content

Commit

Permalink
usb: gadget: f_ncm: add configfs support
Browse files Browse the repository at this point in the history
Add configfs support to the NCM function driver so
that we can, eventually, get rid of kernel-based
gadget drivers.

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 Jun 10, 2013
1 parent 8feffd0 commit e730660
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 0 deletions.
166 changes: 166 additions & 0 deletions drivers/usb/gadget/f_ncm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1175,8 +1175,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
* with regard to ncm_opts->bound access
*/
if (!ncm_opts->bound) {
mutex_lock(&ncm_opts->lock);
gether_set_gadget(ncm_opts->net, cdev->gadget);
status = gether_register_netdev(ncm_opts->net);
mutex_unlock(&ncm_opts->lock);
if (status)
return status;
ncm_opts->bound = true;
Expand Down Expand Up @@ -1290,6 +1292,159 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
return status;
}

static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_ncm_opts,
func_inst.group);
}

CONFIGFS_ATTR_STRUCT(f_ncm_opts);
CONFIGFS_ATTR_OPS(f_ncm_opts);

static void ncm_attr_release(struct config_item *item)
{
struct f_ncm_opts *opts = to_f_ncm_opts(item);

usb_put_function_instance(&opts->func_inst);
}

static struct configfs_item_operations ncm_item_ops = {
.release = ncm_attr_release,
.show_attribute = f_ncm_opts_attr_show,
.store_attribute = f_ncm_opts_attr_store,
};

static ssize_t ncm_opts_dev_addr_show(struct f_ncm_opts *opts, char *page)
{
int result;

mutex_lock(&opts->lock);
result = gether_get_dev_addr(opts->net, page, PAGE_SIZE);
mutex_unlock(&opts->lock);

return result;
}

static ssize_t ncm_opts_dev_addr_store(struct f_ncm_opts *opts,
const char *page, size_t len)
{
int ret;

mutex_lock(&opts->lock);
if (opts->refcnt) {
mutex_unlock(&opts->lock);
return -EBUSY;
}

ret = gether_set_dev_addr(opts->net, page);
mutex_unlock(&opts->lock);
if (!ret)
ret = len;
return ret;
}

static struct f_ncm_opts_attribute f_ncm_opts_dev_addr =
__CONFIGFS_ATTR(dev_addr, S_IRUGO | S_IWUSR, ncm_opts_dev_addr_show,
ncm_opts_dev_addr_store);

static ssize_t ncm_opts_host_addr_show(struct f_ncm_opts *opts, char *page)
{
int result;

mutex_lock(&opts->lock);
result = gether_get_host_addr(opts->net, page, PAGE_SIZE);
mutex_unlock(&opts->lock);

return result;
}

static ssize_t ncm_opts_host_addr_store(struct f_ncm_opts *opts,
const char *page, size_t len)
{
int ret;

mutex_lock(&opts->lock);
if (opts->refcnt) {
mutex_unlock(&opts->lock);
return -EBUSY;
}

ret = gether_set_host_addr(opts->net, page);
mutex_unlock(&opts->lock);
if (!ret)
ret = len;
return ret;
}

static struct f_ncm_opts_attribute f_ncm_opts_host_addr =
__CONFIGFS_ATTR(host_addr, S_IRUGO | S_IWUSR, ncm_opts_host_addr_show,
ncm_opts_host_addr_store);

static ssize_t ncm_opts_qmult_show(struct f_ncm_opts *opts, char *page)
{
unsigned qmult;

mutex_lock(&opts->lock);
qmult = gether_get_qmult(opts->net);
mutex_unlock(&opts->lock);
return sprintf(page, "%d", qmult);
}

static ssize_t ncm_opts_qmult_store(struct f_ncm_opts *opts,
const char *page, size_t len)
{
u8 val;
int ret;

mutex_lock(&opts->lock);
if (opts->refcnt) {
ret = -EBUSY;
goto out;
}

ret = kstrtou8(page, 0, &val);
if (ret)
goto out;

gether_set_qmult(opts->net, val);
ret = len;
out:
mutex_unlock(&opts->lock);
return ret;
}

static struct f_ncm_opts_attribute f_ncm_opts_qmult =
__CONFIGFS_ATTR(qmult, S_IRUGO | S_IWUSR, ncm_opts_qmult_show,
ncm_opts_qmult_store);

static ssize_t ncm_opts_ifname_show(struct f_ncm_opts *opts, char *page)
{
int ret;

mutex_lock(&opts->lock);
ret = gether_get_ifname(opts->net, page, PAGE_SIZE);
mutex_unlock(&opts->lock);

return ret;
}

static struct f_ncm_opts_attribute f_ncm_opts_ifname =
__CONFIGFS_ATTR_RO(ifname, ncm_opts_ifname_show);

static struct configfs_attribute *ncm_attrs[] = {
&f_ncm_opts_dev_addr.attr,
&f_ncm_opts_host_addr.attr,
&f_ncm_opts_qmult.attr,
&f_ncm_opts_ifname.attr,
NULL,
};

static struct config_item_type ncm_func_type = {
.ct_item_ops = &ncm_item_ops,
.ct_attrs = ncm_attrs,
.ct_owner = THIS_MODULE,
};

static void ncm_free_inst(struct usb_function_instance *f)
{
struct f_ncm_opts *opts;
Expand All @@ -1309,20 +1464,28 @@ static struct usb_function_instance *ncm_alloc_inst(void)
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ncm_free_inst;
opts->net = gether_setup_default();
if (IS_ERR(opts->net))
return ERR_PTR(PTR_ERR(opts->net));

config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);

return &opts->func_inst;
}

static void ncm_free(struct usb_function *f)
{
struct f_ncm *ncm;
struct f_ncm_opts *opts;

ncm = func_to_ncm(f);
opts = container_of(f->fi, struct f_ncm_opts, func_inst);
kfree(ncm);
mutex_lock(&opts->lock);
opts->refcnt--;
mutex_unlock(&opts->lock);
}

static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
Expand All @@ -1349,6 +1512,8 @@ struct usb_function *ncm_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM);

opts = container_of(fi, struct f_ncm_opts, func_inst);
mutex_lock(&opts->lock);
opts->refcnt++;

/* export host's Ethernet address in CDC format */
status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
Expand All @@ -1362,6 +1527,7 @@ struct usb_function *ncm_alloc(struct usb_function_instance *fi)
spin_lock_init(&ncm->lock);
ncm_reset_values(ncm);
ncm->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
ncm->port.is_fixed = true;

ncm->port.func.name = "cdc_network";
Expand Down
9 changes: 9 additions & 0 deletions drivers/usb/gadget/u_ncm.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ struct f_ncm_opts {
struct usb_function_instance func_inst;
struct net_device *net;
bool bound;

/*
* 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;
};

#endif /* U_NCM_H */

0 comments on commit e730660

Please sign in to comment.