* [PATCH v4 01/10] usb: gadget:audio: Replace deprecated macro S_IRUGO
2022-01-10 7:37 [PATCH v4 00/10] usb: gadget: audio: Multiple rates, notify Pavel Hofman
@ 2022-01-10 7:37 ` Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 02/10] usb: gadget: u_audio: Support multiple sampling rates Pavel Hofman
` (8 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pavel Hofman @ 2022-01-10 7:37 UTC (permalink / raw)
To: linux-usb
Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
Julian Scheel, John Keeping, Greg Kroah-Hartman
Use octal digits as suggested by checkpatch instead of the deprecated
macro.
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
v3: new patch
---
drivers/usb/gadget/legacy/audio.c | 36 +++++++++++++++----------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index a748ed0842e8..5ec477ffab7f 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -22,32 +22,32 @@ USB_GADGET_COMPOSITE_OPTIONS();
/* Playback(USB-IN) Default Stereo - Fl/Fr */
static int p_chmask = UAC2_DEF_PCHMASK;
-module_param(p_chmask, uint, S_IRUGO);
+module_param(p_chmask, uint, 0444);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
static int p_srate = UAC2_DEF_PSRATE;
-module_param(p_srate, uint, S_IRUGO);
+module_param(p_srate, uint, 0444);
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
/* Playback Default 16bits/sample */
static int p_ssize = UAC2_DEF_PSSIZE;
-module_param(p_ssize, uint, S_IRUGO);
+module_param(p_ssize, uint, 0444);
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
static int c_chmask = UAC2_DEF_CCHMASK;
-module_param(c_chmask, uint, S_IRUGO);
+module_param(c_chmask, uint, 0444);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 64 KHz */
static int c_srate = UAC2_DEF_CSRATE;
-module_param(c_srate, uint, S_IRUGO);
+module_param(c_srate, uint, 0444);
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_param(c_ssize, uint, 0444);
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
@@ -55,58 +55,58 @@ MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
/* Playback(USB-IN) Default Stereo - Fl/Fr */
static int p_chmask = UAC1_DEF_PCHMASK;
-module_param(p_chmask, uint, S_IRUGO);
+module_param(p_chmask, uint, 0444);
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_param(p_srate, uint, 0444);
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_param(p_ssize, uint, 0444);
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_param(c_chmask, uint, 0444);
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_param(c_srate, uint, 0444);
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
/* Capture Default 16bits/sample */
static int c_ssize = UAC1_DEF_CSSIZE;
-module_param(c_ssize, uint, S_IRUGO);
+module_param(c_ssize, uint, 0444);
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
#else /* CONFIG_GADGET_UAC1_LEGACY */
#include "u_uac1_legacy.h"
static char *fn_play = FILE_PCM_PLAYBACK;
-module_param(fn_play, charp, S_IRUGO);
+module_param(fn_play, charp, 0444);
MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
static char *fn_cap = FILE_PCM_CAPTURE;
-module_param(fn_cap, charp, S_IRUGO);
+module_param(fn_cap, charp, 0444);
MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
static char *fn_cntl = FILE_CONTROL;
-module_param(fn_cntl, charp, S_IRUGO);
+module_param(fn_cntl, charp, 0444);
MODULE_PARM_DESC(fn_cntl, "Control device file name");
static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
-module_param(req_buf_size, int, S_IRUGO);
+module_param(req_buf_size, int, 0444);
MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
static int req_count = UAC1_REQ_COUNT;
-module_param(req_count, int, S_IRUGO);
+module_param(req_count, int, 0444);
MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
static int audio_buf_size = UAC1_AUDIO_BUF_SIZE;
-module_param(audio_buf_size, int, S_IRUGO);
+module_param(audio_buf_size, int, 0444);
MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
#endif /* CONFIG_GADGET_UAC1_LEGACY */
#endif
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 02/10] usb: gadget: u_audio: Support multiple sampling rates
2022-01-10 7:37 [PATCH v4 00/10] usb: gadget: audio: Multiple rates, notify Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 01/10] usb: gadget:audio: Replace deprecated macro S_IRUGO Pavel Hofman
@ 2022-01-10 7:37 ` Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 03/10] usb: gadget: u_audio: Move dynamic srate from params to rtd Pavel Hofman
` (7 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pavel Hofman @ 2022-01-10 7:37 UTC (permalink / raw)
To: linux-usb
Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
Julian Scheel, John Keeping, Greg Kroah-Hartman
From: Julian Scheel <julian@jusst.de>
Implement support for multiple sampling rates in u_audio part of the
audio gadget. The currently configured rates are exposed through
read-only amixer controls 'Capture Rate' and 'Playback Rate'.
Signed-off-by: Julian Scheel <julian@jusst.de>
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
v3: removed unused variables in u_audio.c
---
drivers/usb/gadget/function/f_uac1.c | 2 +
drivers/usb/gadget/function/f_uac2.c | 2 +
drivers/usb/gadget/function/u_audio.c | 131 +++++++++++++++++++++++
drivers/usb/gadget/function/u_audio.h | 10 +-
drivers/usb/gadget/function/uac_common.h | 9 ++
5 files changed, 152 insertions(+), 2 deletions(-)
create mode 100644 drivers/usb/gadget/function/uac_common.h
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 03f50643fbba..ccb0e4f41e5d 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -1298,6 +1298,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
audio->params.c_chmask = audio_opts->c_chmask;
audio->params.c_srate = audio_opts->c_srate;
+ audio->params.c_srates[0] = audio_opts->c_srate;
audio->params.c_ssize = audio_opts->c_ssize;
if (FUIN_EN(audio_opts)) {
audio->params.p_fu.id = USB_IN_FU_ID;
@@ -1310,6 +1311,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
}
audio->params.p_chmask = audio_opts->p_chmask;
audio->params.p_srate = audio_opts->p_srate;
+ audio->params.p_srates[0] = audio_opts->p_srate;
audio->params.p_ssize = audio_opts->p_ssize;
if (FUOUT_EN(audio_opts)) {
audio->params.c_fu.id = USB_OUT_FU_ID;
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 36fa6ef0581b..1334691073a0 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1210,6 +1210,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->params.p_chmask = uac2_opts->p_chmask;
agdev->params.p_srate = uac2_opts->p_srate;
+ agdev->params.p_srates[0] = uac2_opts->p_srate;
agdev->params.p_ssize = uac2_opts->p_ssize;
if (FUIN_EN(uac2_opts)) {
agdev->params.p_fu.id = USB_IN_FU_ID;
@@ -1221,6 +1222,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
}
agdev->params.c_chmask = uac2_opts->c_chmask;
agdev->params.c_srate = uac2_opts->c_srate;
+ agdev->params.c_srates[0] = uac2_opts->c_srate;
agdev->params.c_ssize = uac2_opts->c_ssize;
if (FUOUT_EN(uac2_opts)) {
agdev->params.c_fu.id = USB_OUT_FU_ID;
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 4561d7a183ff..50ccb36d22d7 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -32,6 +32,7 @@ enum {
UAC_P_PITCH_CTRL,
UAC_MUTE_CTRL,
UAC_VOLUME_CTRL,
+ UAC_RATE_CTRL,
};
/* Runtime data params for one stream */
@@ -62,6 +63,8 @@ struct uac_rtd_params {
s16 volume;
int mute;
+ struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */
+
spinlock_t lock; /* lock for control transfers */
};
@@ -493,6 +496,44 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep)
dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
}
+int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate)
+{
+ struct uac_params *params = &audio_dev->params;
+ int i;
+
+ dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (params->c_srates[i] == srate) {
+ params->c_srate = srate;
+ return 0;
+ }
+ if (params->c_srates[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 uac_params *params = &audio_dev->params;
+ int i;
+
+ dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (params->p_srates[i] == srate) {
+ params->p_srate = srate;
+ return 0;
+ }
+ if (params->p_srates[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;
@@ -504,6 +545,7 @@ int u_audio_start_capture(struct g_audio *audio_dev)
struct uac_params *params = &audio_dev->params;
int req_len, i;
+ dev_dbg(dev, "start capture with rate %d\n", params->c_srate);
ep = audio_dev->out_ep;
prm = &uac->c_prm;
config_ep_by_speed(gadget, &audio_dev->func, ep);
@@ -596,6 +638,7 @@ int u_audio_start_playback(struct g_audio *audio_dev)
int req_len, i;
unsigned int p_pktsize;
+ dev_dbg(dev, "start playback with rate %d\n", params->p_srate);
ep = audio_dev->in_ep;
prm = &uac->p_prm;
config_ep_by_speed(gadget, &audio_dev->func, ep);
@@ -943,6 +986,68 @@ static int u_audio_volume_put(struct snd_kcontrol *kcontrol,
return change;
}
+static int get_max_srate(const int *srates)
+{
+ int i, max_srate = 0;
+
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (srates[i] == 0)
+ break;
+ if (srates[i] > max_srate)
+ max_srate = srates[i];
+ }
+ return max_srate;
+}
+
+static int get_min_srate(const int *srates)
+{
+ int i, min_srate = INT_MAX;
+
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (srates[i] == 0)
+ break;
+ if (srates[i] < min_srate)
+ min_srate = srates[i];
+ }
+ return min_srate;
+}
+
+static int u_audio_rate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const int *srates;
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+ struct snd_uac_chip *uac = prm->uac;
+ struct g_audio *audio_dev = uac->audio_dev;
+ struct uac_params *params = &audio_dev->params;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+
+ if (prm == &uac->c_prm)
+ srates = params->c_srates;
+ else
+ srates = params->p_srates;
+ uinfo->value.integer.min = get_min_srate(srates);
+ uinfo->value.integer.max = get_max_srate(srates);
+ return 0;
+}
+
+static int u_audio_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+ struct snd_uac_chip *uac = prm->uac;
+ struct g_audio *audio_dev = uac->audio_dev;
+ struct uac_params *params = &audio_dev->params;
+
+ if (prm == &uac->c_prm)
+ ucontrol->value.integer.value[0] = params->c_srate;
+ else
+ ucontrol->value.integer.value[0] = params->p_srate;
+
+ return 0;
+}
static struct snd_kcontrol_new u_audio_controls[] = {
[UAC_FBACK_CTRL] {
@@ -973,6 +1078,13 @@ static struct snd_kcontrol_new u_audio_controls[] = {
.get = u_audio_volume_get,
.put = u_audio_volume_put,
},
+ [UAC_RATE_CTRL] {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "", /* will be filled later */
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = u_audio_rate_info,
+ .get = u_audio_rate_get,
+ },
};
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
@@ -1186,6 +1298,25 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
prm->volume_min = fu->volume_min;
prm->volume_res = fu->volume_res;
}
+
+ /* Add rate control */
+ snprintf(ctrl_name, sizeof(ctrl_name),
+ "%s Rate", direction);
+ u_audio_controls[UAC_RATE_CTRL].name = ctrl_name;
+
+ kctl = snd_ctl_new1(&u_audio_controls[UAC_RATE_CTRL], prm);
+ if (!kctl) {
+ err = -ENOMEM;
+ goto snd_fail;
+ }
+
+ kctl->id.device = pcm->device;
+ kctl->id.subdevice = 0;
+
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ goto snd_fail;
+ prm->snd_kctl_rate = kctl;
}
strscpy(card->driver, card_name, sizeof(card->driver));
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index 8dfdae1721cd..76b5b8169444 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -10,6 +10,7 @@
#define __U_AUDIO_H
#include <linux/usb/composite.h>
+#include "uac_common.h"
/*
* Same maximum frequency deviation on the slower side as in
@@ -40,13 +41,15 @@ struct uac_fu_params {
struct uac_params {
/* playback */
int p_chmask; /* channel mask */
- int p_srate; /* rate in Hz */
+ int p_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
+ int p_srate; /* selected rate in Hz */
int p_ssize; /* sample size */
struct uac_fu_params p_fu; /* Feature Unit parameters */
/* capture */
int c_chmask; /* channel mask */
- int c_srate; /* rate in Hz */
+ int c_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
+ int c_srate; /* selected rate in Hz */
int c_ssize; /* sample size */
struct uac_fu_params c_fu; /* Feature Unit parameters */
@@ -117,6 +120,9 @@ 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);
+
int u_audio_get_volume(struct g_audio *g_audio, int playback, s16 *val);
int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val);
int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val);
diff --git a/drivers/usb/gadget/function/uac_common.h b/drivers/usb/gadget/function/uac_common.h
new file mode 100644
index 000000000000..3ecf89d6e814
--- /dev/null
+++ b/drivers/usb/gadget/function/uac_common.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ */
+
+#ifndef UAC_COMMON_H
+#define UAC_COMMON_H
+
+#define UAC_MAX_RATES 10 /* maximum number of rates configurable by f_uac1/2 */
+#endif
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 03/10] usb: gadget: u_audio: Move dynamic srate from params to rtd
2022-01-10 7:37 [PATCH v4 00/10] usb: gadget: audio: Multiple rates, notify Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 01/10] usb: gadget:audio: Replace deprecated macro S_IRUGO Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 02/10] usb: gadget: u_audio: Support multiple sampling rates Pavel Hofman
@ 2022-01-10 7:37 ` Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 04/10] usb: gadget: u_audio: Add capture/playback srate getter Pavel Hofman
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pavel Hofman @ 2022-01-10 7:37 UTC (permalink / raw)
To: linux-usb
Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
Julian Scheel, John Keeping, Greg Kroah-Hartman
Parameters uac_params.p_srate/c_srate are dynamic now and are not part
of parametric configuration anymore. Move them to the
runtime struct uac_rtd_params for each stream.
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
Suggested-by: John Keeping <john@metanate.com>
---
v3: new patch
v4: added Suggested-by to commit msg
---
drivers/usb/gadget/function/f_uac1.c | 2 -
drivers/usb/gadget/function/f_uac2.c | 2 -
drivers/usb/gadget/function/u_audio.c | 68 ++++++++++++++-------------
drivers/usb/gadget/function/u_audio.h | 4 +-
4 files changed, 38 insertions(+), 38 deletions(-)
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index ccb0e4f41e5d..0397b27df42e 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -1297,7 +1297,6 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
audio->params.c_chmask = audio_opts->c_chmask;
- audio->params.c_srate = audio_opts->c_srate;
audio->params.c_srates[0] = audio_opts->c_srate;
audio->params.c_ssize = audio_opts->c_ssize;
if (FUIN_EN(audio_opts)) {
@@ -1310,7 +1309,6 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->params.p_fu.volume_res = audio_opts->p_volume_res;
}
audio->params.p_chmask = audio_opts->p_chmask;
- audio->params.p_srate = audio_opts->p_srate;
audio->params.p_srates[0] = audio_opts->p_srate;
audio->params.p_ssize = audio_opts->p_ssize;
if (FUOUT_EN(audio_opts)) {
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 1334691073a0..e518f210968c 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1209,7 +1209,6 @@ 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;
agdev->params.p_srates[0] = uac2_opts->p_srate;
agdev->params.p_ssize = uac2_opts->p_ssize;
if (FUIN_EN(uac2_opts)) {
@@ -1221,7 +1220,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->params.p_fu.volume_res = uac2_opts->p_volume_res;
}
agdev->params.c_chmask = uac2_opts->c_chmask;
- agdev->params.c_srate = uac2_opts->c_srate;
agdev->params.c_srates[0] = uac2_opts->c_srate;
agdev->params.c_ssize = uac2_opts->c_ssize;
if (FUOUT_EN(uac2_opts)) {
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 50ccb36d22d7..dce894dcae07 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -64,6 +64,7 @@ struct uac_rtd_params {
int mute;
struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */
+ int srate; /* selected samplerate */
spinlock_t lock; /* lock for control transfers */
@@ -153,8 +154,6 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
struct snd_pcm_runtime *runtime;
struct uac_rtd_params *prm = req->context;
struct snd_uac_chip *uac = prm->uac;
- struct g_audio *audio_dev = uac->audio_dev;
- struct uac_params *params = &audio_dev->params;
unsigned int frames, p_pktsize;
unsigned long long pitched_rate_mil, p_pktsize_residue_mil,
residue_frames_mil, div_result;
@@ -199,15 +198,14 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
*/
unsigned long long p_interval_mil = uac->p_interval * 1000000ULL;
- pitched_rate_mil = (unsigned long long)
- params->p_srate * prm->pitch;
+ pitched_rate_mil = (unsigned long long) prm->srate * prm->pitch;
div_result = pitched_rate_mil;
do_div(div_result, uac->p_interval);
do_div(div_result, 1000000);
frames = (unsigned int) div_result;
pr_debug("p_srate %d, pitch %d, interval_mil %llu, frames %d\n",
- params->p_srate, prm->pitch, p_interval_mil, frames);
+ prm->srate, prm->pitch, p_interval_mil, frames);
p_pktsize = min_t(unsigned int,
uac->p_framesize * frames,
@@ -284,7 +282,6 @@ static void u_audio_iso_fback_complete(struct usb_ep *ep,
struct uac_rtd_params *prm = req->context;
struct snd_uac_chip *uac = prm->uac;
struct g_audio *audio_dev = uac->audio_dev;
- struct uac_params *params = &audio_dev->params;
int status = req->status;
/* i/f shutting down */
@@ -306,7 +303,7 @@ static void u_audio_iso_fback_complete(struct usb_ep *ep,
__func__, status, req->actual, req->length);
u_audio_set_fback_frequency(audio_dev->gadget->speed, audio_dev->out_ep,
- params->c_srate, prm->pitch,
+ prm->srate, prm->pitch,
req->buf);
if (usb_ep_queue(ep, req, GFP_ATOMIC))
@@ -390,16 +387,14 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct g_audio *audio_dev;
struct uac_params *params;
+ struct uac_rtd_params *prm;
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_chmask = params->p_chmask;
c_chmask = params->c_chmask;
uac->p_residue_mil = 0;
@@ -407,19 +402,18 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
runtime->hw = uac_pcm_hardware;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- runtime->hw.rate_min = p_srate;
runtime->hw.formats = uac_ssize_to_fmt(p_ssize);
runtime->hw.channels_min = num_channels(p_chmask);
- runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize
- / runtime->hw.periods_min;
+ prm = &uac->p_prm;
} else {
- runtime->hw.rate_min = c_srate;
runtime->hw.formats = uac_ssize_to_fmt(c_ssize);
runtime->hw.channels_min = num_channels(c_chmask);
- runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize
- / runtime->hw.periods_min;
+ prm = &uac->c_prm;
}
+ runtime->hw.period_bytes_min = 2 * prm->max_psize
+ / runtime->hw.periods_min;
+ runtime->hw.rate_min = prm->srate;
runtime->hw.rate_max = runtime->hw.rate_min;
runtime->hw.channels_max = runtime->hw.channels_min;
@@ -499,12 +493,18 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep)
int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate)
{
struct uac_params *params = &audio_dev->params;
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
int i;
+ unsigned long flags;
dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
+ prm = &uac->c_prm;
for (i = 0; i < UAC_MAX_RATES; i++) {
if (params->c_srates[i] == srate) {
- params->c_srate = srate;
+ spin_lock_irqsave(&prm->lock, flags);
+ prm->srate = srate;
+ spin_unlock_irqrestore(&prm->lock, flags);
return 0;
}
if (params->c_srates[i] == 0)
@@ -518,12 +518,18 @@ EXPORT_SYMBOL_GPL(u_audio_set_capture_srate);
int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
{
struct uac_params *params = &audio_dev->params;
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
int i;
+ unsigned long flags;
dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
+ prm = &uac->p_prm;
for (i = 0; i < UAC_MAX_RATES; i++) {
if (params->p_srates[i] == srate) {
- params->p_srate = srate;
+ spin_lock_irqsave(&prm->lock, flags);
+ prm->srate = srate;
+ spin_unlock_irqrestore(&prm->lock, flags);
return 0;
}
if (params->p_srates[i] == 0)
@@ -545,9 +551,9 @@ int u_audio_start_capture(struct g_audio *audio_dev)
struct uac_params *params = &audio_dev->params;
int req_len, i;
- dev_dbg(dev, "start capture with rate %d\n", params->c_srate);
- ep = audio_dev->out_ep;
prm = &uac->c_prm;
+ dev_dbg(dev, "start capture with rate %d\n", prm->srate);
+ ep = audio_dev->out_ep;
config_ep_by_speed(gadget, &audio_dev->func, ep);
req_len = ep->maxpacket;
@@ -604,7 +610,7 @@ int u_audio_start_capture(struct g_audio *audio_dev)
*/
prm->pitch = 1000000;
u_audio_set_fback_frequency(audio_dev->gadget->speed, ep,
- params->c_srate, prm->pitch,
+ prm->srate, prm->pitch,
req_fback->buf);
if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC))
@@ -638,9 +644,9 @@ int u_audio_start_playback(struct g_audio *audio_dev)
int req_len, i;
unsigned int p_pktsize;
- dev_dbg(dev, "start playback with rate %d\n", params->p_srate);
- ep = audio_dev->in_ep;
prm = &uac->p_prm;
+ dev_dbg(dev, "start playback with rate %d\n", prm->srate);
+ ep = audio_dev->in_ep;
config_ep_by_speed(gadget, &audio_dev->func, ep);
ep_desc = ep->desc;
@@ -661,7 +667,7 @@ int u_audio_start_playback(struct g_audio *audio_dev)
uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
p_pktsize = min_t(unsigned int,
uac->p_framesize *
- (params->p_srate / uac->p_interval),
+ (prm->srate / uac->p_interval),
ep->maxpacket);
req_len = p_pktsize;
@@ -1037,15 +1043,11 @@ static int u_audio_rate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
- struct snd_uac_chip *uac = prm->uac;
- struct g_audio *audio_dev = uac->audio_dev;
- struct uac_params *params = &audio_dev->params;
-
- if (prm == &uac->c_prm)
- ucontrol->value.integer.value[0] = params->c_srate;
- else
- ucontrol->value.integer.value[0] = params->p_srate;
+ unsigned long flags;
+ spin_lock_irqsave(&prm->lock, flags);
+ ucontrol->value.integer.value[0] = prm->srate;
+ spin_unlock_irqrestore(&prm->lock, flags);
return 0;
}
@@ -1117,6 +1119,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
spin_lock_init(&prm->lock);
uac->c_prm.uac = uac;
prm->max_psize = g_audio->out_ep_maxpsize;
+ prm->srate = params->c_srates[0];
prm->reqs = kcalloc(params->req_number,
sizeof(struct usb_request *),
@@ -1141,6 +1144,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
spin_lock_init(&prm->lock);
uac->p_prm.uac = uac;
prm->max_psize = g_audio->in_ep_maxpsize;
+ prm->srate = params->p_srates[0];
prm->reqs = kcalloc(params->req_number,
sizeof(struct usb_request *),
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index 76b5b8169444..84579fe81b92 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -42,17 +42,17 @@ struct uac_params {
/* playback */
int p_chmask; /* channel mask */
int p_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
- int p_srate; /* selected rate in Hz */
int p_ssize; /* sample size */
struct uac_fu_params p_fu; /* Feature Unit parameters */
/* capture */
int c_chmask; /* channel mask */
int c_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
- int c_srate; /* selected rate in Hz */
int c_ssize; /* sample size */
struct uac_fu_params c_fu; /* Feature Unit parameters */
+ /* rates are dynamic, in uac_rtd_params */
+
int req_number; /* number of preallocated requests */
int fb_max; /* upper frequency drift feedback limit per-mil */
};
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 04/10] usb: gadget: u_audio: Add capture/playback srate getter
2022-01-10 7:37 [PATCH v4 00/10] usb: gadget: audio: Multiple rates, notify Pavel Hofman
` (2 preceding siblings ...)
2022-01-10 7:37 ` [PATCH v4 03/10] usb: gadget: u_audio: Move dynamic srate from params to rtd Pavel Hofman
@ 2022-01-10 7:37 ` Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 05/10] usb: gadget: f_uac2: Support multiple sampling rates Pavel Hofman
` (5 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pavel Hofman @ 2022-01-10 7:37 UTC (permalink / raw)
To: linux-usb
Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
Julian Scheel, John Keeping, Greg Kroah-Hartman
UAC1/UAC2 functions will need to query u_audio about the currently set
srate. Add the getter functions.
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
v3: new patch
---
drivers/usb/gadget/function/u_audio.c | 28 +++++++++++++++++++++++++++
drivers/usb/gadget/function/u_audio.h | 2 ++
2 files changed, 30 insertions(+)
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index dce894dcae07..283a449a9538 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -515,6 +515,20 @@ int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate)
}
EXPORT_SYMBOL_GPL(u_audio_set_capture_srate);
+int u_audio_get_capture_srate(struct g_audio *audio_dev, u32 *val)
+{
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
+ unsigned long flags;
+
+ prm = &uac->c_prm;
+ spin_lock_irqsave(&prm->lock, flags);
+ *val = prm->srate;
+ spin_unlock_irqrestore(&prm->lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(u_audio_get_capture_srate);
+
int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
{
struct uac_params *params = &audio_dev->params;
@@ -540,6 +554,20 @@ int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
}
EXPORT_SYMBOL_GPL(u_audio_set_playback_srate);
+int u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val)
+{
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
+ unsigned long flags;
+
+ prm = &uac->p_prm;
+ spin_lock_irqsave(&prm->lock, flags);
+ *val = prm->srate;
+ spin_unlock_irqrestore(&prm->lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(u_audio_get_playback_srate);
+
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_audio.h b/drivers/usb/gadget/function/u_audio.h
index 84579fe81b92..5e6ed0f31cc3 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -120,7 +120,9 @@ 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_get_capture_srate(struct g_audio *audio_dev, u32 *val);
int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate);
+int u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val);
int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate);
int u_audio_get_volume(struct g_audio *g_audio, int playback, s16 *val);
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 05/10] usb: gadget: f_uac2: Support multiple sampling rates
2022-01-10 7:37 [PATCH v4 00/10] usb: gadget: audio: Multiple rates, notify Pavel Hofman
` (3 preceding siblings ...)
2022-01-10 7:37 ` [PATCH v4 04/10] usb: gadget: u_audio: Add capture/playback srate getter Pavel Hofman
@ 2022-01-10 7:37 ` Pavel Hofman
2022-01-12 15:19 ` kernel test robot
2022-01-10 7:37 ` [PATCH v4 06/10] usb: gadget: f_uac1: " Pavel Hofman
` (4 subsequent siblings)
9 siblings, 1 reply; 12+ messages in thread
From: Pavel Hofman @ 2022-01-10 7:37 UTC (permalink / raw)
To: linux-usb
Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
Julian Scheel, John Keeping, Greg Kroah-Hartman
From: Julian Scheel <julian@jusst.de>
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 (stored in
struct f_uac2) is updated.
The gadget no longer supports only one frequency. Therefore USB strings
corresponding to the clock sources are renamed from specific Hz value to
general names Input clock/Output clock.
Config strings with single value stay compatible with the previous
version.
Multiple samplerates passed as configuration arrays to g_audio module
when built for f_uac2.
Signed-off-by: Julian Scheel <julian@jusst.de>
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
v3:
* audio.c: fixed unused variables for CONFIG_UAC1 variants
* audio: used 0444 instead of S_IRUGO
* f_uac2: renamed ctl_id -> clock_id + added explaining
comment
* f_uac2: removed current state srates from struct f_uac2_opts, using
u_audio_get_playback/capture_srate() instead.
* f_uac2: renamed UAC_RATE2_ATTRIBUTE -> UAC2_RATE_ATTRIBUTE, moved from
u_uac2.h to f_uac2.c
* renamed confusing cntrl_range_lay3 to cntrl_subrange_lay3
* struct cntrl_ranges_lay3_xxx is created with DECLARE_UAC2_CNTRL_RANGES_LAY3
* fixed ranges_size calculation, renamed to specific ranges_lay3_size
* updated commit message
v3 patch "usb: gadget: f_uac2: Rename Clock Sources to fixed names":
* Renamed commit message
* Corrected STR_CLKSRC_IN/OUT capitalization
v4:
* patch "usb: gadget: f_uac2: Rename Clock Sources to fixed names" squashed to
this patch, which also resolved unused-but-set-variable warning.
* updated commit msg
---
.../ABI/testing/configfs-usb-gadget-uac2 | 4 +-
Documentation/usb/gadget-testing.rst | 4 +-
drivers/usb/gadget/function/f_uac2.c | 198 ++++++++++++++----
drivers/usb/gadget/function/u_uac2.h | 5 +-
drivers/usb/gadget/legacy/audio.c | 25 ++-
5 files changed, 182 insertions(+), 54 deletions(-)
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2
index 7fb3dbe26857..9d2f59ab9701 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uac2
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac2
@@ -6,7 +6,7 @@ Description:
===================== =======================================
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)
c_sync capture synchronization type
(async/adaptive)
@@ -20,7 +20,7 @@ Description:
(in 1/256 dB)
fb_max maximum extra bandwidth in async mode
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)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst
index cbbd948c626f..419f6e5e890a 100644
--- a/Documentation/usb/gadget-testing.rst
+++ b/Documentation/usb/gadget-testing.rst
@@ -726,7 +726,7 @@ 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)
c_sync capture synchronization type (async/adaptive)
c_mute_present capture mute control enable
@@ -736,7 +736,7 @@ The uac2 function provides these attributes in its function directory:
c_volume_res capture volume control resolution (in 1/256 dB)
fb_max maximum extra bandwidth in async mode
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)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index e518f210968c..058a6a0b5444 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -70,6 +70,8 @@ struct f_uac2 {
/* Interrupt IN endpoint of AC interface */
struct usb_ep *int_ep;
atomic_t int_count;
+ /* transient state, only valid during handling of a single control request */
+ int clock_id;
};
static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
@@ -104,14 +106,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",
@@ -166,7 +165,7 @@ static struct uac_clock_source_descriptor in_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
/* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
- .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+ .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@@ -178,7 +177,7 @@ static struct uac_clock_source_descriptor out_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
/* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
- .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+ .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@@ -634,13 +633,36 @@ struct cntrl_cur_lay3 {
__le32 dCUR;
};
-struct cntrl_range_lay3 {
- __le16 wNumSubRanges;
+struct cntrl_subrange_lay3 {
__le32 dMIN;
__le32 dMAX;
__le32 dRES;
} __packed;
+#define ranges_lay3_size(c) (sizeof(c.wNumSubRanges) + c.wNumSubRanges \
+ * sizeof(struct cntrl_subrange_lay3))
+
+#define DECLARE_UAC2_CNTRL_RANGES_LAY3(k, n) \
+ struct cntrl_ranges_lay3_##k { \
+ __le16 wNumSubRanges; \
+ struct cntrl_subrange_lay3 r[n]; \
+} __packed
+
+DECLARE_UAC2_CNTRL_RANGES_LAY3(srates, UAC_MAX_RATES);
+
+static int get_max_srate(const int *srates)
+{
+ int i, max_srate = 0;
+
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ if (srates[i] == 0)
+ break;
+ if (srates[i] > max_srate)
+ max_srate = srates[i];
+ }
+ return max_srate;
+}
+
static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
struct usb_endpoint_descriptor *ep_desc,
enum usb_device_speed speed, bool is_playback)
@@ -667,11 +689,11 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
if (is_playback) {
chmask = uac2_opts->p_chmask;
- srate = uac2_opts->p_srate;
+ srate = get_max_srate(uac2_opts->p_srates);
ssize = uac2_opts->p_ssize;
} else {
chmask = uac2_opts->c_chmask;
- srate = uac2_opts->c_srate;
+ srate = get_max_srate(uac2_opts->c_srates);
ssize = uac2_opts->c_ssize;
}
@@ -912,10 +934,10 @@ static int afunc_validate_opts(struct g_audio *agdev, struct device *dev)
} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
dev_err(dev, "Error: incorrect capture sample size\n");
return -EINVAL;
- } else if (!opts->p_srate) {
+ } else if (!opts->p_srates[0]) {
dev_err(dev, "Error: incorrect playback sampling rate\n");
return -EINVAL;
- } else if (!opts->c_srate) {
+ } else if (!opts->c_srates[0]) {
dev_err(dev, "Error: incorrect capture sampling rate\n");
return -EINVAL;
}
@@ -1037,9 +1059,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
*bma = cpu_to_le32(control);
}
- 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__);
@@ -1209,7 +1228,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->gadget = gadget;
agdev->params.p_chmask = uac2_opts->p_chmask;
- agdev->params.p_srates[0] = uac2_opts->p_srate;
+ memcpy(agdev->params.p_srates, uac2_opts->p_srates,
+ sizeof(agdev->params.p_srates));
agdev->params.p_ssize = uac2_opts->p_ssize;
if (FUIN_EN(uac2_opts)) {
agdev->params.p_fu.id = USB_IN_FU_ID;
@@ -1220,7 +1240,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->params.p_fu.volume_res = uac2_opts->p_volume_res;
}
agdev->params.c_chmask = uac2_opts->c_chmask;
- agdev->params.c_srates[0] = uac2_opts->c_srate;
+ memcpy(agdev->params.c_srates, uac2_opts->c_srates,
+ sizeof(agdev->params.c_srates));
agdev->params.c_ssize = uac2_opts->c_ssize;
if (FUOUT_EN(uac2_opts)) {
agdev->params.c_fu.id = USB_OUT_FU_ID;
@@ -1423,10 +1444,10 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- int p_srate, c_srate;
+ u32 p_srate, c_srate;
- p_srate = opts->p_srate;
- c_srate = opts->c_srate;
+ u_audio_get_playback_srate(agdev, &p_srate);
+ u_audio_get_capture_srate(agdev, &c_srate);
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
@@ -1500,28 +1521,39 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- int p_srate, c_srate;
-
- p_srate = opts->p_srate;
- c_srate = opts->c_srate;
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
- struct cntrl_range_lay3 r;
+ struct cntrl_ranges_lay3_srates rs;
+ int i;
+ int wNumSubRanges = 0;
+ int srate;
+ int *srates;
if (entity_id == USB_IN_CLK_ID)
- r.dMIN = cpu_to_le32(p_srate);
+ srates = opts->p_srates;
else if (entity_id == USB_OUT_CLK_ID)
- r.dMIN = cpu_to_le32(c_srate);
+ srates = opts->c_srates;
else
return -EOPNOTSUPP;
-
- r.dMAX = r.dMIN;
- r.dRES = 0;
- r.wNumSubRanges = cpu_to_le16(1);
-
- value = min_t(unsigned int, w_length, sizeof(r));
- memcpy(req->buf, &r, value);
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ srate = srates[i];
+ if (srate == 0)
+ break;
+
+ rs.r[wNumSubRanges].dMIN = cpu_to_le32(srate);
+ rs.r[wNumSubRanges].dMAX = cpu_to_le32(srate);
+ rs.r[wNumSubRanges].dRES = 0;
+ wNumSubRanges++;
+ dev_dbg(&agdev->gadget->dev,
+ "%s(): clk %d: rate ID %d: %d\n",
+ __func__, entity_id, wNumSubRanges, srate);
+ }
+ rs.wNumSubRanges = cpu_to_le16(wNumSubRanges);
+ value = min_t(unsigned int, w_length, ranges_lay3_size(rs));
+ dev_dbg(&agdev->gadget->dev, "%s(): sending %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",
@@ -1580,6 +1612,25 @@ 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 g_audio *agdev = func_to_g_audio(fn);
+ struct f_uac2 *uac2 = func_to_uac2(fn);
+ u32 val;
+
+ if (req->actual != 4)
+ return;
+
+ val = le32_to_cpu(*((u32 *)req->buf));
+ dev_dbg(&agdev->gadget->dev, "%s val: %d.\n", __func__, val);
+ if (uac2->clock_id == USB_IN_CLK_ID) {
+ u_audio_set_playback_srate(agdev, val);
+ } else if (uac2->clock_id == USB_OUT_CLK_ID) {
+ u_audio_set_capture_srate(agdev, val);
+ }
+}
+
static void
out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
{
@@ -1631,6 +1682,7 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
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 = fn->config->cdev->req;
struct g_audio *agdev = func_to_g_audio(fn);
struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
@@ -1640,10 +1692,17 @@ out_rq_cur(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;
+ u8 clock_id = w_index >> 8;
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
- if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
+ if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+ dev_dbg(&agdev->gadget->dev,
+ "control_selector UAC2_CS_CONTROL_SAM_FREQ, clock: %d\n", clock_id);
+ cdev->gadget->ep0->driver_data = fn;
+ uac2->clock_id = clock_id;
+ req->complete = uac2_cs_control_sam_freq;
return w_length;
+ }
} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
memcpy(&uac2->setup_cr, cr, sizeof(*cr));
@@ -1836,11 +1895,70 @@ end: \
\
CONFIGFS_ATTR(f_uac2_opts_, name)
+#define UAC2_RATE_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 = 0; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ page[0] = '\0'; \
+ for (i = 0; i < UAC_MAX_RATES; i++) { \
+ if (opts->name##s[i] == 0) \
+ break; \
+ result += sprintf(page + strlen(page), "%u,", \
+ opts->name##s[i]); \
+ } \
+ if (strlen(page) > 0) \
+ page[strlen(page) - 1] = '\n'; \
+ 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); \
+ 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##s, 0x00, sizeof(opts->name##s)); \
+ split_page = kstrdup(page, GFP_KERNEL); \
+ while ((token = strsep(&split_page, ",")) != NULL) { \
+ ret = kstrtou32(token, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ opts->name##s[i++] = num; \
+ ret = len; \
+ }; \
+ \
+end: \
+ kfree(split_page); \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac2_opts_, name)
+
UAC2_ATTRIBUTE(u32, p_chmask);
-UAC2_ATTRIBUTE(u32, p_srate);
+UAC2_RATE_ATTRIBUTE(p_srate);
UAC2_ATTRIBUTE(u32, p_ssize);
UAC2_ATTRIBUTE(u32, c_chmask);
-UAC2_ATTRIBUTE(u32, c_srate);
+UAC2_RATE_ATTRIBUTE(c_srate);
UAC2_ATTRIBUTE_SYNC(c_sync);
UAC2_ATTRIBUTE(u32, c_ssize);
UAC2_ATTRIBUTE(u32, req_number);
@@ -1913,10 +2031,10 @@ static struct usb_function_instance *afunc_alloc_inst(void)
&f_uac2_func_type);
opts->p_chmask = UAC2_DEF_PCHMASK;
- opts->p_srate = UAC2_DEF_PSRATE;
+ opts->p_srates[0] = UAC2_DEF_PSRATE;
opts->p_ssize = UAC2_DEF_PSSIZE;
opts->c_chmask = UAC2_DEF_CCHMASK;
- opts->c_srate = UAC2_DEF_CSRATE;
+ opts->c_srates[0] = UAC2_DEF_CSRATE;
opts->c_ssize = UAC2_DEF_CSSIZE;
opts->c_sync = UAC2_DEF_CSYNC;
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index e0c8e3513bfd..6bfcf6d0e863 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -14,6 +14,7 @@
#define U_UAC2_H
#include <linux/usb/composite.h>
+#include "uac_common.h"
#define UAC2_DEF_PCHMASK 0x3
#define UAC2_DEF_PSRATE 48000
@@ -35,10 +36,10 @@
struct f_uac2_opts {
struct usb_function_instance func_inst;
int p_chmask;
- int p_srate;
+ int p_srates[UAC_MAX_RATES];
int p_ssize;
int c_chmask;
- int c_srate;
+ int c_srates[UAC_MAX_RATES];
int c_ssize;
int c_sync;
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index 5ec477ffab7f..d14b9f2d4c07 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -26,9 +26,10 @@ module_param(p_chmask, uint, 0444);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
-static int p_srate = UAC2_DEF_PSRATE;
-module_param(p_srate, uint, 0444);
-MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+static int p_srates[UAC_MAX_RATES] = {UAC2_DEF_PSRATE};
+static int p_srates_cnt = 1;
+module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, 0444);
+MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)");
/* Playback Default 16bits/sample */
static int p_ssize = UAC2_DEF_PSSIZE;
@@ -41,9 +42,10 @@ module_param(c_chmask, uint, 0444);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 64 KHz */
-static int c_srate = UAC2_DEF_CSRATE;
-module_param(c_srate, uint, 0444);
-MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+static int c_srates[UAC_MAX_RATES] = {UAC2_DEF_CSRATE};
+static int c_srates_cnt = 1;
+module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, 0444);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
/* Capture Default 16bits/sample */
static int c_ssize = UAC2_DEF_CSSIZE;
@@ -237,6 +239,7 @@ static int audio_bind(struct usb_composite_dev *cdev)
{
#ifndef CONFIG_GADGET_UAC1
struct f_uac2_opts *uac2_opts;
+ int i;
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
struct f_uac1_opts *uac1_opts;
@@ -263,10 +266,16 @@ static int audio_bind(struct usb_composite_dev *cdev)
#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;
+
+ for (i = 0; i < p_srates_cnt; ++i)
+ uac2_opts->p_srates[i] = p_srates[i];
+
uac2_opts->p_ssize = p_ssize;
uac2_opts->c_chmask = c_chmask;
- uac2_opts->c_srate = c_srate;
+
+ for (i = 0; i < c_srates_cnt; ++i)
+ uac2_opts->c_srates[i] = c_srates[i];
+
uac2_opts->c_ssize = c_ssize;
uac2_opts->req_number = UAC2_DEF_REQ_NUM;
#else
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v4 05/10] usb: gadget: f_uac2: Support multiple sampling rates
2022-01-10 7:37 ` [PATCH v4 05/10] usb: gadget: f_uac2: Support multiple sampling rates Pavel Hofman
@ 2022-01-12 15:19 ` kernel test robot
0 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2022-01-12 15:19 UTC (permalink / raw)
To: kbuild-all
[-- Attachment #1: Type: text/plain, Size: 5946 bytes --]
Hi Pavel,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on usb/usb-testing]
[also build test WARNING on next-20220112]
[cannot apply to linus/master balbi-usb/testing/next peter-chen-usb/for-usb-next v5.16]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Pavel-Hofman/usb-gadget-audio-Multiple-rates-notify/20220110-154453
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: i386-randconfig-s001 (https://download.01.org/0day-ci/archive/20220112/202201122317.eiQOLXzP-lkp(a)intel.com/config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce:
# apt-get install sparse
# sparse version: v0.6.4-dirty
# https://github.com/0day-ci/linux/commit/275344f5925d7804375e3572fd2fbd1febb029a6
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Pavel-Hofman/usb-gadget-audio-Multiple-rates-notify/20220110-154453
git checkout 275344f5925d7804375e3572fd2fbd1febb029a6
# save the config file to linux build tree
mkdir build_dir
make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=i386 SHELL=/bin/bash drivers/usb/gadget/function/
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
sparse warnings: (new ones prefixed by >>)
>> drivers/usb/gadget/function/f_uac2.c:1553:33: sparse: sparse: restricted __le16 degrades to integer
>> drivers/usb/gadget/function/f_uac2.c:1553:33: sparse: sparse: restricted __le16 degrades to integer
>> drivers/usb/gadget/function/f_uac2.c:1553:33: sparse: sparse: restricted __le16 degrades to integer
>> drivers/usb/gadget/function/f_uac2.c:1553:33: sparse: sparse: restricted __le16 degrades to integer
>> drivers/usb/gadget/function/f_uac2.c:1553:33: sparse: sparse: restricted __le16 degrades to integer
>> drivers/usb/gadget/function/f_uac2.c:1553:33: sparse: sparse: restricted __le16 degrades to integer
drivers/usb/gadget/function/f_uac2.c:1625:15: sparse: sparse: cast to restricted __le32
vim +1553 drivers/usb/gadget/function/f_uac2.c
1511
1512 static int
1513 in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
1514 {
1515 struct usb_request *req = fn->config->cdev->req;
1516 struct g_audio *agdev = func_to_g_audio(fn);
1517 struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
1518 u16 w_length = le16_to_cpu(cr->wLength);
1519 u16 w_index = le16_to_cpu(cr->wIndex);
1520 u16 w_value = le16_to_cpu(cr->wValue);
1521 u8 entity_id = (w_index >> 8) & 0xff;
1522 u8 control_selector = w_value >> 8;
1523 int value = -EOPNOTSUPP;
1524
1525 if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
1526 if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
1527 struct cntrl_ranges_lay3_srates rs;
1528 int i;
1529 int wNumSubRanges = 0;
1530 int srate;
1531 int *srates;
1532
1533 if (entity_id == USB_IN_CLK_ID)
1534 srates = opts->p_srates;
1535 else if (entity_id == USB_OUT_CLK_ID)
1536 srates = opts->c_srates;
1537 else
1538 return -EOPNOTSUPP;
1539 for (i = 0; i < UAC_MAX_RATES; i++) {
1540 srate = srates[i];
1541 if (srate == 0)
1542 break;
1543
1544 rs.r[wNumSubRanges].dMIN = cpu_to_le32(srate);
1545 rs.r[wNumSubRanges].dMAX = cpu_to_le32(srate);
1546 rs.r[wNumSubRanges].dRES = 0;
1547 wNumSubRanges++;
1548 dev_dbg(&agdev->gadget->dev,
1549 "%s(): clk %d: rate ID %d: %d\n",
1550 __func__, entity_id, wNumSubRanges, srate);
1551 }
1552 rs.wNumSubRanges = cpu_to_le16(wNumSubRanges);
> 1553 value = min_t(unsigned int, w_length, ranges_lay3_size(rs));
1554 dev_dbg(&agdev->gadget->dev, "%s(): sending %d rates, size %d\n",
1555 __func__, rs.wNumSubRanges, value);
1556 memcpy(req->buf, &rs, value);
1557 } else {
1558 dev_err(&agdev->gadget->dev,
1559 "%s:%d control_selector=%d TODO!\n",
1560 __func__, __LINE__, control_selector);
1561 }
1562 } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
1563 (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
1564 unsigned int is_playback = 0;
1565
1566 if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
1567 is_playback = 1;
1568
1569 if (control_selector == UAC_FU_VOLUME) {
1570 struct cntrl_range_lay2 r;
1571 s16 max_db, min_db, res_db;
1572
1573 if (is_playback) {
1574 max_db = opts->p_volume_max;
1575 min_db = opts->p_volume_min;
1576 res_db = opts->p_volume_res;
1577 } else {
1578 max_db = opts->c_volume_max;
1579 min_db = opts->c_volume_min;
1580 res_db = opts->c_volume_res;
1581 }
1582
1583 r.wMAX = cpu_to_le16(max_db);
1584 r.wMIN = cpu_to_le16(min_db);
1585 r.wRES = cpu_to_le16(res_db);
1586 r.wNumSubRanges = cpu_to_le16(1);
1587
1588 value = min_t(unsigned int, w_length, sizeof(r));
1589 memcpy(req->buf, &r, value);
1590 } else {
1591 dev_err(&agdev->gadget->dev,
1592 "%s:%d control_selector=%d TODO!\n",
1593 __func__, __LINE__, control_selector);
1594 }
1595 } else {
1596 dev_err(&agdev->gadget->dev,
1597 "%s:%d entity_id=%d control_selector=%d TODO!\n",
1598 __func__, __LINE__, entity_id, control_selector);
1599 }
1600
1601 return value;
1602 }
1603
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v4 06/10] usb: gadget: f_uac1: Support multiple sampling rates
2022-01-10 7:37 [PATCH v4 00/10] usb: gadget: audio: Multiple rates, notify Pavel Hofman
` (4 preceding siblings ...)
2022-01-10 7:37 ` [PATCH v4 05/10] usb: gadget: f_uac2: Support multiple sampling rates Pavel Hofman
@ 2022-01-10 7:37 ` Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 07/10] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped) Pavel Hofman
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pavel Hofman @ 2022-01-10 7:37 UTC (permalink / raw)
To: linux-usb
Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
Julian Scheel, John Keeping, Greg Kroah-Hartman
From: Julian Scheel <julian@jusst.de>
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.
Config strings with single value stay compatible with the previous version.
Multiple samplerates passed as configuration arrays to g_audio module
when built for f_uac1.
Signed-off-by: Julian Scheel <julian@jusst.de>
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
v3:
* audio.c: fixed unused variables for CONFIG_UAC1 variants
* audio: used 0444 instead of S_IRUGO
* f_uac1: moved current state srates from struct f_uac1_opts to f_uac1
* f_uac1: renamed UAC_RATE1_ATTRIBUTE -> UAC1_RATE_ATTRIBUTE, moved from
u_uac1.h to f_uac1.c
* updated commit message
---
.../ABI/testing/configfs-usb-gadget-uac1 | 4 +-
Documentation/usb/gadget-testing.rst | 4 +-
drivers/usb/gadget/function/f_uac1.c | 181 +++++++++++++++---
drivers/usb/gadget/function/u_uac1.h | 5 +-
drivers/usb/gadget/legacy/audio.c | 25 ++-
5 files changed, 179 insertions(+), 40 deletions(-)
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1
index d4b8cf40a9e4..09725e273e9b 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uac1
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1
@@ -6,7 +6,7 @@ Description:
===================== =======================================
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)
c_mute_present capture mute control enable
c_volume_present capture volume control enable
@@ -17,7 +17,7 @@ Description:
c_volume_res capture volume control resolution
(in 1/256 dB)
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)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst
index 419f6e5e890a..046842b00c89 100644
--- a/Documentation/usb/gadget-testing.rst
+++ b/Documentation/usb/gadget-testing.rst
@@ -916,7 +916,7 @@ 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)
c_mute_present capture mute control enable
c_volume_present capture volume control enable
@@ -924,7 +924,7 @@ The uac1 function provides these attributes in its function directory:
c_volume_max capture volume control max value (in 1/256 dB)
c_volume_res capture volume control resolution (in 1/256 dB)
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)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 0397b27df42e..73df76a6fbe0 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -3,6 +3,7 @@
* f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
*
* Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ * Copyright (C) 2021 Julian Scheel <julian@jusst.de>
*
* This driver doesn't expect any real Audio codec to be present
* on the device - the audio streams are simply sinked to and
@@ -42,6 +43,9 @@ struct f_uac1 {
/* Interrupt IN endpoint of AC interface */
struct usb_ep *int_ep;
atomic_t int_count;
+ int ctl_id; /* EP id */
+ int c_srate; /* current capture srate */
+ int p_srate; /* current playback prate */
};
static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
@@ -188,16 +192,18 @@ static struct uac1_as_header_descriptor as_in_header_desc = {
.wFormatTag = cpu_to_le16(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 */
@@ -221,14 +227,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 */
@@ -333,6 +339,30 @@ 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_uac1 *uac1 = func_to_uac1(fn);
+ 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 | 2)) {
+ uac1->p_srate = val;
+ u_audio_set_playback_srate(agdev, uac1->p_srate);
+ } else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
+ uac1->c_srate = val;
+ u_audio_set_capture_srate(agdev, uac1->c_srate);
+ }
+}
+
static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
{
struct g_audio *audio = req->context;
@@ -707,18 +737,27 @@ 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_uac1 *uac1 = func_to_uac1(f);
int value = -EOPNOTSUPP;
u16 ep = le16_to_cpu(ctrl->wIndex);
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;
@@ -743,16 +782,33 @@ 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 f_uac1 *uac1 = func_to_uac1(f);
+ u8 *buf = (u8 *)req->buf;
int value = -EOPNOTSUPP;
- u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+ u8 ep = le16_to_cpu(ctrl->wIndex);
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 | 2))
+ val = uac1->p_srate;
+ else if (ep == (USB_DIR_OUT | 1))
+ val = uac1->c_srate;
+ 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:
@@ -1074,10 +1130,10 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
dev_err(dev, "Error: incorrect capture sample size\n");
return -EINVAL;
- } else if (!opts->p_srate) {
+ } else if (!opts->p_srates[0]) {
dev_err(dev, "Error: incorrect playback sampling rate\n");
return -EINVAL;
- } else if (!opts->c_srate) {
+ } else if (!opts->c_srates[0]) {
dev_err(dev, "Error: incorrect capture sampling rate\n");
return -EINVAL;
}
@@ -1118,10 +1174,9 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
struct f_uac1_opts *audio_opts;
struct usb_ep *ep = NULL;
struct usb_string *us;
- u8 *sam_freq;
- int rate;
int ba_iface_id;
int status;
+ int idx, i;
status = f_audio_validate_opts(audio, dev);
if (status)
@@ -1213,12 +1268,25 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
}
/* 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_srates[i] == 0)
+ break;
+ memcpy(as_out_type_i_desc.tSamFreq[idx++],
+ &audio_opts->c_srates[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_srates[i] == 0)
+ break;
+ memcpy(as_in_type_i_desc.tSamFreq[idx++],
+ &audio_opts->p_srates[i], 3);
+ }
+ as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
+ as_in_type_i_desc.bSamFreqType = idx;
+ uac1->p_srate = audio_opts->p_srates[0];
+ uac1->c_srate = audio_opts->c_srates[0];
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
@@ -1297,7 +1365,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
audio->params.c_chmask = audio_opts->c_chmask;
- audio->params.c_srates[0] = audio_opts->c_srate;
+ memcpy(audio->params.c_srates, audio_opts->c_srates,
+ sizeof(audio->params.c_srates));
audio->params.c_ssize = audio_opts->c_ssize;
if (FUIN_EN(audio_opts)) {
audio->params.p_fu.id = USB_IN_FU_ID;
@@ -1309,7 +1378,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->params.p_fu.volume_res = audio_opts->p_volume_res;
}
audio->params.p_chmask = audio_opts->p_chmask;
- audio->params.p_srates[0] = audio_opts->p_srate;
+ memcpy(audio->params.p_srates, audio_opts->p_srates,
+ sizeof(audio->params.p_srates));
audio->params.p_ssize = audio_opts->p_ssize;
if (FUOUT_EN(audio_opts)) {
audio->params.c_fu.id = USB_OUT_FU_ID;
@@ -1414,11 +1484,70 @@ end: \
\
CONFIGFS_ATTR(f_uac1_opts_, name)
+#define UAC1_RATE_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 = 0; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ page[0] = '\0'; \
+ for (i = 0; i < UAC_MAX_RATES; i++) { \
+ if (opts->name##s[i] == 0) \
+ break; \
+ result += sprintf(page + strlen(page), "%u,", \
+ opts->name##s[i]); \
+ } \
+ if (strlen(page) > 0) \
+ page[strlen(page) - 1] = '\n'; \
+ 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); \
+ 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##s, 0x00, sizeof(opts->name##s)); \
+ split_page = kstrdup(page, GFP_KERNEL); \
+ while ((token = strsep(&split_page, ",")) != NULL) { \
+ ret = kstrtou32(token, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ opts->name##s[i++] = num; \
+ ret = len; \
+ }; \
+ \
+end: \
+ kfree(split_page); \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac1_opts_, name)
+
UAC1_ATTRIBUTE(u32, c_chmask);
-UAC1_ATTRIBUTE(u32, c_srate);
+UAC1_RATE_ATTRIBUTE(c_srate);
UAC1_ATTRIBUTE(u32, c_ssize);
UAC1_ATTRIBUTE(u32, p_chmask);
-UAC1_ATTRIBUTE(u32, p_srate);
+UAC1_RATE_ATTRIBUTE(p_srate);
UAC1_ATTRIBUTE(u32, p_ssize);
UAC1_ATTRIBUTE(u32, req_number);
@@ -1487,10 +1616,10 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
&f_uac1_func_type);
opts->c_chmask = UAC1_DEF_CCHMASK;
- opts->c_srate = UAC1_DEF_CSRATE;
+ opts->c_srates[0] = UAC1_DEF_CSRATE;
opts->c_ssize = UAC1_DEF_CSSIZE;
opts->p_chmask = UAC1_DEF_PCHMASK;
- opts->p_srate = UAC1_DEF_PSRATE;
+ opts->p_srates[0] = UAC1_DEF_PSRATE;
opts->p_ssize = UAC1_DEF_PSSIZE;
opts->p_mute_present = UAC1_DEF_MUTE_PRESENT;
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 589fae861141..b6cd6171d306 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -9,6 +9,7 @@
#define __U_UAC1_H
#include <linux/usb/composite.h>
+#include "uac_common.h"
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
#define UAC1_DEF_CCHMASK 0x3
@@ -30,10 +31,10 @@
struct f_uac1_opts {
struct usb_function_instance func_inst;
int c_chmask;
- int c_srate;
+ int c_srates[UAC_MAX_RATES];
int c_ssize;
int p_chmask;
- int p_srate;
+ int p_srates[UAC_MAX_RATES];
int p_ssize;
bool p_mute_present;
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index d14b9f2d4c07..c89c777a1aa3 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -61,9 +61,10 @@ module_param(p_chmask, uint, 0444);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
-static int p_srate = UAC1_DEF_PSRATE;
-module_param(p_srate, uint, 0444);
-MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+static int p_srates[UAC_MAX_RATES] = {UAC1_DEF_PSRATE};
+static int p_srates_cnt = 1;
+module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, 0444);
+MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)");
/* Playback Default 16bits/sample */
static int p_ssize = UAC1_DEF_PSSIZE;
@@ -76,9 +77,10 @@ module_param(c_chmask, uint, 0444);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 48 KHz */
-static int c_srate = UAC1_DEF_CSRATE;
-module_param(c_srate, uint, 0444);
-MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+static int c_srates[UAC_MAX_RATES] = {UAC1_DEF_CSRATE};
+static int c_srates_cnt = 1;
+module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, 0444);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
/* Capture Default 16bits/sample */
static int c_ssize = UAC1_DEF_CSSIZE;
@@ -243,6 +245,7 @@ static int audio_bind(struct usb_composite_dev *cdev)
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
struct f_uac1_opts *uac1_opts;
+ int i;
#else
struct f_uac1_legacy_opts *uac1_opts;
#endif
@@ -282,10 +285,16 @@ static int audio_bind(struct usb_composite_dev *cdev)
#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;
+
+ for (i = 0; i < p_srates_cnt; ++i)
+ uac1_opts->p_srates[i] = p_srates[i];
+
uac1_opts->p_ssize = p_ssize;
uac1_opts->c_chmask = c_chmask;
- uac1_opts->c_srate = c_srate;
+
+ for (i = 0; i < c_srates_cnt; ++i)
+ uac1_opts->c_srates[i] = c_srates[i];
+
uac1_opts->c_ssize = c_ssize;
uac1_opts->req_number = UAC1_DEF_REQ_NUM;
#else /* CONFIG_GADGET_UAC1_LEGACY */
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 07/10] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped)
2022-01-10 7:37 [PATCH v4 00/10] usb: gadget: audio: Multiple rates, notify Pavel Hofman
` (5 preceding siblings ...)
2022-01-10 7:37 ` [PATCH v4 06/10] usb: gadget: f_uac1: " Pavel Hofman
@ 2022-01-10 7:37 ` Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 08/10] usb: gadget: u_audio: Add suspend call Pavel Hofman
` (2 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pavel Hofman @ 2022-01-10 7:37 UTC (permalink / raw)
To: linux-usb
Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
Julian Scheel, John Keeping, Greg Kroah-Hartman
The Playback/Capture ctl currently reports rate value set by USB
control selector UAC2_CS_CONTROL_SAM_FREQ (fixed for UAC1). When the
stops playback/capture, the reported value does not change. The gadget
side has no information whether the host has started/stopped
capture/playback.
This patch sets the value reported by the respective rate ctl to zero
when the host side has stopped playback/capture. Also, it calls
snd_ctl_notify when start/stop occurs, so that a subscribed client can
act appropriately.
Tests have confirmed that USB hosts change UAC2_CS_CONTROL_SAM_FREQ
before switching altsetting to activate playback/capture, resulting in
correct order (params->c/p_srate is set to requested rate before
u_audio_start_capture/playback is called).
The gadget rate notifications are used by user-space audio gadget
controller gaudio_ctl https://github.com/pavhofman/gaudio_ctl.
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
v3:
* using prm->srate instead of params->c_srate/p_srate due to the added commit
"[v3] usb: gadget: u_audio: Move dynamic srate from params to rtd"
---
drivers/usb/gadget/function/u_audio.c | 28 ++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 283a449a9538..fab1bc439002 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -65,6 +65,7 @@ struct uac_rtd_params {
struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */
int srate; /* selected samplerate */
+ int active; /* playback/capture running */
spinlock_t lock; /* lock for control transfers */
@@ -490,6 +491,21 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep)
dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
}
+static void set_active(struct uac_rtd_params *prm, bool active)
+{
+ // notifying through the Rate ctrl
+ struct snd_kcontrol *kctl = prm->snd_kctl_rate;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prm->lock, flags);
+ if (prm->active != active) {
+ prm->active = active;
+ snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &kctl->id);
+ }
+ spin_unlock_irqrestore(&prm->lock, flags);
+}
+
int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate)
{
struct uac_params *params = &audio_dev->params;
@@ -607,6 +623,8 @@ int u_audio_start_capture(struct g_audio *audio_dev)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
}
+ set_active(&uac->c_prm, true);
+
ep_fback = audio_dev->in_ep_fback;
if (!ep_fback)
return 0;
@@ -652,6 +670,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
+ set_active(&uac->c_prm, false);
if (audio_dev->in_ep_fback)
free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback);
free_ep(&uac->c_prm, audio_dev->out_ep);
@@ -723,6 +742,8 @@ int u_audio_start_playback(struct g_audio *audio_dev)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
}
+ set_active(&uac->p_prm, true);
+
return 0;
}
EXPORT_SYMBOL_GPL(u_audio_start_playback);
@@ -731,6 +752,7 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
+ set_active(&uac->p_prm, false);
free_ep(&uac->p_prm, audio_dev->in_ep);
}
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
@@ -1074,7 +1096,11 @@ static int u_audio_rate_get(struct snd_kcontrol *kcontrol,
unsigned long flags;
spin_lock_irqsave(&prm->lock, flags);
- ucontrol->value.integer.value[0] = prm->srate;
+ if (prm->active)
+ ucontrol->value.integer.value[0] = prm->srate;
+ else
+ /* not active: reporting zero rate */
+ ucontrol->value.integer.value[0] = 0;
spin_unlock_irqrestore(&prm->lock, flags);
return 0;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 08/10] usb: gadget: u_audio: Add suspend call
2022-01-10 7:37 [PATCH v4 00/10] usb: gadget: audio: Multiple rates, notify Pavel Hofman
` (6 preceding siblings ...)
2022-01-10 7:37 ` [PATCH v4 07/10] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped) Pavel Hofman
@ 2022-01-10 7:37 ` Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 09/10] usb: gadget: f_uac2: Add suspend callback Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 10/10] usb: gadget: f_uac1: " Pavel Hofman
9 siblings, 0 replies; 12+ messages in thread
From: Pavel Hofman @ 2022-01-10 7:37 UTC (permalink / raw)
To: linux-usb
Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
Julian Scheel, John Keeping, Greg Kroah-Hartman
Add exported method u_audio_suspend which sets stream status to
inactive and sends notifications. The method does not free any
resources.
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
v3:
* fixed commit title
* removed stopping substreams (moved to a separate patch series)
* calling set_active(false) instead of the removed set_srate(0)
* updated commit msg
---
drivers/usb/gadget/function/u_audio.c | 9 +++++++++
drivers/usb/gadget/function/u_audio.h | 2 ++
2 files changed, 11 insertions(+)
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index fab1bc439002..2bb569895a90 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -757,6 +757,15 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
}
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
+void u_audio_suspend(struct g_audio *audio_dev)
+{
+ struct snd_uac_chip *uac = audio_dev->uac;
+
+ set_active(&uac->p_prm, false);
+ set_active(&uac->c_prm, false);
+}
+EXPORT_SYMBOL_GPL(u_audio_suspend);
+
int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val)
{
struct snd_uac_chip *uac = audio_dev->uac;
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index 5e6ed0f31cc3..9512b8fccfaa 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -130,4 +130,6 @@ int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val);
int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val);
int u_audio_set_mute(struct g_audio *g_audio, int playback, int val);
+void u_audio_suspend(struct g_audio *g_audio);
+
#endif /* __U_AUDIO_H */
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 09/10] usb: gadget: f_uac2: Add suspend callback
2022-01-10 7:37 [PATCH v4 00/10] usb: gadget: audio: Multiple rates, notify Pavel Hofman
` (7 preceding siblings ...)
2022-01-10 7:37 ` [PATCH v4 08/10] usb: gadget: u_audio: Add suspend call Pavel Hofman
@ 2022-01-10 7:37 ` Pavel Hofman
2022-01-10 7:37 ` [PATCH v4 10/10] usb: gadget: f_uac1: " Pavel Hofman
9 siblings, 0 replies; 12+ messages in thread
From: Pavel Hofman @ 2022-01-10 7:37 UTC (permalink / raw)
To: linux-usb
Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
Julian Scheel, John Keeping, Greg Kroah-Hartman
When USB cable gets disconnected, the undergoing playback/capture
stalls, without any notification to u_audio about the change.
Experiments with a dwc2 gadget revealed that Suspend interrupt is
thrown at cable disconnection, which the gadget framework translates to
calling suspend callback of a function, if it is defined.
Add the suspend callback to f_uac2 function, calling
corresponding method of u_audio in order to stop the respective PCM
streams and to notify subscribed clients at cable disconnection.
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
v3: fixed commit title and msg
---
drivers/usb/gadget/function/f_uac2.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 058a6a0b5444..6be60529cb25 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1432,6 +1432,14 @@ afunc_disable(struct usb_function *fn)
usb_ep_disable(uac2->int_ep);
}
+static void
+afunc_suspend(struct usb_function *fn)
+{
+ struct f_uac2 *uac2 = func_to_uac2(fn);
+
+ u_audio_suspend(&uac2->g_audio);
+}
+
static int
in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
@@ -2103,6 +2111,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
uac2->g_audio.func.set_alt = afunc_set_alt;
uac2->g_audio.func.get_alt = afunc_get_alt;
uac2->g_audio.func.disable = afunc_disable;
+ uac2->g_audio.func.suspend = afunc_suspend;
uac2->g_audio.func.setup = afunc_setup;
uac2->g_audio.func.free_func = afunc_free;
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 10/10] usb: gadget: f_uac1: Add suspend callback
2022-01-10 7:37 [PATCH v4 00/10] usb: gadget: audio: Multiple rates, notify Pavel Hofman
` (8 preceding siblings ...)
2022-01-10 7:37 ` [PATCH v4 09/10] usb: gadget: f_uac2: Add suspend callback Pavel Hofman
@ 2022-01-10 7:37 ` Pavel Hofman
9 siblings, 0 replies; 12+ messages in thread
From: Pavel Hofman @ 2022-01-10 7:37 UTC (permalink / raw)
To: linux-usb
Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
Julian Scheel, John Keeping, Greg Kroah-Hartman
Add suspend callback to f_uac1 function, calling corresponding method
of u_audio in order to stop the respective PCM streams and to notify
subscribed clients about the stop.
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
v3: fixed commit title and msg
---
drivers/usb/gadget/function/f_uac1.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 73df76a6fbe0..1484e5c231d3 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -961,6 +961,14 @@ static void f_audio_disable(struct usb_function *f)
usb_ep_disable(uac1->int_ep);
}
+static void
+f_audio_suspend(struct usb_function *f)
+{
+ struct f_uac1 *uac1 = func_to_uac1(f);
+
+ u_audio_suspend(&uac1->g_audio);
+}
+
/*-------------------------------------------------------------------------*/
static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
{
@@ -1691,6 +1699,7 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
uac1->g_audio.func.get_alt = f_audio_get_alt;
uac1->g_audio.func.setup = f_audio_setup;
uac1->g_audio.func.disable = f_audio_disable;
+ uac1->g_audio.func.suspend = f_audio_suspend;
uac1->g_audio.func.free_func = f_audio_free;
return &uac1->g_audio.func;
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread