2017-06-26 07:35:20

by Julian Scheel

[permalink] [raw]
Subject: [PATCH 0/3] USB Audio Gadget: Support multiple sampling rates

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 | 116 +++++++-
drivers/usb/gadget/function/u_audio.h | 9 +-
drivers/usb/gadget/function/u_uac.h | 178 ++++++++++++
drivers/usb/gadget/function/u_uac1.h | 41 ---
drivers/usb/gadget/function/u_uac2.h | 44 ---
9 files changed, 582 insertions(+), 373 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.1


2017-06-26 07:35:28

by Julian Scheel

[permalink] [raw]
Subject: [PATCH 1/3] usb: gadget: f_uac1: Fix endpoint reading

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.1

2017-06-26 07:36:00

by Julian Scheel

[permalink] [raw]
Subject: [PATCH 2/3] usb: gadget: f_uac*: Reduce code duplication

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]>
---
drivers/usb/gadget/function/f_uac1.c | 142 +++++++++---------------------
drivers/usb/gadget/function/f_uac2.c | 161 ++++++++++-------------------------
drivers/usb/gadget/function/u_uac.h | 115 +++++++++++++++++++++++++
drivers/usb/gadget/function/u_uac1.h | 41 ---------
drivers/usb/gadget/function/u_uac2.h | 44 ----------
5 files changed, 199 insertions(+), 304 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..7e5a9bd46bcf 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -21,18 +21,9 @@
#include <linux/module.h>

#include "u_audio.h"
-#include "u_uac1.h"
+#include "u_uac.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);
-}
+#define UAC1_OUT_EP_MAX_PACKET_SIZE 200

/*
* DESCRIPTORS ... most are static, but strings and full
@@ -435,7 +426,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 +471,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 +489,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 +504,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 +621,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 +653,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 +673,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 +708,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_uac.h b/drivers/usb/gadget/function/u_uac.h
new file mode 100644
index 000000000000..d014fd117a45
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uac.h
@@ -0,0 +1,115 @@
+/*
+ * 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
+
+
+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);
+}
+
+static 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
--
2.13.1

2017-06-26 07:36:08

by Julian Scheel

[permalink] [raw]
Subject: [PATCH 3/3] usb: gadget: f_uac*: Support multiple sampling rates

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 | 116 +++++++++++++++-
drivers/usb/gadget/function/u_audio.h | 9 +-
drivers/usb/gadget/function/u_uac.h | 69 +++++++++-
7 files changed, 393 insertions(+), 79 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 7e5a9bd46bcf..6ae210a9ddfc 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
@@ -177,16 +178,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 */
@@ -210,14 +213,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 */
@@ -313,23 +316,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;

@@ -353,16 +390,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:
@@ -509,9 +564,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);

@@ -543,12 +597,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);
@@ -600,10 +665,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;

@@ -627,13 +696,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,
@@ -674,10 +744,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 5dd73b9e5172..20fbce04c90d 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,56 @@ 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;
+}
+
+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;
+}

int u_audio_start_capture(struct g_audio *audio_dev)
{
@@ -457,6 +553,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);
@@ -472,7 +569,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);
@@ -529,6 +626,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;
@@ -618,6 +716,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 d014fd117a45..e66900b3efaa 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
@@ -22,14 +23,15 @@
#define UAC_DEF_PSSIZE 2
#define UAC_DEF_REQ_NUM 2

-
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;
@@ -81,10 +83,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.1

2017-06-26 23:29:54

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 2/3] usb: gadget: f_uac*: Reduce code duplication

Hi Julian,

