summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrzej Pietrasiewicz <andrzej.p@samsung.com>2014-07-22 21:58:35 +0400
committerFelipe Balbi <balbi@ti.com>2014-08-20 23:04:42 +0400
commit3aeea3c53e73b972ff07a1d03d6cc07f97de4f2f (patch)
treee05e46602c596d7b717f5db3bfb93894f782baef
parent065a107cdd70f0621011424009b3ecd4e42481b1 (diff)
downloadlinux-3aeea3c53e73b972ff07a1d03d6cc07f97de4f2f.tar.xz
usb: gadget: f_uac2: add configfs support
Add support for using f_uac2 function as a component of a gadget composed with configfs. Tested-by: Sebastian Reimers <sebastian.reimers@googlemail.com> Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r--Documentation/ABI/testing/configfs-usb-gadget-uac212
-rw-r--r--drivers/usb/gadget/function/f_uac2.c105
-rw-r--r--drivers/usb/gadget/function/u_uac2.h3
3 files changed, 120 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2
new file mode 100644
index 000000000000..2bfdd4efa9bd
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac2
@@ -0,0 +1,12 @@
+What: /config/usb-gadget/gadget/functions/uac2.name
+Date: Sep 2014
+KernelVersion: 3.18
+Description:
+ The attributes:
+
+ c_chmask - capture channel mask
+ c_srate - capture sampling rate
+ c_ssize - capture sample size (bytes)
+ p_chmask - playback channel mask
+ p_srate - playback sampling rate
+ p_ssize - playback sample size (bytes)
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 1b9671effa12..0d65e7c08a93 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1330,6 +1330,93 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr)
return value;
}
+static inline struct f_uac2_opts *to_f_uac2_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_uac2_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_uac2_opts);
+CONFIGFS_ATTR_OPS(f_uac2_opts);
+
+static void f_uac2_attr_release(struct config_item *item)
+{
+ struct f_uac2_opts *opts = to_f_uac2_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations f_uac2_item_ops = {
+ .release = f_uac2_attr_release,
+ .show_attribute = f_uac2_opts_attr_show,
+ .store_attribute = f_uac2_opts_attr_store,
+};
+
+#define UAC2_ATTRIBUTE(name) \
+static ssize_t f_uac2_opts_##name##_show(struct f_uac2_opts *opts, \
+ char *page) \
+{ \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%u\n", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac2_opts_##name##_store(struct f_uac2_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->name = num; \
+ ret = len; \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+static struct f_uac2_opts_attribute f_uac2_opts_##name = \
+ __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \
+ f_uac2_opts_##name##_show, \
+ f_uac2_opts_##name##_store)
+
+UAC2_ATTRIBUTE(p_chmask);
+UAC2_ATTRIBUTE(p_srate);
+UAC2_ATTRIBUTE(p_ssize);
+UAC2_ATTRIBUTE(c_chmask);
+UAC2_ATTRIBUTE(c_srate);
+UAC2_ATTRIBUTE(c_ssize);
+
+static struct configfs_attribute *f_uac2_attrs[] = {
+ &f_uac2_opts_p_chmask.attr,
+ &f_uac2_opts_p_srate.attr,
+ &f_uac2_opts_p_ssize.attr,
+ &f_uac2_opts_c_chmask.attr,
+ &f_uac2_opts_c_srate.attr,
+ &f_uac2_opts_c_ssize.attr,
+ NULL,
+};
+
+static struct config_item_type f_uac2_func_type = {
+ .ct_item_ops = &f_uac2_item_ops,
+ .ct_attrs = f_uac2_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
static void afunc_free_inst(struct usb_function_instance *f)
{
struct f_uac2_opts *opts;
@@ -1346,17 +1433,32 @@ static struct usb_function_instance *afunc_alloc_inst(void)
if (!opts)
return ERR_PTR(-ENOMEM);
+ mutex_init(&opts->lock);
opts->func_inst.free_func_inst = afunc_free_inst;
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &f_uac2_func_type);
+
+ opts->p_chmask = UAC2_DEF_PCHMASK;
+ opts->p_srate = UAC2_DEF_PSRATE;
+ opts->p_ssize = UAC2_DEF_PSSIZE;
+ opts->c_chmask = UAC2_DEF_CCHMASK;
+ opts->c_srate = UAC2_DEF_CSRATE;
+ opts->c_ssize = UAC2_DEF_CSSIZE;
return &opts->func_inst;
}
static void afunc_free(struct usb_function *f)
{
struct audio_dev *agdev;
+ struct f_uac2_opts *opts;
agdev = func_to_agdev(f);
+ opts = container_of(f->fi, struct f_uac2_opts, func_inst);
kfree(agdev);
+ mutex_lock(&opts->lock);
+ --opts->refcnt;
+ mutex_unlock(&opts->lock);
}
static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
@@ -1389,6 +1491,9 @@ struct usb_function *afunc_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_uac2_opts, func_inst);
+ mutex_lock(&opts->lock);
+ ++opts->refcnt;
+ mutex_unlock(&opts->lock);
agdev->func.name = "uac2_func";
agdev->func.bind = afunc_bind;
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index ed7f7361a53e..78dd37279bd4 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -34,6 +34,9 @@ struct f_uac2_opts {
int c_srate;
int c_ssize;
bool bound;
+
+ struct mutex lock;
+ int refcnt;
};
#endif