Sending v2 of this patch series, including fixes for build regressions
introduced with the original series. I missed the usage of f_uac* in the
legacy modules and did not test that. Fixed now, see patches changelog for
details.
This patch series adds support for exposing multiple supported sampling rates
from UAC1 and UAC2 USB gadgets to the connected host. It is currently limited
to up to ten discrete sampling frequencies. The USB specification does not
actually limit this, but to avoid complex list handling I am using a static
array for now.
As the configfs bindings for f_uac1 and f_uac2 have been identical already, I
decided to move the shared code for this out of the functions first. This
avoids code duplication and simplifies the addition of the list parsing for
sampling rates.
The host can configure active sampling rate and the function adapts it's
internal active rate automatically. On playback/capture start the rate is
checked, so that the user recognizes rate mismatches. Furthermore the active
rate is exposed as an amixer control with change notifications, so that
users can check current rate upfront and get notified about updates.
Julian Scheel (3):
usb: gadget: f_uac1: Fix endpoint reading
usb: gadget: f_uac*: Reduce code duplication
usb: gadget: f_uac*: Support multiple sampling rates
Documentation/ABI/testing/configfs-usb-gadget-uac1 | 4 +-
Documentation/usb/gadget-testing.txt | 8 +-
drivers/usb/gadget/function/f_uac1.c | 258 +++++++++---------
drivers/usb/gadget/function/f_uac2.c | 297 ++++++++++-----------
drivers/usb/gadget/function/u_audio.c | 118 +++++++-
drivers/usb/gadget/function/u_audio.h | 9 +-
drivers/usb/gadget/function/u_uac.h | 180 +++++++++++++
drivers/usb/gadget/function/u_uac1.h | 41 ---
drivers/usb/gadget/function/u_uac2.h | 44 ---
drivers/usb/gadget/legacy/audio.c | 91 ++-----
10 files changed, 608 insertions(+), 442 deletions(-)
create mode 100644 drivers/usb/gadget/function/u_uac.h
delete mode 100644 drivers/usb/gadget/function/u_uac1.h
delete mode 100644 drivers/usb/gadget/function/u_uac2.h
--
2.13.2
The endpoint is stored in the lower byte of wIndex, according to USB
Audio 1.0 specification, section 5.2.1.1.
Signed-off-by: Julian Scheel <[email protected]>
---
drivers/usb/gadget/function/f_uac1.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 8656f84e17d9..b955913bd7ea 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -327,7 +327,7 @@ static int audio_set_endpoint_req(struct usb_function *f,
{
struct usb_composite_dev *cdev = f->config->cdev;
int value = -EOPNOTSUPP;
- u16 ep = le16_to_cpu(ctrl->wIndex);
+ u8 ep = le16_to_cpu(ctrl->wIndex) & 0xff;
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
@@ -363,7 +363,7 @@ static int audio_get_endpoint_req(struct usb_function *f,
{
struct usb_composite_dev *cdev = f->config->cdev;
int value = -EOPNOTSUPP;
- u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+ u8 ep = le16_to_cpu(ctrl->wIndex) & 0xff;
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
--
2.13.2
This replaces the dedicated headers for uac1 and uac2 functions with a
shared header for both of them. Apart from unifying the struct names,
further duplicated code for configfs setup is moved out of the function
files into the shared header.
Signed-off-by: Julian Scheel <[email protected]>
---
Changes in v2:
- Fix build as module
- Fix build of legacy/audio module in f_uac1/f_uac2 mode
drivers/usb/gadget/function/f_uac1.c | 142 ++++++++----------------------
drivers/usb/gadget/function/f_uac2.c | 161 ++++++++++------------------------
drivers/usb/gadget/function/u_audio.c | 1 -
drivers/usb/gadget/function/u_uac.h | 116 ++++++++++++++++++++++++
drivers/usb/gadget/function/u_uac1.h | 41 ---------
drivers/usb/gadget/function/u_uac2.h | 44 ----------
drivers/usb/gadget/legacy/audio.c | 91 +++++--------------
7 files changed, 222 insertions(+), 374 deletions(-)
create mode 100644 drivers/usb/gadget/function/u_uac.h
delete mode 100644 drivers/usb/gadget/function/u_uac1.h
delete mode 100644 drivers/usb/gadget/function/u_uac2.h
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index b955913bd7ea..6e86e4f704e1 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -21,18 +21,7 @@
#include <linux/module.h>
#include "u_audio.h"
-#include "u_uac1.h"
-
-struct f_uac1 {
- struct g_audio g_audio;
- u8 ac_intf, as_in_intf, as_out_intf;
- u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */
-};
-
-static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
-{
- return container_of(f, struct f_uac1, g_audio.func);
-}
+#include "u_uac.h"
/*
* DESCRIPTORS ... most are static, but strings and full
@@ -435,7 +424,7 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &gadget->dev;
- struct f_uac1 *uac1 = func_to_uac1(f);
+ struct f_uac *uac1 = func_to_uac(f);
int ret = 0;
/* No i/f has more than 2 alt settings */
@@ -480,7 +469,7 @@ static int f_audio_get_alt(struct usb_function *f, unsigned intf)
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &gadget->dev;
- struct f_uac1 *uac1 = func_to_uac1(f);
+ struct f_uac *uac1 = func_to_uac(f);
if (intf == uac1->ac_intf)
return uac1->ac_alt;
@@ -498,7 +487,7 @@ static int f_audio_get_alt(struct usb_function *f, unsigned intf)
static void f_audio_disable(struct usb_function *f)
{
- struct f_uac1 *uac1 = func_to_uac1(f);
+ struct f_uac *uac1 = func_to_uac(f);
uac1->as_out_alt = 0;
uac1->as_in_alt = 0;
@@ -513,16 +502,16 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct usb_gadget *gadget = cdev->gadget;
- struct f_uac1 *uac1 = func_to_uac1(f);
+ struct f_uac *uac1 = func_to_uac(f);
struct g_audio *audio = func_to_g_audio(f);
- struct f_uac1_opts *audio_opts;
+ struct f_uac_opts *audio_opts;
struct usb_ep *ep = NULL;
struct usb_string *us;
u8 *sam_freq;
int rate;
int status;
- audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
+ audio_opts = container_of(f->fi, struct f_uac_opts, func_inst);
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
if (IS_ERR(us))
@@ -630,82 +619,27 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
/*-------------------------------------------------------------------------*/
-static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item)
-{
- return container_of(to_config_group(item), struct f_uac1_opts,
- func_inst.group);
-}
-
-static void f_uac1_attr_release(struct config_item *item)
-{
- struct f_uac1_opts *opts = to_f_uac1_opts(item);
-
- usb_put_function_instance(&opts->func_inst);
-}
-
static struct configfs_item_operations f_uac1_item_ops = {
- .release = f_uac1_attr_release,
+ .release = f_uac_attr_release,
};
-#define UAC1_ATTRIBUTE(name) \
-static ssize_t f_uac1_opts_##name##_show( \
- struct config_item *item, \
- char *page) \
-{ \
- struct f_uac1_opts *opts = to_f_uac1_opts(item); \
- int result; \
- \
- mutex_lock(&opts->lock); \
- result = sprintf(page, "%u\n", opts->name); \
- mutex_unlock(&opts->lock); \
- \
- return result; \
-} \
- \
-static ssize_t f_uac1_opts_##name##_store( \
- struct config_item *item, \
- const char *page, size_t len) \
-{ \
- struct f_uac1_opts *opts = to_f_uac1_opts(item); \
- 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; \
-} \
- \
-CONFIGFS_ATTR(f_uac1_opts_, name)
-
-UAC1_ATTRIBUTE(c_chmask);
-UAC1_ATTRIBUTE(c_srate);
-UAC1_ATTRIBUTE(c_ssize);
-UAC1_ATTRIBUTE(p_chmask);
-UAC1_ATTRIBUTE(p_srate);
-UAC1_ATTRIBUTE(p_ssize);
-UAC1_ATTRIBUTE(req_number);
+
+UAC_ATTRIBUTE(c_chmask);
+UAC_ATTRIBUTE(c_srate);
+UAC_ATTRIBUTE(c_ssize);
+UAC_ATTRIBUTE(p_chmask);
+UAC_ATTRIBUTE(p_srate);
+UAC_ATTRIBUTE(p_ssize);
+UAC_ATTRIBUTE(req_number);
static struct configfs_attribute *f_uac1_attrs[] = {
- &f_uac1_opts_attr_c_chmask,
- &f_uac1_opts_attr_c_srate,
- &f_uac1_opts_attr_c_ssize,
- &f_uac1_opts_attr_p_chmask,
- &f_uac1_opts_attr_p_srate,
- &f_uac1_opts_attr_p_ssize,
- &f_uac1_opts_attr_req_number,
+ &f_uac_opts_attr_c_chmask,
+ &f_uac_opts_attr_c_srate,
+ &f_uac_opts_attr_c_ssize,
+ &f_uac_opts_attr_p_chmask,
+ &f_uac_opts_attr_p_srate,
+ &f_uac_opts_attr_p_ssize,
+ &f_uac_opts_attr_req_number,
NULL,
};
@@ -717,15 +651,15 @@ static struct config_item_type f_uac1_func_type = {
static void f_audio_free_inst(struct usb_function_instance *f)
{
- struct f_uac1_opts *opts;
+ struct f_uac_opts *opts;
- opts = container_of(f, struct f_uac1_opts, func_inst);
+ opts = container_of(f, struct f_uac_opts, func_inst);
kfree(opts);
}
static struct usb_function_instance *f_audio_alloc_inst(void)
{
- struct f_uac1_opts *opts;
+ struct f_uac_opts *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
@@ -737,23 +671,23 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
config_group_init_type_name(&opts->func_inst.group, "",
&f_uac1_func_type);
- opts->c_chmask = UAC1_DEF_CCHMASK;
- opts->c_srate = UAC1_DEF_CSRATE;
- opts->c_ssize = UAC1_DEF_CSSIZE;
- opts->p_chmask = UAC1_DEF_PCHMASK;
- opts->p_srate = UAC1_DEF_PSRATE;
- opts->p_ssize = UAC1_DEF_PSSIZE;
- opts->req_number = UAC1_DEF_REQ_NUM;
+ opts->c_chmask = UAC_DEF_CCHMASK;
+ opts->c_srate = UAC_DEF_CSRATE;
+ opts->c_ssize = UAC_DEF_CSSIZE;
+ opts->p_chmask = UAC_DEF_PCHMASK;
+ opts->p_srate = UAC_DEF_PSRATE;
+ opts->p_ssize = UAC_DEF_PSSIZE;
+ opts->req_number = UAC_DEF_REQ_NUM;
return &opts->func_inst;
}
static void f_audio_free(struct usb_function *f)
{
struct g_audio *audio;
- struct f_uac1_opts *opts;
+ struct f_uac_opts *opts;
audio = func_to_g_audio(f);
- opts = container_of(f->fi, struct f_uac1_opts, func_inst);
+ opts = container_of(f->fi, struct f_uac_opts, func_inst);
kfree(audio);
mutex_lock(&opts->lock);
--opts->refcnt;
@@ -772,15 +706,15 @@ static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
{
- struct f_uac1 *uac1;
- struct f_uac1_opts *opts;
+ struct f_uac *uac1;
+ struct f_uac_opts *opts;
/* allocate and initialize one new instance */
uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL);
if (!uac1)
return ERR_PTR(-ENOMEM);
- opts = container_of(fi, struct f_uac1_opts, func_inst);
+ opts = container_of(fi, struct f_uac_opts, func_inst);
mutex_lock(&opts->lock);
++opts->refcnt;
mutex_unlock(&opts->lock);
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 9082ce261e70..f5e93e168ec5 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -16,7 +16,7 @@
#include <linux/module.h>
#include "u_audio.h"
-#include "u_uac2.h"
+#include "u_uac.h"
/*
* The driver implements a simple UAC_2 topology.
@@ -47,23 +47,6 @@
#define UNFLW_CTRL 8
#define OVFLW_CTRL 10
-struct f_uac2 {
- struct g_audio g_audio;
- u8 ac_intf, as_in_intf, as_out_intf;
- u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */
-};
-
-static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
-{
- return container_of(f, struct f_uac2, g_audio.func);
-}
-
-static inline
-struct f_uac2_opts *g_audio_to_uac2_opts(struct g_audio *agdev)
-{
- return container_of(agdev->func.fi, struct f_uac2_opts, func_inst);
-}
-
/* --------- USB Function Interface ------------- */
enum {
@@ -451,7 +434,7 @@ struct cntrl_range_lay3 {
__u32 dRES;
} __packed;
-static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
+static void set_ep_max_packet_size(const struct f_uac_opts *uac2_opts,
struct usb_endpoint_descriptor *ep_desc,
unsigned int factor, bool is_playback)
{
@@ -477,16 +460,16 @@ static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
static int
afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
{
- struct f_uac2 *uac2 = func_to_uac2(fn);
+ struct f_uac *uac2 = func_to_uac(fn);
struct g_audio *agdev = func_to_g_audio(fn);
struct usb_composite_dev *cdev = cfg->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &gadget->dev;
- struct f_uac2_opts *uac2_opts;
+ struct f_uac_opts *uac2_opts;
struct usb_string *us;
int ret;
- uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst);
+ uac2_opts = container_of(fn->fi, struct f_uac_opts, func_inst);
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
if (IS_ERR(us))
@@ -606,7 +589,7 @@ static int
afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
{
struct usb_composite_dev *cdev = fn->config->cdev;
- struct f_uac2 *uac2 = func_to_uac2(fn);
+ struct f_uac *uac2 = func_to_uac(fn);
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &gadget->dev;
int ret = 0;
@@ -651,7 +634,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
static int
afunc_get_alt(struct usb_function *fn, unsigned intf)
{
- struct f_uac2 *uac2 = func_to_uac2(fn);
+ struct f_uac *uac2 = func_to_uac(fn);
struct g_audio *agdev = func_to_g_audio(fn);
if (intf == uac2->ac_intf)
@@ -671,7 +654,7 @@ afunc_get_alt(struct usb_function *fn, unsigned intf)
static void
afunc_disable(struct usb_function *fn)
{
- struct f_uac2 *uac2 = func_to_uac2(fn);
+ struct f_uac *uac2 = func_to_uac(fn);
uac2->as_in_alt = 0;
uac2->as_out_alt = 0;
@@ -684,7 +667,7 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
struct usb_request *req = fn->config->cdev->req;
struct g_audio *agdev = func_to_g_audio(fn);
- struct f_uac2_opts *opts;
+ struct f_uac_opts *opts;
u16 w_length = le16_to_cpu(cr->wLength);
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
@@ -693,7 +676,7 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
int value = -EOPNOTSUPP;
int p_srate, c_srate;
- opts = g_audio_to_uac2_opts(agdev);
+ opts = g_audio_to_uac_opts(agdev);
p_srate = opts->p_srate;
c_srate = opts->c_srate;
@@ -725,7 +708,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
struct usb_request *req = fn->config->cdev->req;
struct g_audio *agdev = func_to_g_audio(fn);
- struct f_uac2_opts *opts;
+ struct f_uac_opts *opts;
u16 w_length = le16_to_cpu(cr->wLength);
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
@@ -735,7 +718,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
int value = -EOPNOTSUPP;
int p_srate, c_srate;
- opts = g_audio_to_uac2_opts(agdev);
+ opts = g_audio_to_uac_opts(agdev);
p_srate = opts->p_srate;
c_srate = opts->c_srate;
@@ -789,7 +772,7 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
static int
setup_rq_inf(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
- struct f_uac2 *uac2 = func_to_uac2(fn);
+ struct f_uac *uac2 = func_to_uac(fn);
struct g_audio *agdev = func_to_g_audio(fn);
u16 w_index = le16_to_cpu(cr->wIndex);
u8 intf = w_index & 0xff;
@@ -841,80 +824,26 @@ 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);
-}
-
-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,
-};
-
-#define UAC2_ATTRIBUTE(name) \
-static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
- char *page) \
-{ \
- struct f_uac2_opts *opts = to_f_uac2_opts(item); \
- 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 config_item *item, \
- const char *page, size_t len) \
-{ \
- struct f_uac2_opts *opts = to_f_uac2_opts(item); \
- 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; \
-} \
- \
-CONFIGFS_ATTR(f_uac2_opts_, name)
-
-UAC2_ATTRIBUTE(p_chmask);
-UAC2_ATTRIBUTE(p_srate);
-UAC2_ATTRIBUTE(p_ssize);
-UAC2_ATTRIBUTE(c_chmask);
-UAC2_ATTRIBUTE(c_srate);
-UAC2_ATTRIBUTE(c_ssize);
-UAC2_ATTRIBUTE(req_number);
+ .release = f_uac_attr_release,
+};
+
+UAC_ATTRIBUTE(p_chmask);
+UAC_ATTRIBUTE(p_srate);
+UAC_ATTRIBUTE(p_ssize);
+UAC_ATTRIBUTE(c_chmask);
+UAC_ATTRIBUTE(c_srate);
+UAC_ATTRIBUTE(c_ssize);
+UAC_ATTRIBUTE(req_number);
static struct configfs_attribute *f_uac2_attrs[] = {
- &f_uac2_opts_attr_p_chmask,
- &f_uac2_opts_attr_p_srate,
- &f_uac2_opts_attr_p_ssize,
- &f_uac2_opts_attr_c_chmask,
- &f_uac2_opts_attr_c_srate,
- &f_uac2_opts_attr_c_ssize,
- &f_uac2_opts_attr_req_number,
+ &f_uac_opts_attr_p_chmask,
+ &f_uac_opts_attr_p_srate,
+ &f_uac_opts_attr_p_ssize,
+ &f_uac_opts_attr_c_chmask,
+ &f_uac_opts_attr_c_srate,
+ &f_uac_opts_attr_c_ssize,
+ &f_uac_opts_attr_req_number,
NULL,
};
@@ -926,15 +855,15 @@ static struct config_item_type f_uac2_func_type = {
static void afunc_free_inst(struct usb_function_instance *f)
{
- struct f_uac2_opts *opts;
+ struct f_uac_opts *opts;
- opts = container_of(f, struct f_uac2_opts, func_inst);
+ opts = container_of(f, struct f_uac_opts, func_inst);
kfree(opts);
}
static struct usb_function_instance *afunc_alloc_inst(void)
{
- struct f_uac2_opts *opts;
+ struct f_uac_opts *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
@@ -946,23 +875,23 @@ static struct usb_function_instance *afunc_alloc_inst(void)
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;
- opts->req_number = UAC2_DEF_REQ_NUM;
+ opts->p_chmask = UAC_DEF_PCHMASK;
+ opts->p_srate = UAC_DEF_PSRATE;
+ opts->p_ssize = UAC_DEF_PSSIZE;
+ opts->c_chmask = UAC_DEF_CCHMASK;
+ opts->c_srate = UAC_DEF_CSRATE;
+ opts->c_ssize = UAC_DEF_CSSIZE;
+ opts->req_number = UAC_DEF_REQ_NUM;
return &opts->func_inst;
}
static void afunc_free(struct usb_function *f)
{
struct g_audio *agdev;
- struct f_uac2_opts *opts;
+ struct f_uac_opts *opts;
agdev = func_to_g_audio(f);
- opts = container_of(f->fi, struct f_uac2_opts, func_inst);
+ opts = container_of(f->fi, struct f_uac_opts, func_inst);
kfree(agdev);
mutex_lock(&opts->lock);
--opts->refcnt;
@@ -981,14 +910,14 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
{
- struct f_uac2 *uac2;
- struct f_uac2_opts *opts;
+ struct f_uac *uac2;
+ struct f_uac_opts *opts;
uac2 = kzalloc(sizeof(*uac2), GFP_KERNEL);
if (uac2 == NULL)
return ERR_PTR(-ENOMEM);
- opts = container_of(fi, struct f_uac2_opts, func_inst);
+ opts = container_of(fi, struct f_uac_opts, func_inst);
mutex_lock(&opts->lock);
++opts->refcnt;
mutex_unlock(&opts->lock);
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 5dd73b9e5172..bf4da4361b6d 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -392,7 +392,6 @@ static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep)
dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
}
-
int u_audio_start_capture(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
diff --git a/drivers/usb/gadget/function/u_uac.h b/drivers/usb/gadget/function/u_uac.h
new file mode 100644
index 000000000000..b0bc811b1ffb
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uac.h
@@ -0,0 +1,116 @@
+/*
+ * u_uac1.h - Utility definitions for UAC function
+ *
+ * Copyright (C) 2016 Ruslan Bilovol <[email protected]>
+ * Copyright (C) 2017 Julian Scheel <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __U_UAC_H
+#define __U_UAC_H
+
+#include <linux/usb/composite.h>
+
+#define UAC_DEF_CCHMASK 0x3
+#define UAC_DEF_CSRATE 48000
+#define UAC_DEF_CSSIZE 2
+#define UAC_DEF_PCHMASK 0x3
+#define UAC_DEF_PSRATE 48000
+#define UAC_DEF_PSSIZE 2
+#define UAC_DEF_REQ_NUM 2
+
+#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
+
+struct f_uac_opts {
+ struct usb_function_instance func_inst;
+ int c_chmask;
+ int c_srate;
+ int c_ssize;
+ int p_chmask;
+ int p_srate;
+ int p_ssize;
+ int req_number;
+ unsigned bound:1;
+
+ struct mutex lock;
+ int refcnt;
+};
+
+#define UAC_ATTRIBUTE(name) \
+static ssize_t f_uac_opts_##name##_show( \
+ struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac_opts *opts = to_f_uac_opts(item); \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%u\n", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac_opts_##name##_store( \
+ struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac_opts *opts = to_f_uac_opts(item); \
+ 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; \
+} \
+ \
+CONFIGFS_ATTR(f_uac_opts_, name)
+
+struct f_uac {
+ struct g_audio g_audio;
+ u8 ac_intf, as_in_intf, as_out_intf;
+ u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */
+};
+
+static inline struct f_uac *func_to_uac(struct usb_function *f)
+{
+ return container_of(f, struct f_uac, g_audio.func);
+}
+
+static inline
+struct f_uac_opts *g_audio_to_uac_opts(struct g_audio *agdev)
+{
+ return container_of(agdev->func.fi, struct f_uac_opts, func_inst);
+}
+
+static inline struct f_uac_opts *to_f_uac_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_uac_opts,
+ func_inst.group);
+}
+
+void f_uac_attr_release(struct config_item *item)
+{
+ struct f_uac_opts *opts = to_f_uac_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+
+#endif /* __U_UAC_H */
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
deleted file mode 100644
index 6f188fd8633f..000000000000
--- a/drivers/usb/gadget/function/u_uac1.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * u_uac1.h - Utility definitions for UAC1 function
- *
- * Copyright (C) 2016 Ruslan Bilovol <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __U_UAC1_H
-#define __U_UAC1_H
-
-#include <linux/usb/composite.h>
-
-#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
-#define UAC1_DEF_CCHMASK 0x3
-#define UAC1_DEF_CSRATE 48000
-#define UAC1_DEF_CSSIZE 2
-#define UAC1_DEF_PCHMASK 0x3
-#define UAC1_DEF_PSRATE 48000
-#define UAC1_DEF_PSSIZE 2
-#define UAC1_DEF_REQ_NUM 2
-
-
-struct f_uac1_opts {
- struct usb_function_instance func_inst;
- int c_chmask;
- int c_srate;
- int c_ssize;
- int p_chmask;
- int p_srate;
- int p_ssize;
- int req_number;
- unsigned bound:1;
-
- struct mutex lock;
- int refcnt;
-};
-
-#endif /* __U_UAC1_H */
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
deleted file mode 100644
index 19eeb83538a5..000000000000
--- a/drivers/usb/gadget/function/u_uac2.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * u_uac2.h
- *
- * Utility definitions for UAC2 function
- *
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Author: Andrzej Pietrasiewicz <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef U_UAC2_H
-#define U_UAC2_H
-
-#include <linux/usb/composite.h>
-
-#define UAC2_DEF_PCHMASK 0x3
-#define UAC2_DEF_PSRATE 48000
-#define UAC2_DEF_PSSIZE 2
-#define UAC2_DEF_CCHMASK 0x3
-#define UAC2_DEF_CSRATE 64000
-#define UAC2_DEF_CSSIZE 2
-#define UAC2_DEF_REQ_NUM 2
-
-struct f_uac2_opts {
- struct usb_function_instance func_inst;
- int p_chmask;
- int p_srate;
- int p_ssize;
- int c_chmask;
- int c_srate;
- int c_ssize;
- int req_number;
- bool bound;
-
- struct mutex lock;
- int refcnt;
-};
-
-#endif
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index 1f5cdbe162df..912a23cf40d0 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -20,69 +20,36 @@
USB_GADGET_COMPOSITE_OPTIONS();
-#ifndef CONFIG_GADGET_UAC1
-#include "u_uac2.h"
+#if !defined(CONFIG_GADGET_UAC1) || !defined(CONFIG_GADGET_UAC1_LEGACY)
+#include "u_uac.h"
/* Playback(USB-IN) Default Stereo - Fl/Fr */
-static int p_chmask = UAC2_DEF_PCHMASK;
+static int p_chmask = UAC_DEF_PCHMASK;
module_param(p_chmask, uint, S_IRUGO);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
-static int p_srate = UAC2_DEF_PSRATE;
+static int p_srate = UAC_DEF_PSRATE;
module_param(p_srate, uint, S_IRUGO);
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
/* Playback Default 16bits/sample */
-static int p_ssize = UAC2_DEF_PSSIZE;
+static int p_ssize = UAC_DEF_PSSIZE;
module_param(p_ssize, uint, S_IRUGO);
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
-static int c_chmask = UAC2_DEF_CCHMASK;
+static int c_chmask = UAC_DEF_CCHMASK;
module_param(c_chmask, uint, S_IRUGO);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 64 KHz */
-static int c_srate = UAC2_DEF_CSRATE;
+static int c_srate = UAC_DEF_CSRATE;
module_param(c_srate, uint, S_IRUGO);
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
/* Capture Default 16bits/sample */
-static int c_ssize = UAC2_DEF_CSSIZE;
-module_param(c_ssize, uint, S_IRUGO);
-MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
-#else
-#ifndef CONFIG_GADGET_UAC1_LEGACY
-#include "u_uac1.h"
-
-/* Playback(USB-IN) Default Stereo - Fl/Fr */
-static int p_chmask = UAC1_DEF_PCHMASK;
-module_param(p_chmask, uint, S_IRUGO);
-MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
-
-/* Playback Default 48 KHz */
-static int p_srate = UAC1_DEF_PSRATE;
-module_param(p_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
-
-/* Playback Default 16bits/sample */
-static int p_ssize = UAC1_DEF_PSSIZE;
-module_param(p_ssize, uint, S_IRUGO);
-MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
-
-/* Capture(USB-OUT) Default Stereo - Fl/Fr */
-static int c_chmask = UAC1_DEF_CCHMASK;
-module_param(c_chmask, uint, S_IRUGO);
-MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
-
-/* Capture Default 48 KHz */
-static int c_srate = UAC1_DEF_CSRATE;
-module_param(c_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
-
-/* Capture Default 16bits/sample */
-static int c_ssize = UAC1_DEF_CSSIZE;
+static int c_ssize = UAC_DEF_CSSIZE;
module_param(c_ssize, uint, S_IRUGO);
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
#else /* CONFIG_GADGET_UAC1_LEGACY */
@@ -112,7 +79,6 @@ static int audio_buf_size = UAC1_AUDIO_BUF_SIZE;
module_param(audio_buf_size, int, S_IRUGO);
MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
#endif /* CONFIG_GADGET_UAC1_LEGACY */
-#endif
/* string IDs are assigned dynamically */
@@ -238,24 +204,22 @@ static struct usb_configuration audio_config_driver = {
static int audio_bind(struct usb_composite_dev *cdev)
{
-#ifndef CONFIG_GADGET_UAC1
- struct f_uac2_opts *uac2_opts;
-#else
-#ifndef CONFIG_GADGET_UAC1_LEGACY
- struct f_uac1_opts *uac1_opts;
+#if !defined(CONFIG_GADGET_UAC1) || !defined(CONFIG_GADGET_UAC1_LEGACY)
+ struct f_uac_opts *uac_opts;
#else
struct f_uac1_legacy_opts *uac1_opts;
#endif
-#endif
int status;
#ifndef CONFIG_GADGET_UAC1
fi_uac2 = usb_get_function_instance("uac2");
if (IS_ERR(fi_uac2))
return PTR_ERR(fi_uac2);
+ uac_opts = container_of(fi_uac2, struct f_uac_opts, func_inst);
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
fi_uac1 = usb_get_function_instance("uac1");
+ uac_opts = container_of(fi_uac1, struct f_uac_opts, func_inst);
#else
fi_uac1 = usb_get_function_instance("uac1_legacy");
#endif
@@ -263,25 +227,17 @@ static int audio_bind(struct usb_composite_dev *cdev)
return PTR_ERR(fi_uac1);
#endif
-#ifndef CONFIG_GADGET_UAC1
- uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
- uac2_opts->p_chmask = p_chmask;
- uac2_opts->p_srate = p_srate;
- uac2_opts->p_ssize = p_ssize;
- uac2_opts->c_chmask = c_chmask;
- uac2_opts->c_srate = c_srate;
- uac2_opts->c_ssize = c_ssize;
- uac2_opts->req_number = UAC2_DEF_REQ_NUM;
-#else
-#ifndef CONFIG_GADGET_UAC1_LEGACY
- uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
- uac1_opts->p_chmask = p_chmask;
- uac1_opts->p_srate = p_srate;
- uac1_opts->p_ssize = p_ssize;
- uac1_opts->c_chmask = c_chmask;
- uac1_opts->c_srate = c_srate;
- uac1_opts->c_ssize = c_ssize;
- uac1_opts->req_number = UAC1_DEF_REQ_NUM;
+#if !defined(CONFIG_GADGET_UAC1) || !defined(CONFIG_GADGET_UAC1_LEGACY)
+ memset(uac_opts, 0x0, sizeof(uac_opts));
+ uac_opts->p_chmask = p_chmask;
+ uac_opts->p_srate[0] = p_srate;
+ uac_opts->p_srate_active = p_srate;
+ uac_opts->p_ssize = p_ssize;
+ uac_opts->c_chmask = c_chmask;
+ uac_opts->c_srate[0] = c_srate;
+ uac_opts->c_srate_active = c_srate;
+ uac_opts->c_ssize = c_ssize;
+ uac_opts->req_number = UAC_DEF_REQ_NUM;
#else /* CONFIG_GADGET_UAC1_LEGACY */
uac1_opts = container_of(fi_uac1, struct f_uac1_legacy_opts, func_inst);
uac1_opts->fn_play = fn_play;
@@ -291,7 +247,6 @@ static int audio_bind(struct usb_composite_dev *cdev)
uac1_opts->req_count = req_count;
uac1_opts->audio_buf_size = audio_buf_size;
#endif /* CONFIG_GADGET_UAC1_LEGACY */
-#endif
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
--
2.13.2
Implement support for multiple sampling rates in the USB Audio gadgets.
A list of sampling rates can be specified via configfs. All enabled
sampling rates are sent to the USB host on request. When the host
selects a sampling rate the internal active rate is updated. The
currently configured rates are exposed through amixer controls. Also on
pcm open from userspace the requested rated is checked against the
currently configured rate of the host.
Signed-off-by: Julian Scheel <[email protected]>
---
Documentation/ABI/testing/configfs-usb-gadget-uac1 | 4 +-
Documentation/usb/gadget-testing.txt | 8 +-
drivers/usb/gadget/function/f_uac1.c | 120 +++++++++++++----
drivers/usb/gadget/function/f_uac2.c | 146 +++++++++++++++------
drivers/usb/gadget/function/u_audio.c | 119 ++++++++++++++++-
drivers/usb/gadget/function/u_audio.h | 9 +-
drivers/usb/gadget/function/u_uac.h | 68 +++++++++-
7 files changed, 396 insertions(+), 78 deletions(-)
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1
index abfe447c848f..ad2fa4a00918 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uac1
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1
@@ -5,10 +5,10 @@ Description:
The attributes:
c_chmask - capture channel mask
- c_srate - capture sampling rate
+ c_srate - list of capture sampling rates (comma-separataed)
c_ssize - capture sample size (bytes)
p_chmask - playback channel mask
- p_srate - playback sampling rate
+ p_srate - list of playback sampling rates (comma-separated)
p_ssize - playback sample size (bytes)
req_number - the number of pre-allocated request
for both capture and playback
diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt
index fbc397d17e98..855555878880 100644
--- a/Documentation/usb/gadget-testing.txt
+++ b/Documentation/usb/gadget-testing.txt
@@ -629,10 +629,10 @@ The function name to use when creating the function directory is "uac2".
The uac2 function provides these attributes in its function directory:
c_chmask - capture channel mask
- c_srate - capture sampling rate
+ c_srate - list of capture sampling rates (comma-separated)
c_ssize - capture sample size (bytes)
p_chmask - playback channel mask
- p_srate - playback sampling rate
+ p_srate - list of playback sampling rates (comma-separated)
p_ssize - playback sample size (bytes)
req_number - the number of pre-allocated request for both capture
and playback
@@ -790,10 +790,10 @@ The function name to use when creating the function directory is "uac1".
The uac1 function provides these attributes in its function directory:
c_chmask - capture channel mask
- c_srate - capture sampling rate
+ c_srate - list of capture sampling rates (comma-separated)
c_ssize - capture sample size (bytes)
p_chmask - playback channel mask
- p_srate - playback sampling rate
+ p_srate - list of playback sampling rates (comma-separated)
p_ssize - playback sample size (bytes)
req_number - the number of pre-allocated request for both capture
and playback
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 6e86e4f704e1..d14777973b9f 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -2,6 +2,7 @@
* f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
*
* Copyright (C) 2016 Ruslan Bilovol <[email protected]>
+ * Copyright (C) 2017 Julian Scheel <[email protected]>
*
* This driver doesn't expect any real Audio codec to be present
* on the device - the audio streams are simply sinked to and
@@ -175,16 +176,18 @@ static struct uac1_as_header_descriptor as_in_header_desc = {
.wFormatTag = UAC_FORMAT_TYPE_I_PCM,
};
-DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
+#define uac_format_type_i_discrete_descriptor \
+ uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES
-static struct uac_format_type_i_discrete_descriptor_1 as_out_type_i_desc = {
- .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = {
+ .bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
- .bSamFreqType = 1,
+ .bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
@@ -208,14 +211,14 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
.wLockDelay = cpu_to_le16(1),
};
-static struct uac_format_type_i_discrete_descriptor_1 as_in_type_i_desc = {
- .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = {
+ .bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
- .bSamFreqType = 1,
+ .bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
@@ -311,23 +314,57 @@ static struct usb_gadget_strings *uac1_strings[] = {
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
+static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usb_function *fn = ep->driver_data;
+ struct usb_composite_dev *cdev = fn->config->cdev;
+ struct g_audio *agdev = func_to_g_audio(fn);
+ struct f_uac *uac1 = func_to_uac(fn);
+ struct f_uac_opts *opts = g_audio_to_uac_opts(agdev);
+ u8 *buf = (u8 *)req->buf;
+ u32 val = 0;
+
+ if (req->actual != 3) {
+ WARN(cdev, "Invalid data size for UAC_EP_CS_ATTR_SAMPLE_RATE.\n");
+ return;
+ }
+
+ val = buf[0] | (buf[1] << 8) | (buf[2] << 16);
+
+ if (uac1->ctl_id == (USB_DIR_IN | 1)) {
+ opts->p_srate_active = val;
+ u_audio_set_playback_srate(agdev, opts->p_srate_active);
+ } else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
+ opts->c_srate_active = val;
+ u_audio_set_capture_srate(agdev, opts->c_srate_active);
+ }
+}
+
static int audio_set_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = f->config->cdev->req;
+ struct f_uac *uac1 = func_to_uac(f);
int value = -EOPNOTSUPP;
u8 ep = le16_to_cpu(ctrl->wIndex) & 0xff;
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 cs = w_value >> 8;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
- case UAC_SET_CUR:
+ case UAC_SET_CUR: {
+ if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
+ cdev->gadget->ep0->driver_data = f;
+ uac1->ctl_id = ep;
+ req->complete = uac_cs_attr_sample_rate;
+ }
value = len;
break;
-
+ }
case UAC_SET_MIN:
break;
@@ -351,16 +388,34 @@ static int audio_get_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = f->config->cdev->req;
+ struct g_audio *agdev = func_to_g_audio(f);
+ struct f_uac_opts *opts = g_audio_to_uac_opts(agdev);
+ u8 *buf = (u8 *)req->buf;
int value = -EOPNOTSUPP;
u8 ep = le16_to_cpu(ctrl->wIndex) & 0xff;
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 cs = w_value >> 8;
+ u32 val = 0;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
- case UAC_GET_CUR:
+ case UAC_GET_CUR: {
+ if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
+ if (ep == (USB_DIR_IN | 1))
+ val = opts->p_srate_active;
+ else if (ep == (USB_DIR_OUT | 1))
+ val = opts->c_srate_active;
+ buf[2] = (val >> 16) & 0xff;
+ buf[1] = (val >> 8) & 0xff;
+ buf[0] = val & 0xff;
+ }
+ value = len;
+ break;
+ }
case UAC_GET_MIN:
case UAC_GET_MAX:
case UAC_GET_RES:
@@ -507,9 +562,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
struct f_uac_opts *audio_opts;
struct usb_ep *ep = NULL;
struct usb_string *us;
- u8 *sam_freq;
- int rate;
int status;
+ int idx, i;
audio_opts = container_of(f->fi, struct f_uac_opts, func_inst);
@@ -541,12 +595,23 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8;
/* Set sample rates */
- rate = audio_opts->c_srate;
- sam_freq = as_out_type_i_desc.tSamFreq[0];
- memcpy(sam_freq, &rate, 3);
- rate = audio_opts->p_srate;
- sam_freq = as_in_type_i_desc.tSamFreq[0];
- memcpy(sam_freq, &rate, 3);
+ for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
+ if (audio_opts->c_srate[i] == 0)
+ break;
+ memcpy(as_out_type_i_desc.tSamFreq[idx++],
+ &audio_opts->c_srate[i], 3);
+ }
+ as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
+ as_out_type_i_desc.bSamFreqType = idx;
+
+ for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
+ if (audio_opts->p_srate[i] == 0)
+ break;
+ memcpy(as_in_type_i_desc.tSamFreq[idx++],
+ &audio_opts->p_srate[i], 3);
+ }
+ as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
+ as_in_type_i_desc.bSamFreqType = idx;
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
@@ -598,10 +663,14 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->out_ep_maxpsize = as_out_ep_desc.wMaxPacketSize;
audio->in_ep_maxpsize = as_in_ep_desc.wMaxPacketSize;
audio->params.c_chmask = audio_opts->c_chmask;
- audio->params.c_srate = audio_opts->c_srate;
+ memcpy(audio->params.c_srate, audio_opts->c_srate,
+ sizeof(audio->params.c_srate));
+ audio->params.c_srate_active = audio_opts->c_srate_active;
audio->params.c_ssize = audio_opts->c_ssize;
audio->params.p_chmask = audio_opts->p_chmask;
- audio->params.p_srate = audio_opts->p_srate;
+ memcpy(audio->params.p_srate, audio_opts->p_srate,
+ sizeof(audio->params.p_srate));
+ audio->params.p_srate_active = audio_opts->p_srate_active;
audio->params.p_ssize = audio_opts->p_ssize;
audio->params.req_number = audio_opts->req_number;
@@ -625,13 +694,14 @@ static struct configfs_item_operations f_uac1_item_ops = {
UAC_ATTRIBUTE(c_chmask);
-UAC_ATTRIBUTE(c_srate);
UAC_ATTRIBUTE(c_ssize);
UAC_ATTRIBUTE(p_chmask);
-UAC_ATTRIBUTE(p_srate);
UAC_ATTRIBUTE(p_ssize);
UAC_ATTRIBUTE(req_number);
+UAC_RATE_ATTRIBUTE(p_srate);
+UAC_RATE_ATTRIBUTE(c_srate);
+
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac_opts_attr_c_chmask,
&f_uac_opts_attr_c_srate,
@@ -672,10 +742,12 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
&f_uac1_func_type);
opts->c_chmask = UAC_DEF_CCHMASK;
- opts->c_srate = UAC_DEF_CSRATE;
+ opts->c_srate[0] = UAC_DEF_CSRATE;
+ opts->c_srate_active = UAC_DEF_CSRATE;
opts->c_ssize = UAC_DEF_CSSIZE;
opts->p_chmask = UAC_DEF_PCHMASK;
- opts->p_srate = UAC_DEF_PSRATE;
+ opts->p_srate[0] = UAC_DEF_PSRATE;
+ opts->p_srate_active = UAC_DEF_PSRATE;
opts->p_ssize = UAC_DEF_PSSIZE;
opts->req_number = UAC_DEF_REQ_NUM;
return &opts->func_inst;
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index f5e93e168ec5..7d52a233d301 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -4,6 +4,7 @@
* Copyright (C) 2011
* Yadwinder Singh ([email protected])
* Jaswinder Singh ([email protected])
+ * Copyright (C) 2017 Julian Scheel <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -64,14 +65,11 @@ enum {
STR_AS_IN_ALT1,
};
-static char clksrc_in[8];
-static char clksrc_out[8];
-
static struct usb_string strings_fn[] = {
[STR_ASSOC].s = "Source/Sink",
[STR_IF_CTRL].s = "Topology Control",
- [STR_CLKSRC_IN].s = clksrc_in,
- [STR_CLKSRC_OUT].s = clksrc_out,
+ [STR_CLKSRC_IN].s = "Input clock",
+ [STR_CLKSRC_OUT].s = "Output clock",
[STR_USB_IT].s = "USBH Out",
[STR_IO_IT].s = "USBD Out",
[STR_USB_OT].s = "USBH In",
@@ -124,7 +122,7 @@ static struct uac_clock_source_descriptor in_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
.bClockID = USB_IN_CLK_ID,
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
- .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+ .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@@ -136,7 +134,7 @@ static struct uac_clock_source_descriptor out_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
.bClockID = USB_OUT_CLK_ID,
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
- .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+ .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@@ -428,26 +426,43 @@ struct cntrl_cur_lay3 {
};
struct cntrl_range_lay3 {
- __u16 wNumSubRanges;
__u32 dMIN;
__u32 dMAX;
__u32 dRES;
} __packed;
+#define ranges_size(c) (sizeof(c.wNumSubRanges) + c.wNumSubRanges \
+ * sizeof(struct cntrl_ranges_lay3))
+struct cntrl_ranges_lay3 {
+ __u16 wNumSubRanges;
+ struct cntrl_range_lay3 r[UAC_MAX_RATES];
+} __packed;
+
static void set_ep_max_packet_size(const struct f_uac_opts *uac2_opts,
struct usb_endpoint_descriptor *ep_desc,
unsigned int factor, bool is_playback)
{
- int chmask, srate, ssize;
+ int chmask, srate = 0, ssize;
u16 max_packet_size;
+ int i;
if (is_playback) {
chmask = uac2_opts->p_chmask;
- srate = uac2_opts->p_srate;
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (uac2_opts->p_srate[i] == 0)
+ break;
+ if (uac2_opts->p_srate[i] > srate)
+ srate = uac2_opts->p_srate[i];
+ }
ssize = uac2_opts->p_ssize;
} else {
chmask = uac2_opts->c_chmask;
- srate = uac2_opts->c_srate;
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (uac2_opts->c_srate[i] == 0)
+ break;
+ if (uac2_opts->c_srate[i] > srate)
+ srate = uac2_opts->c_srate[i];
+ }
ssize = uac2_opts->c_ssize;
}
@@ -502,9 +517,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize;
as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8;
- snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
- snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
-
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
@@ -568,10 +580,14 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->gadget = gadget;
agdev->params.p_chmask = uac2_opts->p_chmask;
- agdev->params.p_srate = uac2_opts->p_srate;
+ memcpy(agdev->params.p_srate, uac2_opts->p_srate,
+ sizeof(agdev->params.p_srate));
+ agdev->params.p_srate_active = uac2_opts->p_srate_active;
agdev->params.p_ssize = uac2_opts->p_ssize;
agdev->params.c_chmask = uac2_opts->c_chmask;
- agdev->params.c_srate = uac2_opts->c_srate;
+ memcpy(agdev->params.c_srate, uac2_opts->c_srate,
+ sizeof(agdev->params.c_srate));
+ agdev->params.c_srate_active = uac2_opts->c_srate_active;
agdev->params.c_ssize = uac2_opts->c_ssize;
agdev->params.req_number = uac2_opts->req_number;
ret = g_audio_setup(agdev, "UAC2 PCM", "UAC2_Gadget");
@@ -677,8 +693,8 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
int p_srate, c_srate;
opts = g_audio_to_uac_opts(agdev);
- p_srate = opts->p_srate;
- c_srate = opts->c_srate;
+ p_srate = opts->p_srate_active;
+ c_srate = opts->c_srate_active;
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
struct cntrl_cur_lay3 c;
@@ -689,6 +705,7 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
else if (entity_id == USB_OUT_CLK_ID)
c.dCUR = c_srate;
+ DBG(fn->config->cdev, "%s(): %d\n", __func__, c.dCUR);
value = min_t(unsigned, w_length, sizeof c);
memcpy(req->buf, &c, value);
} else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) {
@@ -714,28 +731,40 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
- struct cntrl_range_lay3 r;
+ struct cntrl_ranges_lay3 rs;
int value = -EOPNOTSUPP;
- int p_srate, c_srate;
+ int srate = 0;
+ int i;
opts = g_audio_to_uac_opts(agdev);
- p_srate = opts->p_srate;
- c_srate = opts->c_srate;
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
- if (entity_id == USB_IN_CLK_ID)
- r.dMIN = p_srate;
- else if (entity_id == USB_OUT_CLK_ID)
- r.dMIN = c_srate;
- else
- return -EOPNOTSUPP;
-
- r.dMAX = r.dMIN;
- r.dRES = 0;
- r.wNumSubRanges = 1;
+ rs.wNumSubRanges = 0;
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (entity_id == USB_IN_CLK_ID)
+ srate = opts->p_srate[i];
+ else if (entity_id == USB_OUT_CLK_ID)
+ srate = opts->c_srate[i];
+ else
+ return -EOPNOTSUPP;
+
+ if (srate == 0)
+ break;
+
+ rs.r[rs.wNumSubRanges].dMIN = srate;
+ rs.r[rs.wNumSubRanges].dMAX = srate;
+ rs.r[rs.wNumSubRanges].dRES = 0;
+ rs.wNumSubRanges++;
+ DBG(fn->config->cdev,
+ "%s(): clk %d: report rate %d. %d\n",
+ __func__, entity_id, rs.wNumSubRanges,
+ srate);
+ }
- value = min_t(unsigned, w_length, sizeof r);
- memcpy(req->buf, &r, value);
+ value = min_t(unsigned int, w_length, ranges_size(rs));
+ DBG(fn->config->cdev, "%s(): send %d rates, size %d\n",
+ __func__, rs.wNumSubRanges, value);
+ memcpy(req->buf, &rs, value);
} else {
dev_err(&agdev->gadget->dev,
"%s:%d control_selector=%d TODO!\n",
@@ -748,6 +777,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
static int
ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
+ DBG(fn->config->cdev, "%s(): %d\n", __func__, cr->bRequest);
if (cr->bRequest == UAC2_CS_CUR)
return in_rq_cur(fn, cr);
else if (cr->bRequest == UAC2_CS_RANGE)
@@ -756,15 +786,50 @@ ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
return -EOPNOTSUPP;
}
+static void uac2_cs_control_sam_freq(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usb_function *fn = ep->driver_data;
+ struct usb_composite_dev *cdev = fn->config->cdev;
+ struct g_audio *agdev = func_to_g_audio(fn);
+ struct f_uac *uac2 = func_to_uac(fn);
+ struct f_uac_opts *opts = g_audio_to_uac_opts(agdev);
+ u32 val;
+
+ if (req->actual != 4) {
+ WARN(cdev, "Invalid data size for UAC2_CS_CONTROL_SAM_FREQ.\n");
+ return;
+ }
+
+ val = le32_to_cpu(*((u32 *)req->buf));
+ if (uac2->ctl_id == USB_IN_CLK_ID) {
+ opts->p_srate_active = val;
+ u_audio_set_playback_srate(agdev, opts->p_srate_active);
+ } else if (uac2->ctl_id == USB_OUT_CLK_ID) {
+ opts->c_srate_active = val;
+ u_audio_set_capture_srate(agdev, opts->c_srate_active);
+ }
+}
+
static int
out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
+ struct usb_composite_dev *cdev = fn->config->cdev;
+ struct usb_request *req = cdev->req;
u16 w_length = le16_to_cpu(cr->wLength);
+ struct f_uac *uac2 = func_to_uac(fn);
u16 w_value = le16_to_cpu(cr->wValue);
+ u16 w_index = le16_to_cpu(cr->wIndex);
u8 control_selector = w_value >> 8;
+ u8 clock_id = w_index >> 8;
- if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
+ if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+ DBG(cdev, "control_selector UAC2_CS_CONTROL_SAM_FREQ, clock: %d\n",
+ clock_id);
+ cdev->gadget->ep0->driver_data = fn;
+ uac2->ctl_id = clock_id;
+ req->complete = uac2_cs_control_sam_freq;
return w_length;
+ }
return -EOPNOTSUPP;
}
@@ -829,13 +894,14 @@ static struct configfs_item_operations f_uac2_item_ops = {
};
UAC_ATTRIBUTE(p_chmask);
-UAC_ATTRIBUTE(p_srate);
UAC_ATTRIBUTE(p_ssize);
UAC_ATTRIBUTE(c_chmask);
-UAC_ATTRIBUTE(c_srate);
UAC_ATTRIBUTE(c_ssize);
UAC_ATTRIBUTE(req_number);
+UAC_RATE_ATTRIBUTE(p_srate);
+UAC_RATE_ATTRIBUTE(c_srate);
+
static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac_opts_attr_p_chmask,
&f_uac_opts_attr_p_srate,
@@ -876,10 +942,12 @@ static struct usb_function_instance *afunc_alloc_inst(void)
&f_uac2_func_type);
opts->p_chmask = UAC_DEF_PCHMASK;
- opts->p_srate = UAC_DEF_PSRATE;
+ opts->p_srate[0] = UAC_DEF_PSRATE;
+ opts->p_srate_active = UAC_DEF_PSRATE;
opts->p_ssize = UAC_DEF_PSSIZE;
opts->c_chmask = UAC_DEF_CCHMASK;
- opts->c_srate = UAC_DEF_CSRATE;
+ opts->c_srate[0] = UAC_DEF_CSRATE;
+ opts->c_srate_active = UAC_DEF_CSRATE;
opts->c_ssize = UAC_DEF_CSSIZE;
opts->req_number = UAC_DEF_REQ_NUM;
return &opts->func_inst;
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index bf4da4361b6d..6aef4aa9b18f 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -22,6 +22,7 @@
*/
#include <linux/module.h>
+#include <sound/control.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -286,18 +287,17 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct g_audio *audio_dev;
+ struct g_audio *audio_dev = uac->audio_dev;
struct uac_params *params;
int p_ssize, c_ssize;
int p_srate, c_srate;
int p_chmask, c_chmask;
- audio_dev = uac->audio_dev;
params = &audio_dev->params;
p_ssize = params->p_ssize;
c_ssize = params->c_ssize;
- p_srate = params->p_srate;
- c_srate = params->c_srate;
+ p_srate = params->p_srate_active;
+ c_srate = params->c_srate_active;
p_chmask = params->p_chmask;
c_chmask = params->c_chmask;
uac->p_residue = 0;
@@ -348,6 +348,52 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
+static int uac_pcm_rate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 324000;
+ return 0;
+}
+
+static int uac_pcm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_uac_chip *uac = snd_kcontrol_chip(kcontrol);
+ struct g_audio *audio_dev = uac->audio_dev;
+ struct uac_params *params = &audio_dev->params;
+
+ if (kcontrol->private_value == SNDRV_PCM_STREAM_CAPTURE)
+ ucontrol->value.integer.value[0] = params->c_srate_active;
+ else if (kcontrol->private_value == SNDRV_PCM_STREAM_PLAYBACK)
+ ucontrol->value.integer.value[0] = params->p_srate_active;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct snd_kcontrol_new uac_pcm_controls[] = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Capture Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = uac_pcm_rate_info,
+ .get = uac_pcm_rate_get,
+ .private_value = SNDRV_PCM_STREAM_CAPTURE,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Playback Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = uac_pcm_rate_info,
+ .get = uac_pcm_rate_get,
+ .private_value = SNDRV_PCM_STREAM_PLAYBACK,
+},
+};
+
/* ALSA cries without these function pointers */
static int uac_pcm_null(struct snd_pcm_substream *substream)
{
@@ -392,6 +438,59 @@ static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep)
dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
}
+static struct snd_kcontrol *u_audio_get_ctl(struct g_audio *audio_dev,
+ const char *name)
+{
+ struct snd_ctl_elem_id elem_id;
+
+ memset(&elem_id, 0, sizeof(elem_id));
+ elem_id.iface = SNDRV_CTL_ELEM_IFACE_PCM;
+ strcpy(elem_id.name, name);
+ return snd_ctl_find_id(audio_dev->uac->card, &elem_id);
+}
+
+int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate)
+{
+ struct snd_kcontrol *ctl = u_audio_get_ctl(audio_dev, "Capture Rate");
+ struct uac_params *params = &audio_dev->params;
+ int i;
+
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (params->c_srate[i] == srate) {
+ params->c_srate_active = srate;
+ snd_ctl_notify(audio_dev->uac->card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
+ return 0;
+ }
+ if (params->c_srate[i] == 0)
+ break;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(u_audio_set_capture_srate);
+
+int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
+{
+ struct snd_kcontrol *ctl = u_audio_get_ctl(audio_dev, "Playback Rate");
+ struct uac_params *params = &audio_dev->params;
+ int i;
+
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (params->p_srate[i] == srate) {
+ params->p_srate_active = srate;
+ snd_ctl_notify(audio_dev->uac->card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
+ return 0;
+ }
+ if (params->p_srate[i] == 0)
+ break;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(u_audio_set_playback_srate);
+
int u_audio_start_capture(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
@@ -456,6 +555,7 @@ int u_audio_start_playback(struct g_audio *audio_dev)
const struct usb_endpoint_descriptor *ep_desc;
int req_len, i;
+ dev_dbg(dev, "start playback with rate %d\n", params->p_srate_active);
ep = audio_dev->in_ep;
prm = &uac->p_prm;
config_ep_by_speed(gadget, &audio_dev->func, ep);
@@ -471,7 +571,7 @@ int u_audio_start_playback(struct g_audio *audio_dev)
/* pre-compute some values for iso_complete() */
uac->p_framesize = params->p_ssize *
num_channels(params->p_chmask);
- rate = params->p_srate * uac->p_framesize;
+ rate = params->p_srate_active * uac->p_framesize;
uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
uac->p_pktsize = min_t(unsigned int, rate / uac->p_interval,
prm->max_psize);
@@ -528,6 +628,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
struct uac_params *params;
int p_chmask, c_chmask;
int err;
+ int i;
if (!g_audio)
return -EINVAL;
@@ -617,6 +718,14 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX);
+ /* Add controls */
+ for (i = 0; i < ARRAY_SIZE(uac_pcm_controls); i++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&uac_pcm_controls[i], uac));
+ if (err < 0)
+ goto snd_fail;
+ }
+
err = snd_card_register(card);
if (!err)
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index 07e13784cbb8..3ba500a322dc 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -21,15 +21,18 @@
#include <linux/usb/composite.h>
+#define UAC_MAX_RATES 10
struct uac_params {
/* playback */
int p_chmask; /* channel mask */
- int p_srate; /* rate in Hz */
+ int p_srate[UAC_MAX_RATES]; /* rate in Hz */
+ int p_srate_active; /* selected rate in Hz */
int p_ssize; /* sample size */
/* capture */
int c_chmask; /* channel mask */
- int c_srate; /* rate in Hz */
+ int c_srate[UAC_MAX_RATES]; /* rate in Hz */
+ int c_srate_active; /* selected rate in Hz */
int c_ssize; /* sample size */
int req_number; /* number of preallocated requests */
@@ -91,5 +94,7 @@ int u_audio_start_capture(struct g_audio *g_audio);
void u_audio_stop_capture(struct g_audio *g_audio);
int u_audio_start_playback(struct g_audio *g_audio);
void u_audio_stop_playback(struct g_audio *g_audio);
+int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate);
+int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate);
#endif /* __U_AUDIO_H */
diff --git a/drivers/usb/gadget/function/u_uac.h b/drivers/usb/gadget/function/u_uac.h
index b0bc811b1ffb..8721f8f2d931 100644
--- a/drivers/usb/gadget/function/u_uac.h
+++ b/drivers/usb/gadget/function/u_uac.h
@@ -13,6 +13,7 @@
#define __U_UAC_H
#include <linux/usb/composite.h>
+#include "u_audio.h"
#define UAC_DEF_CCHMASK 0x3
#define UAC_DEF_CSRATE 48000
@@ -27,10 +28,12 @@
struct f_uac_opts {
struct usb_function_instance func_inst;
int c_chmask;
- int c_srate;
+ int c_srate[UAC_MAX_RATES];
+ int c_srate_active;
int c_ssize;
int p_chmask;
- int p_srate;
+ int p_srate[UAC_MAX_RATES];
+ int p_srate_active;
int p_ssize;
int req_number;
unsigned bound:1;
@@ -82,10 +85,71 @@ end: \
\
CONFIGFS_ATTR(f_uac_opts_, name)
+#define UAC_RATE_ATTRIBUTE(name) \
+static ssize_t f_uac_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac_opts *opts = to_f_uac_opts(item); \
+ int result = 0; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ page[0] = '\0'; \
+ for (i = 0; i < UAC_MAX_RATES; i++) { \
+ if (opts->name[i] == 0) \
+ continue; \
+ result += sprintf(page + strlen(page), "%u,", \
+ opts->name[i]); \
+ } \
+ if (strlen(page) > 0) \
+ page[strlen(page) - 1] = '\n'; \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac_opts *opts = to_f_uac_opts(item); \
+ char *split_page = NULL; \
+ int ret = -EINVAL; \
+ char *token; \
+ u32 num; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ i = 0; \
+ memset(opts->name, 0x00, sizeof(opts->name)); \
+ split_page = kstrdup(page, GFP_KERNEL); \
+ while ((token = strsep(&split_page, ",")) != NULL) { \
+ ret = kstrtou32(token, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ opts->name[i++] = num; \
+ opts->name##_active = num; \
+ ret = len; \
+ }; \
+ \
+end: \
+ kfree(split_page); \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac_opts_, name)
+
struct f_uac {
struct g_audio g_audio;
u8 ac_intf, as_in_intf, as_out_intf;
u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */
+ int ctl_id;
};
static inline struct f_uac *func_to_uac(struct usb_function *f)
--
2.13.2
Hi Julian,
[auto build test ERROR on balbi-usb/next]
[also build test ERROR on next-20170630]
[cannot apply to v4.12-rc7]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Julian-Scheel/USB-Audio-Gadget-Support-multiple-sampling-rates/20170702-215432
base: https://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git next
config: x86_64-randconfig-x012-201727 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
drivers/usb/gadget/function/usb_f_uac2.o: In function `f_uac_attr_release':
>> (.text+0x0): multiple definition of `f_uac_attr_release'
drivers/usb/gadget/function/usb_f_uac1.o:(.text+0x0): first defined here
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Julian,
[auto build test WARNING on balbi-usb/next]
[also build test WARNING on next-20170630]
[cannot apply to v4.12-rc7]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Julian-Scheel/USB-Audio-Gadget-Support-multiple-sampling-rates/20170702-215432
base: https://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git next
coccinelle warnings: (new ones prefixed by >>)
>> drivers/usb/gadget/legacy/audio.c:231:23-29: ERROR: application of sizeof to pointer
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
drivers/usb/gadget/legacy/audio.c:231:23-29: ERROR: application of sizeof to pointer
sizeof when applied to a pointer typed expression gives the size of
the pointer
Generated by: scripts/coccinelle/misc/noderef.cocci
Fixes: f95cee9b299f ("usb: gadget: f_uac*: Reduce code duplication")
CC: Julian Scheel <[email protected]>
Signed-off-by: Fengguang Wu <[email protected]>
---
audio.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -228,7 +228,7 @@ static int audio_bind(struct usb_composi
#endif
#if !defined(CONFIG_GADGET_UAC1) || !defined(CONFIG_GADGET_UAC1_LEGACY)
- memset(uac_opts, 0x0, sizeof(uac_opts));
+ memset(uac_opts, 0x0, sizeof(*uac_opts));
uac_opts->p_chmask = p_chmask;
uac_opts->p_srate[0] = p_srate;
uac_opts->p_srate_active = p_srate;