[auto build test ERROR on balbi-usb/next]
[also build test ERROR on next-20170626]
[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-gadget-f_uac1-Fix-endpoint-reading/20170626-202843
base: https://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git next
config: arm-imx_v6_v7_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=arm

All errors (new ones prefixed by >>):

>> drivers/usb/gadget/legacy/audio.c:24:20: fatal error: u_uac2.h: No such file or directory
#include "u_uac2.h"
^
compilation terminated.

vim +24 drivers/usb/gadget/legacy/audio.c

c6994e6f drivers/usb/gadget/audio.c Bryan Wu 2009-06-03 18 #define DRIVER_DESC "Linux USB Audio Gadget"
132fcb46 drivers/usb/gadget/audio.c Jassi Brar 2012-02-02 19 #define DRIVER_VERSION "Feb 2, 2012"
c6994e6f drivers/usb/gadget/audio.c Bryan Wu 2009-06-03 20
7d16e8d3 drivers/usb/gadget/audio.c Sebastian Andrzej Siewior 2012-09-10 21 USB_GADGET_COMPOSITE_OPTIONS();
c6994e6f drivers/usb/gadget/audio.c Bryan Wu 2009-06-03 22
0591bc23 drivers/usb/gadget/legacy/audio.c Ruslan Bilovol 2017-06-18 23 #ifndef CONFIG_GADGET_UAC1
065a107c drivers/usb/gadget/legacy/audio.c Andrzej Pietrasiewicz 2014-07-22 @24 #include "u_uac2.h"
065a107c drivers/usb/gadget/legacy/audio.c Andrzej Pietrasiewicz 2014-07-22 25
ad94ac0c drivers/usb/gadget/legacy/audio.c Andrzej Pietrasiewicz 2014-07-22 26 /* Playback(USB-IN) Default Stereo - Fl/Fr */
065a107c drivers/usb/gadget/legacy/audio.c Andrzej Pietrasiewicz 2014-07-22 27 static int p_chmask = UAC2_DEF_PCHMASK;

:::::: The code at line 24 was first introduced by commit
:::::: 065a107cdd70f0621011424009b3ecd4e42481b1 usb: gadget: f_uac2: use defined constants as defaults

:::::: TO: Andrzej Pietrasiewicz <[email protected]>
:::::: CC: Felipe Balbi <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (2.49 kB)
.config.gz (31.25 kB)
Download all attachments

2018-06-30 18:18:01

by Eugeniu Rosca

[permalink] [raw]
Subject: Re: [PATCHv2 3/3] usb: gadget: f_uac*: Support multiple sampling rates

Hello Julian,

Would it be possible to revive the uac2 multiple sampling rate
patch-set [1] by rebasing it onto the most recent kernel? If you
don't have time for this, I could help you.

Hello Felipe,

Would you please give the green light to start working on this feature,
meaning that you will accept it once enough review/testing is done?

Best regards,
Eugeniu.

[1] https://lkml.org/lkml/2017/6/30/276 ("[PATCHv2 0/3] USB Audio Gadget: Support multiple sampling rates")

2018-07-02 14:18:58

by Julian Scheel

[permalink] [raw]
Subject: Re: [PATCHv2 3/3] usb: gadget: f_uac*: Support multiple sampling rates

Hi Eugeniu,

On 30.06.2018 20:16, Eugeniu Rosca wrote:
> Would it be possible to revive the uac2 multiple sampling rate
> patch-set [1] by rebasing it onto the most recent kernel? If you
> don't have time for this, I could help you.

I have this on my todo-list for a while now... In fact I fixed the build
errors reported by the robot last year, but didn't had the time to
verify all of it and push again.
Still, I'd be happy to get this merged, so I'll try to check the state
of this within the next days and either post to the list or get back to
you if more work needs to be done.

Best,
Julian

2018-07-02 16:31:28

by Eugeniu Rosca

[permalink] [raw]
Subject: Re: [PATCHv2 3/3] usb: gadget: f_uac*: Support multiple sampling rates

Hi Julian,

[CC:Takashi, since we are discussing sound-related parts of USB]

On Mon, Jul 02, 2018 at 04:07:19PM +0200, Julian Scheel wrote:
> Hi Eugeniu,
>
> On 30.06.2018 20:16, Eugeniu Rosca wrote:
> > Would it be possible to revive the uac2 multiple sampling rate
> > patch-set [1] by rebasing it onto the most recent kernel? If you
> > don't have time for this, I could help you.
>
> I have this on my todo-list for a while now... In fact I fixed the build
> errors reported by the robot last year, but didn't had the time to
> verify all of it and push again.
> Still, I'd be happy to get this merged, so I'll try to check the state
> of this within the next days and either post to the list or get back to
> you if more work needs to be done.

We've been living with an internally developed uac2 multiple rate
support since June 2015, initially written on top of v3.14. Due to
significant refactoring of uac2 driver brought by v4.13 commit [1], I
went through the comparison between the in-house implementation (which
no more applied cleanly to post-[1] vanilla) and your proposal from [2].

I found a lot things in common, but also a number of differences. The
latter were caused, as example, by internal requirements like:

---<-snip->---
When enabling multiple rates, the application must be aware of this and
needs to reconfigure the device according to the current assigned rate.
ALSA hw parameter refinement is dynamically limited to the actual
assigned rate, the application does not need to close the device on rate
change but just need to restart configuration (snd_pcm_hw_free(),
snd_pcm_hw_params_any()...). snd_hw_params_any() will always provide
the actual assigned rate.
---<-snip->---

The above frees the need for amixer notifications. A PoC/dirty patch
which makes `arecord` work according to this principle is shown in [3].
The way it was tested on our side is:

prereq > connect USB gadget port of H3-Salvator-X to Ubuntu PC
target > create uac2 gadget (scripted)
target > aplay -C -c 2 -r 48000 -f S16_LE -D plughw:UAC2Gadget \
| aplay -c 2 -r 48000 -f S16_LE -D entertainment_main
host > ls -1 sample-*.wav
sample-11025_hz.wav
sample-16000_hz.wav
sample-22000_hz.wav
sample-32000_hz.wav
sample-44100_hz.wav
sample-48000_hz.wav
sample-8000_hz.wav
sample-96000_hz.wav
host > aplay -D hw:<gadget-card-name> -c 2 sample-*.wav
target > Playback of all files is perceived on the target
(alternatively, the gadget stream can be recorded and compared
to original wav files).

Another internal requirement defines the exact behavior of the
target/gadget, depending on the moment at which the host requests
the sample rate change (from gadget point of view):

---<-snip->---
- If rate changes while refinement is not finished, the call
to snd_pcm_prepare() will fail and user has to restart again.
- If rate changes after prepare is done, but stream is not running,
the call to snd_pcm_start() will fail and user has to restart again.
- If rate changes while stream is running, it will be signaled as
xrun (after providing pending frames on capture, as described above).
---<-snip->---

Due to these mentioned requirements, our final solution was a mix
between your and our approaches. I hope we can reach some
understanding what is best suitable for the regular users, so that at
least some core part of the feature is accepted in mainline.

Any comments from Takashi would be greatly appreciated.
I look forward for your re-based patches. Thank you.

Best regards,
Eugeniu.

[1] eb9fecb9e69b ("usb: gadget: f_uac2: split out audio core")
[2] https://lkml.org/lkml/2017/6/26/91 ("[PATCH 0/3] USB Audio Gadget: \
Support multiple sampling rates")
[3] https://github.com/erosca/alsa-utils/commit/uac2_test

2018-07-02 22:30:53

by Ruslan Bilovol

[permalink] [raw]
Subject: Re: [PATCHv2 3/3] usb: gadget: f_uac*: Support multiple sampling rates

Hi guys,

On Mon, Jul 2, 2018 at 7:30 PM, Eugeniu Rosca <[email protected]> wrote:
> Hi Julian,
>
> [CC:Takashi, since we are discussing sound-related parts of USB]
>
> On Mon, Jul 02, 2018 at 04:07:19PM +0200, Julian Scheel wrote:
>> Hi Eugeniu,
>>
>> On 30.06.2018 20:16, Eugeniu Rosca wrote:
>> > Would it be possible to revive the uac2 multiple sampling rate
>> > patch-set [1] by rebasing it onto the most recent kernel? If you
>> > don't have time for this, I could help you.
>>
>> I have this on my todo-list for a while now... In fact I fixed the build
>> errors reported by the robot last year, but didn't had the time to
>> verify all of it and push again.
>> Still, I'd be happy to get this merged, so I'll try to check the state
>> of this within the next days and either post to the list or get back to
>> you if more work needs to be done.
>
> We've been living with an internally developed uac2 multiple rate
> support since June 2015, initially written on top of v3.14. Due to
> significant refactoring of uac2 driver brought by v4.13 commit [1], I
> went through the comparison between the in-house implementation (which
> no more applied cleanly to post-[1] vanilla) and your proposal from [2].
>

When Julian posted his patches, I've been working on UAC3 gadget
implementation which I posted later [1]. While I originally tried to make
UAC3 function to have same configfs files as UAC1/2, now, preparing
UAC3 gadget patch I see it doesn't fit this approach, and patch
"usb: gadget: f_uac*: Reduce code duplication" isn't applicable for UAC3
case. Especially for channels configuration which in new spec can be
done only through clusters descriptions, which makes configfs interface
more complicated and not so straightforward as UAC1/2 have.

I didn't finish my UAC3 gadget patch v2 yet, but if you can try to avoid
adding patch "Reduce code duplication" or wait for a few weeks, when
I'll post UAC3 patches, it would be great; so we will be able to take into
account new spec as well.

[1]: https://lkml.org/lkml/2017/11/6/1514

Thanks,
Ruslan

2018-07-05 23:08:29

by Eugeniu Rosca

[permalink] [raw]
Subject: Re: [PATCHv2 3/3] usb: gadget: f_uac*: Support multiple sampling rates

On Tue, Jul 03, 2018 at 01:29:54AM +0300, Ruslan Bilovol wrote:
> Hi guys,
>
> On Mon, Jul 2, 2018 at 7:30 PM, Eugeniu Rosca <[email protected]> wrote:
> > Hi Julian,
> >
> > [CC:Takashi, since we are discussing sound-related parts of USB]
> >
> > On Mon, Jul 02, 2018 at 04:07:19PM +0200, Julian Scheel wrote:
> >> Hi Eugeniu,
> >>
> >> On 30.06.2018 20:16, Eugeniu Rosca wrote:
> >> > Would it be possible to revive the uac2 multiple sampling rate
> >> > patch-set [1] by rebasing it onto the most recent kernel? If you
> >> > don't have time for this, I could help you.
> >>
> >> I have this on my todo-list for a while now... In fact I fixed the build
> >> errors reported by the robot last year, but didn't had the time to
> >> verify all of it and push again.
> >> Still, I'd be happy to get this merged, so I'll try to check the state
> >> of this within the next days and either post to the list or get back to
> >> you if more work needs to be done.
> >
> > We've been living with an internally developed uac2 multiple rate
> > support since June 2015, initially written on top of v3.14. Due to
> > significant refactoring of uac2 driver brought by v4.13 commit [1], I
> > went through the comparison between the in-house implementation (which
> > no more applied cleanly to post-[1] vanilla) and your proposal from [2].
> >
>
> When Julian posted his patches, I've been working on UAC3 gadget
> implementation which I posted later [1]. While I originally tried to make
> UAC3 function to have same configfs files as UAC1/2, now, preparing
> UAC3 gadget patch I see it doesn't fit this approach, and patch
> "usb: gadget: f_uac*: Reduce code duplication" isn't applicable for UAC3
> case. Especially for channels configuration which in new spec can be
> done only through clusters descriptions, which makes configfs interface
> more complicated and not so straightforward as UAC1/2 have.
>
> I didn't finish my UAC3 gadget patch v2 yet, but if you can try to avoid
> adding patch "Reduce code duplication" or wait for a few weeks, when
> I'll post UAC3 patches, it would be great; so we will be able to take into
> account new spec as well.
>
> [1]: https://lkml.org/lkml/2017/11/6/1514

Hello Ruslan, hello Julian,

I took some time to study the implementations of existing uac drivers,
including uac3 v1 [1] and would like to share some thoughts with you.

My main thesis is that, w/o going too deep into the semantics and specs,
there is just a lot of code shared between the drivers. Why I am
pointing this out is because features like [2] and possibly a number of
other upcoming features which are agnostic on the exact UAC spec (just
like multiple rate support is) will need a copy-paste (and individual
maintenance) in every single UAC{1,2,3} driver.

To quantify the degree of similarity (the percentage of common
code), I took the uac2 driver as reference and compared it to uac1
and to uac3 implementations.

Some comments/assumptions:
- To get relevant diff output between the drivers, I needed to reorder
the functions some *.c files slightly (this already can be a source
of bugs, since contributors can't see the difference between the
drivers clearly).
- To highlight as much common code as possible, I needed to reorder
statements inside functions (some functions performing the same role
are doing things in slightly different order, which again can be
source of subtle bugs).
- I used v4.18-rc3, on top of which patch [1] applies cleanly.
- I only compared *.c code (functions and macros), not data structs.
- I ignored the differences in the names of local variables.
- I ignored the differences in the prints.
- I considered 'struct f_uac{1,2,3}_opts' identical, since they
really are.
- I ignored the difference between 'struct f_uac{1,2,3}' since they
can be easily unified.
- I ignored the values behind the 'UAC{1,2,3}_DEF_*' macros, since they
are not essential.
- Obviously, I ignored any whitespace difference.
- I paired the functions/macros by role.
- The actual % values are estimated *visually*, therefore might be not
precise (unless it is a 0% or 100%).

The results are drawn in the following two tables.

###### UAC2 vs UAC1

| uac2 | % | uac1 |
|------------------------|-----|------------------------|
| afunc_bind | 50 | f_audio_bind |
| afunc_unbind | 100 | f_audio_unbind |
| afunc_set_alt | 100 | f_audio_set_alt |
| afunc_get_alt | 100 | f_audio_get_alt |
| afunc_disable | 100 | f_audio_disable |
| in_rq_cur | 0 | - |
| in_rq_range | 0 | - |
| out_rq_cur | 0 | - |
| ac_rq_in | 0 | - |
| setup_rq_inf | 0 | - |
| afunc_setup | 50 | f_audio_setup |
| to_f_uac2_opts | 100 | to_f_uac1_opts |
| f_uac2_attr_release | 100 | f_uac1_attr_release |
| UAC2_ATTRIBUTE | 100 | UAC1_ATTRIBUTE |
| afunc_free_inst | 100 | f_audio_free_inst |
| afunc_alloc_inst | 100 | f_audio_alloc_inst |
| afunc_free | 100 | f_audio_free |
| afunc_alloc | 100 | f_audio_alloc |
| set_ep_max_packet_size | 0 | - |
| - | 0 | audio_get_endpoint_req |
| - | 0 | audio_set_endpoint_req |

###### UAC2 vs UAC3

| uac2 | % | uac3 |
|------------------------|-----|--------------------------|
| afunc_bind | 50 | f_audio_bind |
| afunc_unbind | 50 | f_audio_unbind |
| afunc_set_alt | 100 | f_audio_set_alt |
| afunc_get_alt | 100 | f_audio_get_alt |
| afunc_disable | 100 | f_audio_disable |
| in_rq_cur | 30 | in_rq_cur |
| in_rq_range | 30 | in_rq_range |
| out_rq_cur | 20 | out_rq_cur |
| ac_rq_in | 50 | ac_rq_in |
| setup_rq_inf | 90 | setup_rq_inf |
| afunc_setup | 100 | f_audio_setup |
| to_f_uac2_opts | 100 | to_f_uac3_opts |
| f_uac2_attr_release | 100 | f_uac3_attr_release |
| UAC2_ATTRIBUTE | 100 | UAC3_ATTRIBUTE |
| afunc_free_inst | 100 | f_audio_free_inst |
| afunc_alloc_inst | 100 | f_audio_alloc_inst |
| afunc_free | 100 | f_audio_free |
| afunc_alloc | 100 | f_audio_alloc |
| set_ep_max_packet_size | 100 | set_ep_max_packet_size |
| - | 0 | in_rq_hc_desc |
| - | 0 | uac3_copy_descriptors |
| - | 0 | alloc_fu_desc |
| - | 0 | build_cluster_descriptor |
| - | 0 | UAC3_ATTRIBUTE_CHMASK |

The message I wanted to share here is that, in my opinion, there is
really a lot of common code across UAC drivers. AFAIK features like
multiple sampling rate support really don't care about the exact UAC
spec, which means that w/o any attempt to reduce code duplication, we
would end up with duplicated (and diverging over time) implementations
of such features in several places.

Can I have your view on that?
Since you likely have more experience with uac* code, do you think is
feasible to isolate the common parts into a separate file?

Thanks,
Eugeniu.

[1] https://lkml.org/lkml/2017/11/6/1514 ("[PATCH 1/1]
usb: gadget: add USB Audio Device Class 3.0 gadget support")
[2] https://lkml.org/lkml/2017/6/30/276 ("[PATCHv2 0/3]
USB Audio Gadget: Support multiple sampling rates")
[3] https://lkml.org/lkml/2017/6/30/270 ("[PATCHv2 2/3]
usb: gadget: f_uac*: Reduce code duplication")