All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval
@ 2021-12-20 21:11 Pavel Hofman
  2021-12-20 21:11 ` [PATCH v2 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls Pavel Hofman
                   ` (11 more replies)
  0 siblings, 12 replies; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

Hi all,

This series implements:
* Support for multiple rates in the audio gadget
* Notification of gadget-side alsa processes about playback/capture
start/stop on the host side via Playback/Capture Rate controls.
* Detection of the USB cable disconnection by handling SUSPEND call
in f_uac1/2. The disconnection generates a stop notification.
* Dynamic bInterval calculation for HS and SS

Patches for the multirate support, originally authored by Julian Scheel,
were rebased and modified for the current code base. Julian has
acknowledged the presented patches.

The detection of cable disconnection was discussed with dwc2 maintainer
Minas Harutyunyan who confirmed that the suspend event can be used
(https://lore.kernel.org/all/5aada8e3-f385-0589-8d58-187abd1a924d@synopsys.com/T/).
Tests on dwc2 have confirmed reliable detection, the gadget correctly
reports playback/capture stop at cable disconnection.

The start/stop/current rate notification feature is accompanied by
example implementation of audio gadget controller
https://github.com/pavhofman/gaudio_ctl. The controller also handles
debouncing fast start/stop events when USB host audio driver is loaded
and/or audio daemon re/started.

Changes:
--------

v2: Fixed compilation of "usb: gadget: f_uac1: Support multiple sampling
rates" - added changes for CONFIG_GADGET_UAC1

---------
Julian Scheel (4):
  usb: gadget: u_audio: Support multiple sampling rates
  usb: gadget: f_uac2: Support multiple sampling rates
  usb: gadget: f_uac1: Support multiple sampling rates
  usb: gadget: f_uac2: Renaming Clock Sources to fixed names

Pavel Hofman (7):
  usb: gadget: u_audio: Subdevice 0 for capture ctls
  usb: gadget: u_audio: Rate ctl notifies about current srate
    (0=stopped)
  usb: gadget: u_audio: Stopping PCM substream at capture/playback stop
  usb: gadget: u_audio: Adding suspend call
  usb: gadget: f_uac2: Adding suspend callback
  usb: gadget: f_uac1: Adding suspend callback
  usb: gadget: f_uac2: Determining bInterval for HS and SS

 .../ABI/testing/configfs-usb-gadget-uac1      |   4 +-
 .../ABI/testing/configfs-usb-gadget-uac2      |   4 +-
 Documentation/usb/gadget-testing.rst          |   8 +-
 drivers/usb/gadget/function/f_uac1.c          | 121 ++++++++--
 drivers/usb/gadget/function/f_uac2.c          | 221 ++++++++++++------
 drivers/usb/gadget/function/u_audio.c         | 162 ++++++++++++-
 drivers/usb/gadget/function/u_audio.h         |  12 +-
 drivers/usb/gadget/function/u_uac1.h          |  63 ++++-
 drivers/usb/gadget/function/u_uac2.h          |  62 +++++
 drivers/usb/gadget/function/uac_common.h      |   9 +
 drivers/usb/gadget/legacy/audio.c             |  40 +++-
 11 files changed, 597 insertions(+), 109 deletions(-)
 create mode 100644 drivers/usb/gadget/function/uac_common.h

-- 
2.25.1


^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH v2 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
@ 2021-12-20 21:11 ` Pavel Hofman
  2021-12-20 21:11 ` [PATCH v2 02/11] usb: gadget: u_audio: Support multiple sampling rates Pavel Hofman
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

Both capture and playback alsa devices use subdevice 0. Yet capture-side
ctls are defined for subdevice 1. The patch sets subdevice 0 for them.

Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
 drivers/usb/gadget/function/u_audio.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index c46400be5464..4f6c0049c534 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -1145,7 +1145,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
 			}
 
 			kctl->id.device = pcm->device;
-			kctl->id.subdevice = i;
+			kctl->id.subdevice = 0;
 
 			err = snd_ctl_add(card, kctl);
 			if (err < 0)
@@ -1168,7 +1168,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
 			}
 
 			kctl->id.device = pcm->device;
-			kctl->id.subdevice = i;
+			kctl->id.subdevice = 0;
 
 
 			kctl->tlv.c = u_audio_volume_tlv;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v2 02/11] usb: gadget: u_audio: Support multiple sampling rates
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
  2021-12-20 21:11 ` [PATCH v2 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls Pavel Hofman
@ 2021-12-20 21:11 ` Pavel Hofman
  2021-12-21 11:35   ` John Keeping
  2021-12-20 21:11 ` [PATCH v2 03/11] usb: gadget: f_uac2: " Pavel Hofman
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, 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>
---
 drivers/usb/gadget/function/f_uac1.c     |   2 +
 drivers/usb/gadget/function/f_uac2.c     |   2 +
 drivers/usb/gadget/function/u_audio.c    | 135 +++++++++++++++++++++++
 drivers/usb/gadget/function/u_audio.h    |  10 +-
 drivers/usb/gadget/function/uac_common.h |   9 ++
 5 files changed, 156 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 f8c1f406f19b..1d6e426e5078 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 4f6c0049c534..e737a104156d 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 */
 
 };
@@ -490,6 +493,48 @@ 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;
+	struct snd_uac_chip *uac = audio_dev->uac;
+	struct uac_rtd_params *prm = &uac->c_prm;
+	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 snd_uac_chip *uac = audio_dev->uac;
+	struct uac_rtd_params *prm = &uac->p_prm;
+	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;
@@ -501,6 +546,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);
@@ -593,6 +639,7 @@ int u_audio_start_playback(struct g_audio *audio_dev)
 	int req_len, i;
 	unsigned int p_interval, 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);
@@ -941,6 +988,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 uac_pcm_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 uac_pcm_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] {
@@ -971,6 +1080,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 =		uac_pcm_rate_info,
+		.get =		uac_pcm_rate_get,
+	},
 };
 
 int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
@@ -1184,6 +1300,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] 40+ messages in thread

* [PATCH v2 03/11] usb: gadget: f_uac2: Support multiple sampling rates
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
  2021-12-20 21:11 ` [PATCH v2 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls Pavel Hofman
  2021-12-20 21:11 ` [PATCH v2 02/11] usb: gadget: u_audio: Support multiple sampling rates Pavel Hofman
@ 2021-12-20 21:11 ` Pavel Hofman
  2021-12-21 11:59   ` John Keeping
  2021-12-20 21:11 ` [PATCH v2 04/11] usb: gadget: f_uac1: " Pavel Hofman
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, 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_uac2.

Signed-off-by: Julian Scheel <julian@jusst.de>
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
 .../ABI/testing/configfs-usb-gadget-uac2      |   4 +-
 Documentation/usb/gadget-testing.rst          |   4 +-
 drivers/usb/gadget/function/f_uac2.c          | 118 ++++++++++++++----
 drivers/usb/gadget/function/u_uac2.h          |  62 +++++++++
 drivers/usb/gadget/legacy/audio.c             |  28 +++--
 5 files changed, 177 insertions(+), 39 deletions(-)

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2
index 244d96650123..b20b0471d48a 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 c18113077889..928f60a31544 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 1d6e426e5078..74e32bb146c7 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -70,6 +70,7 @@ struct f_uac2 {
 	/* Interrupt IN endpoint of AC interface */
 	struct usb_ep	*int_ep;
 	atomic_t	int_count;
+	int ctl_id;
 };
 
 static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
@@ -166,7 +167,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 +179,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,
 };
 
@@ -635,12 +636,32 @@ struct cntrl_cur_lay3 {
 };
 
 struct cntrl_range_lay3 {
-	__le16	wNumSubRanges;
 	__le32	dMIN;
 	__le32	dMAX;
 	__le32	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 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 +688,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 +933,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;
 	}
@@ -1210,7 +1231,8 @@ 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;
+	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;
@@ -1222,7 +1244,8 @@ 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;
+	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;
@@ -1502,28 +1525,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 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_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",
@@ -1582,6 +1616,28 @@ 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);
+	struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
+	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->ctl_id == USB_IN_CLK_ID) {
+		opts->p_srate = val;
+		u_audio_set_playback_srate(agdev, opts->p_srate);
+	} else if (uac2->ctl_id == USB_OUT_CLK_ID) {
+		opts->c_srate = val;
+		u_audio_set_capture_srate(agdev, opts->c_srate);
+	}
+}
+
 static void
 out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
 {
@@ -1633,6 +1689,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);
@@ -1642,10 +1699,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->ctl_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));
@@ -1839,10 +1903,10 @@ end:									\
 CONFIGFS_ATTR(f_uac2_opts_, name)
 
 UAC2_ATTRIBUTE(u32, p_chmask);
-UAC2_ATTRIBUTE(u32, p_srate);
+UAC_RATE2_ATTRIBUTE(p_srate);
 UAC2_ATTRIBUTE(u32, p_ssize);
 UAC2_ATTRIBUTE(u32, c_chmask);
-UAC2_ATTRIBUTE(u32, c_srate);
+UAC_RATE2_ATTRIBUTE(c_srate);
 UAC2_ATTRIBUTE_SYNC(c_sync);
 UAC2_ATTRIBUTE(u32, c_ssize);
 UAC2_ATTRIBUTE(u32, req_number);
@@ -1915,9 +1979,11 @@ static struct usb_function_instance *afunc_alloc_inst(void)
 				    &f_uac2_func_type);
 
 	opts->p_chmask = UAC2_DEF_PCHMASK;
+	opts->p_srates[0] = UAC2_DEF_PSRATE;
 	opts->p_srate = UAC2_DEF_PSRATE;
 	opts->p_ssize = UAC2_DEF_PSSIZE;
 	opts->c_chmask = UAC2_DEF_CCHMASK;
+	opts->c_srates[0] = UAC2_DEF_CSRATE;
 	opts->c_srate = 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..8058217322f8 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,9 +36,11 @@
 struct f_uac2_opts {
 	struct usb_function_instance	func_inst;
 	int				p_chmask;
+	int				p_srates[UAC_MAX_RATES];
 	int				p_srate;
 	int				p_ssize;
 	int				c_chmask;
+	int				c_srates[UAC_MAX_RATES];
 	int				c_srate;
 	int				c_ssize;
 	int				c_sync;
@@ -62,4 +65,63 @@ struct f_uac2_opts {
 	int				refcnt;
 };
 
+#define UAC_RATE2_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;					\
+		opts->name = num;				\
+		ret = len;						\
+	};								\
+									\
+end:									\
+	kfree(split_page);						\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(f_uac2_opts_, name)
 #endif
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index a748ed0842e8..58bcb26c7854 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -26,9 +26,10 @@ module_param(p_chmask, uint, S_IRUGO);
 MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
 
 /* Playback Default 48 KHz */
-static int p_srate = UAC2_DEF_PSRATE;
-module_param(p_srate, uint, S_IRUGO);
-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, S_IRUGO);
+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, S_IRUGO);
 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_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, S_IRUGO);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
 
 /* Capture Default 16bits/sample */
 static int c_ssize = UAC2_DEF_CSSIZE;
@@ -244,7 +246,7 @@ static int audio_bind(struct usb_composite_dev *cdev)
 	struct f_uac1_legacy_opts	*uac1_opts;
 #endif
 #endif
-	int			status;
+	int status, i;
 
 #ifndef CONFIG_GADGET_UAC1
 	fi_uac2 = usb_get_function_instance("uac2");
@@ -263,10 +265,18 @@ 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_srate = p_srates[0];
+
 	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_srate = c_srates[0];
+
 	uac2_opts->c_ssize = c_ssize;
 	uac2_opts->req_number = UAC2_DEF_REQ_NUM;
 #else
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v2 04/11] usb: gadget: f_uac1: Support multiple sampling rates
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (2 preceding siblings ...)
  2021-12-20 21:11 ` [PATCH v2 03/11] usb: gadget: f_uac2: " Pavel Hofman
@ 2021-12-20 21:11 ` Pavel Hofman
  2021-12-20 21:11 ` [PATCH v2 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names Pavel Hofman
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, 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>
---
 .../ABI/testing/configfs-usb-gadget-uac1      |   4 +-
 Documentation/usb/gadget-testing.rst          |   4 +-
 drivers/usb/gadget/function/f_uac1.c          | 114 ++++++++++++++----
 drivers/usb/gadget/function/u_uac1.h          |  63 +++++++++-
 drivers/usb/gadget/legacy/audio.c             |  26 ++--
 5 files changed, 176 insertions(+), 35 deletions(-)

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1
index b576b3d6ea6d..a8ecf17f688b 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 928f60a31544..f21cc21d2d7b 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 ccb0e4f41e5d..7fd2b5580374 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,7 @@ struct f_uac1 {
 	/* Interrupt IN endpoint of AC interface */
 	struct usb_ep	*int_ep;
 	atomic_t	int_count;
+	int ctl_id;
 };
 
 static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
@@ -188,16 +190,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 +225,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 +337,31 @@ 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);
+	struct f_uac1_opts *opts = g_audio_to_uac1_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 | 2)) {
+		opts->p_srate = val;
+		u_audio_set_playback_srate(agdev, opts->p_srate);
+	} else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
+		opts->c_srate = val;
+		u_audio_set_capture_srate(agdev, opts->c_srate);
+	}
+}
+
 static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
 {
 	struct g_audio *audio = req->context;
@@ -707,18 +736,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 +781,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_uac1_opts *opts = g_audio_to_uac1_opts(agdev);
+	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 = opts->p_srate;
+			else if (ep == (USB_DIR_OUT | 1))
+				val = opts->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:
@@ -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,23 @@ 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;
 
 	/* allocate instance-specific interface IDs, and patch descriptors */
 	status = usb_interface_id(c, f);
@@ -1298,7 +1364,8 @@ 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;
+	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;
@@ -1311,7 +1378,8 @@ 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;
+	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;
@@ -1417,10 +1485,10 @@ end:									\
 CONFIGFS_ATTR(f_uac1_opts_, name)
 
 UAC1_ATTRIBUTE(u32, c_chmask);
-UAC1_ATTRIBUTE(u32, c_srate);
+UAC_RATE1_ATTRIBUTE(c_srate);
 UAC1_ATTRIBUTE(u32, c_ssize);
 UAC1_ATTRIBUTE(u32, p_chmask);
-UAC1_ATTRIBUTE(u32, p_srate);
+UAC_RATE1_ATTRIBUTE(p_srate);
 UAC1_ATTRIBUTE(u32, p_ssize);
 UAC1_ATTRIBUTE(u32, req_number);
 
@@ -1490,9 +1558,11 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
 
 	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..57dce469b46d 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,9 +31,11 @@
 struct f_uac1_opts {
 	struct usb_function_instance	func_inst;
 	int				c_chmask;
+	int				c_srates[UAC_MAX_RATES];
 	int				c_srate;
 	int				c_ssize;
 	int				p_chmask;
+	int				p_srates[UAC_MAX_RATES];
 	int				p_srate;
 	int				p_ssize;
 
@@ -54,5 +57,63 @@ struct f_uac1_opts {
 	struct mutex			lock;
 	int				refcnt;
 };
-
+#define UAC_RATE1_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;					\
+		opts->name = num;				\
+		ret = len;						\
+	};								\
+									\
+end:									\
+	kfree(split_page);						\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(f_uac1_opts_, name)
 #endif /* __U_UAC1_H */
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index 58bcb26c7854..9a0071cdecb6 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -61,9 +61,10 @@ module_param(p_chmask, uint, S_IRUGO);
 MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
 
 /* Playback Default 48 KHz */
-static int p_srate = UAC1_DEF_PSRATE;
-module_param(p_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+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, S_IRUGO);
+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, S_IRUGO);
 MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
 
 /* Capture Default 48 KHz */
-static int c_srate = UAC1_DEF_CSRATE;
-module_param(c_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+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, S_IRUGO);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
 
 /* Capture Default 16bits/sample */
 static int c_ssize = UAC1_DEF_CSSIZE;
@@ -283,10 +285,18 @@ 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_srate = p_srates[0];
+
 	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_srate = c_srates[0];
+
 	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] 40+ messages in thread

* [PATCH v2 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (3 preceding siblings ...)
  2021-12-20 21:11 ` [PATCH v2 04/11] usb: gadget: f_uac1: " Pavel Hofman
@ 2021-12-20 21:11 ` Pavel Hofman
  2021-12-21 12:02   ` John Keeping
  2021-12-20 21:11 ` [PATCH v2 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped) Pavel Hofman
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

From: Julian Scheel <julian@jusst.de>

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.

Signed-off-by: Julian Scheel <julian@jusst.de>
Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
 drivers/usb/gadget/function/f_uac2.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 74e32bb146c7..ef8e39e80523 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -105,14 +105,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",
@@ -1058,9 +1055,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__);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v2 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped)
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (4 preceding siblings ...)
  2021-12-20 21:11 ` [PATCH v2 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names Pavel Hofman
@ 2021-12-20 21:11 ` Pavel Hofman
  2021-12-21 12:07   ` John Keeping
  2021-12-20 21:11 ` [PATCH v2 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop Pavel Hofman
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, 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
host has stopped 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>
---
 drivers/usb/gadget/function/u_audio.c | 46 ++++++++++++---------------
 1 file changed, 21 insertions(+), 25 deletions(-)

diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index e737a104156d..a6293415c071 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 rep_srate; /* srate reported by snd_kctl_rate */
 
   spinlock_t lock; /* lock for control transfers */
 
@@ -496,8 +497,6 @@ 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 = &uac->c_prm;
 	int i;
 
 	dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
@@ -516,8 +515,6 @@ EXPORT_SYMBOL_GPL(u_audio_set_capture_srate);
 
 int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
 {
-	struct snd_uac_chip *uac = audio_dev->uac;
-	struct uac_rtd_params *prm = &uac->p_prm;
 	struct uac_params *params = &audio_dev->params;
 	int i;
 
@@ -535,6 +532,18 @@ int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
 }
 EXPORT_SYMBOL_GPL(u_audio_set_playback_srate);
 
+static void set_reported_srate(struct uac_rtd_params *prm, int srate)
+{
+	struct snd_kcontrol *kctl = prm->snd_kctl_rate;
+
+	if (prm->rep_srate != srate) {
+		prm->rep_srate = srate;
+		snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				&kctl->id);
+		pr_debug("Setting '%s' to %d", kctl->id.name, srate);
+	}
+}
+
 int u_audio_start_capture(struct g_audio *audio_dev)
 {
 	struct snd_uac_chip *uac = audio_dev->uac;
@@ -574,6 +583,8 @@ int u_audio_start_capture(struct g_audio *audio_dev)
 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 	}
 
+	set_reported_srate(&uac->c_prm, params->c_srate);
+
 	ep_fback = audio_dev->in_ep_fback;
 	if (!ep_fback)
 		return 0;
@@ -619,6 +630,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
 {
 	struct snd_uac_chip *uac = audio_dev->uac;
 
+	set_reported_srate(&uac->c_prm, 0);
 	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);
@@ -691,6 +703,8 @@ int u_audio_start_playback(struct g_audio *audio_dev)
 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 	}
 
+	set_reported_srate(&uac->p_prm, params->p_srate);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(u_audio_start_playback);
@@ -699,6 +713,7 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
 {
 	struct snd_uac_chip *uac = audio_dev->uac;
 
+	set_reported_srate(&uac->p_prm, 0);
 	free_ep(&uac->p_prm, audio_dev->in_ep);
 }
 EXPORT_SYMBOL_GPL(u_audio_stop_playback);
@@ -1001,19 +1016,6 @@ static int get_max_srate(const int *srates)
 	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 uac_pcm_rate_info(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_info *uinfo)
 {
@@ -1030,7 +1032,7 @@ static int uac_pcm_rate_info(struct snd_kcontrol *kcontrol,
 		srates = params->c_srates;
 	else
 		srates = params->p_srates;
-	uinfo->value.integer.min = get_min_srate(srates);
+	uinfo->value.integer.min = 0;
 	uinfo->value.integer.max = get_max_srate(srates);
 	return 0;
 }
@@ -1039,14 +1041,8 @@ static int uac_pcm_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;
+	ucontrol->value.integer.value[0] = prm->rep_srate;
 
 	return 0;
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v2 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (5 preceding siblings ...)
  2021-12-20 21:11 ` [PATCH v2 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped) Pavel Hofman
@ 2021-12-20 21:11 ` Pavel Hofman
  2021-12-21 12:18   ` John Keeping
  2021-12-20 21:11 ` [PATCH v2 08/11] usb: gadget: u_audio: Adding suspend call Pavel Hofman
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

When the USB host stops capture/playback, the corresponding PCM
substream (if activated and running) is stopped and drained.

Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
 drivers/usb/gadget/function/u_audio.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index a6293415c071..9dbce51c2eb7 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -544,6 +544,20 @@ static void set_reported_srate(struct uac_rtd_params *prm, int srate)
 	}
 }
 
+static void stop_substream(struct uac_rtd_params *prm)
+{
+	unsigned long _flags;
+	struct snd_pcm_substream *substream;
+
+	substream = prm->ss;
+	if (substream) {
+		snd_pcm_stream_lock_irqsave(substream, _flags);
+		if (snd_pcm_running(substream))
+			snd_pcm_stop(substream, SNDRV_PCM_STATE_DRAINING);
+		snd_pcm_stream_unlock_irqrestore(substream, _flags);
+	}
+}
+
 int u_audio_start_capture(struct g_audio *audio_dev)
 {
 	struct snd_uac_chip *uac = audio_dev->uac;
@@ -630,6 +644,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
 {
 	struct snd_uac_chip *uac = audio_dev->uac;
 
+	stop_substream(&uac->c_prm);
 	set_reported_srate(&uac->c_prm, 0);
 	if (audio_dev->in_ep_fback)
 		free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback);
@@ -713,6 +728,7 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
 {
 	struct snd_uac_chip *uac = audio_dev->uac;
 
+	stop_substream(&uac->p_prm);
 	set_reported_srate(&uac->p_prm, 0);
 	free_ep(&uac->p_prm, audio_dev->in_ep);
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v2 08/11] usb: gadget: u_audio: Adding suspend call
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (6 preceding siblings ...)
  2021-12-20 21:11 ` [PATCH v2 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop Pavel Hofman
@ 2021-12-20 21:11 ` Pavel Hofman
  2021-12-20 21:11 ` [PATCH v2 09/11] usb: gadget: f_uac2: Adding suspend callback Pavel Hofman
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

Adding exported method u_audio_suspend which stops capture and playback
PCM streams and notifies about zero sample rates. The method does not
free any resources.

Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
 drivers/usb/gadget/function/u_audio.c | 11 +++++++++++
 drivers/usb/gadget/function/u_audio.h |  2 ++
 2 files changed, 13 insertions(+)

diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 9dbce51c2eb7..58e18952953b 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -734,6 +734,17 @@ 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;
+
+	stop_substream(&uac->p_prm);
+	stop_substream(&uac->c_prm);
+	set_reported_srate(&uac->p_prm, 0);
+	set_reported_srate(&uac->c_prm, 0);
+}
+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 76b5b8169444..74e7519d9497 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -128,4 +128,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] 40+ messages in thread

* [PATCH v2 09/11] usb: gadget: f_uac2: Adding suspend callback
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (7 preceding siblings ...)
  2021-12-20 21:11 ` [PATCH v2 08/11] usb: gadget: u_audio: Adding suspend call Pavel Hofman
@ 2021-12-20 21:11 ` Pavel Hofman
  2021-12-20 21:11 ` [PATCH v2 10/11] usb: gadget: f_uac1: " Pavel Hofman
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, 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.

This patch adds 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>
---
 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 ef8e39e80523..984f757de5a4 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1430,6 +1430,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)
 {
@@ -2047,6 +2055,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] 40+ messages in thread

* [PATCH v2 10/11] usb: gadget: f_uac1: Adding suspend callback
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (8 preceding siblings ...)
  2021-12-20 21:11 ` [PATCH v2 09/11] usb: gadget: f_uac2: Adding suspend callback Pavel Hofman
@ 2021-12-20 21:11 ` Pavel Hofman
  2021-12-20 21:11 ` [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS Pavel Hofman
  2021-12-21  7:59 ` [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Greg Kroah-Hartman
  11 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

Adding 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>
---
 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 7fd2b5580374..3eb16f1baf90 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)
 {
@@ -1634,6 +1642,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] 40+ messages in thread

* [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (9 preceding siblings ...)
  2021-12-20 21:11 ` [PATCH v2 10/11] usb: gadget: f_uac1: " Pavel Hofman
@ 2021-12-20 21:11 ` Pavel Hofman
  2021-12-21 12:29   ` John Keeping
  2021-12-21  7:59 ` [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Greg Kroah-Hartman
  11 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2021-12-20 21:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Pavel Hofman, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

So far bInterval for HS and SS was fixed at 4, disallowing faster
samplerates. The patch determines the largest bInterval (4 to 1) for
which the required bandwidth of the max samplerate fits the max allowed
packet size. If the required bandwidth exceeds max bandwidth for
single-packet mode (ep->mc=1), bInterval is left at 1.

The FS mode is left at fixed bInterval=1.

Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
---
 drivers/usb/gadget/function/f_uac2.c | 90 +++++++++++++++++-----------
 1 file changed, 55 insertions(+), 35 deletions(-)

diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 984f757de5a4..e72f6f42e1b7 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -333,7 +333,7 @@ static struct usb_endpoint_descriptor fs_epout_desc = {
 	.bEndpointAddress = USB_DIR_OUT,
 	/* .bmAttributes = DYNAMIC */
 	/* .wMaxPacketSize = DYNAMIC */
-	.bInterval = 1,
+	/* .bInterval = DYNAMIC */
 };
 
 static struct usb_endpoint_descriptor hs_epout_desc = {
@@ -342,7 +342,7 @@ static struct usb_endpoint_descriptor hs_epout_desc = {
 
 	/* .bmAttributes = DYNAMIC */
 	/* .wMaxPacketSize = DYNAMIC */
-	.bInterval = 4,
+	/* .bInterval = DYNAMIC */
 };
 
 static struct usb_endpoint_descriptor ss_epout_desc = {
@@ -352,7 +352,7 @@ static struct usb_endpoint_descriptor ss_epout_desc = {
 	.bEndpointAddress = USB_DIR_OUT,
 	/* .bmAttributes = DYNAMIC */
 	/* .wMaxPacketSize = DYNAMIC */
-	.bInterval = 4,
+	/* .bInterval = DYNAMIC */
 };
 
 static struct usb_ss_ep_comp_descriptor ss_epout_desc_comp = {
@@ -467,7 +467,7 @@ static struct usb_endpoint_descriptor fs_epin_desc = {
 	.bEndpointAddress = USB_DIR_IN,
 	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
 	/* .wMaxPacketSize = DYNAMIC */
-	.bInterval = 1,
+	/* .bInterval = DYNAMIC */
 };
 
 static struct usb_endpoint_descriptor hs_epin_desc = {
@@ -476,7 +476,7 @@ static struct usb_endpoint_descriptor hs_epin_desc = {
 
 	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
 	/* .wMaxPacketSize = DYNAMIC */
-	.bInterval = 4,
+	/* .bInterval = DYNAMIC */
 };
 
 static struct usb_endpoint_descriptor ss_epin_desc = {
@@ -486,7 +486,7 @@ static struct usb_endpoint_descriptor ss_epin_desc = {
 	.bEndpointAddress = USB_DIR_IN,
 	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
 	/* .wMaxPacketSize = DYNAMIC */
-	.bInterval = 4,
+	/* .bInterval = DYNAMIC */
 };
 
 static struct usb_ss_ep_comp_descriptor ss_epin_desc_comp = {
@@ -659,29 +659,11 @@ static int get_max_srate(const int *srates)
 	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)
+static int get_max_bw_for_binterval(const struct f_uac2_opts *uac2_opts,
+	u8 binterval, unsigned int factor, bool is_playback)
 {
 	int chmask, srate, ssize;
-	u16 max_size_bw, max_size_ep;
-	unsigned int factor;
-
-	switch (speed) {
-	case USB_SPEED_FULL:
-		max_size_ep = 1023;
-		factor = 1000;
-		break;
-
-	case USB_SPEED_HIGH:
-	case USB_SPEED_SUPER:
-		max_size_ep = 1024;
-		factor = 8000;
-		break;
-
-	default:
-		return -EINVAL;
-	}
+	u16 max_size_bw;
 
 	if (is_playback) {
 		chmask = uac2_opts->p_chmask;
@@ -699,14 +681,52 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
 		srate = srate * (1000 + uac2_opts->fb_max) / 1000;
 		// updated srate is always bigger, therefore DIV_ROUND_UP always yields +1
 		max_size_bw = num_channels(chmask) * ssize *
-			(DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))));
+			(DIV_ROUND_UP(srate, factor / (1 << (binterval - 1))));
 	} else {
 		// adding 1 frame provision for Win10
 		max_size_bw = num_channels(chmask) * ssize *
-			(DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))) + 1);
+			(DIV_ROUND_UP(srate, factor / (1 << (binterval - 1))) + 1);
 	}
+	return max_size_bw;
+}
+
+static int set_ep_max_packet_size_bint(const struct f_uac2_opts *uac2_opts,
+	struct usb_endpoint_descriptor *ep_desc,
+	enum usb_device_speed speed, bool is_playback)
+{
+	u16 max_size_bw, max_size_ep;
+	u8 binterval;
+
+	switch (speed) {
+	case USB_SPEED_FULL:
+		max_size_ep = 1023;
+		// fixed
+		binterval = 1;
+		max_size_bw = get_max_bw_for_binterval(uac2_opts, binterval, 1000,
+			is_playback);
+		break;
+
+	case USB_SPEED_HIGH:
+	case USB_SPEED_SUPER:
+		max_size_ep = 1024;
+		// checking bInterval from 4 (= 1ms) to 1 if the required bandwidth fits
+		for (binterval = 4; binterval > 0; --binterval) {
+			max_size_bw = get_max_bw_for_binterval(uac2_opts, binterval, 8000,
+					is_playback);
+			if (max_size_bw <= max_size_ep) {
+				// found largest bInterval/max_size_bw fitting max_size_ep
+				break;
+			}
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
 	ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw,
 						    max_size_ep));
+	ep_desc->bInterval = binterval;
 
 	return 0;
 }
@@ -1119,42 +1139,42 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 	}
 
 	/* Calculate wMaxPacketSize according to audio bandwidth */
-	ret = set_ep_max_packet_size(uac2_opts, &fs_epin_desc, USB_SPEED_FULL,
+	ret = set_ep_max_packet_size_bint(uac2_opts, &fs_epin_desc, USB_SPEED_FULL,
 				     true);
 	if (ret < 0) {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 		return ret;
 	}
 
-	ret = set_ep_max_packet_size(uac2_opts, &fs_epout_desc, USB_SPEED_FULL,
+	ret = set_ep_max_packet_size_bint(uac2_opts, &fs_epout_desc, USB_SPEED_FULL,
 				     false);
 	if (ret < 0) {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 		return ret;
 	}
 
-	ret = set_ep_max_packet_size(uac2_opts, &hs_epin_desc, USB_SPEED_HIGH,
+	ret = set_ep_max_packet_size_bint(uac2_opts, &hs_epin_desc, USB_SPEED_HIGH,
 				     true);
 	if (ret < 0) {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 		return ret;
 	}
 
-	ret = set_ep_max_packet_size(uac2_opts, &hs_epout_desc, USB_SPEED_HIGH,
+	ret = set_ep_max_packet_size_bint(uac2_opts, &hs_epout_desc, USB_SPEED_HIGH,
 				     false);
 	if (ret < 0) {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 		return ret;
 	}
 
-	ret = set_ep_max_packet_size(uac2_opts, &ss_epin_desc, USB_SPEED_SUPER,
+	ret = set_ep_max_packet_size_bint(uac2_opts, &ss_epin_desc, USB_SPEED_SUPER,
 				     true);
 	if (ret < 0) {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 		return ret;
 	}
 
-	ret = set_ep_max_packet_size(uac2_opts, &ss_epout_desc, USB_SPEED_SUPER,
+	ret = set_ep_max_packet_size_bint(uac2_opts, &ss_epout_desc, USB_SPEED_SUPER,
 				     false);
 	if (ret < 0) {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval
  2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (10 preceding siblings ...)
  2021-12-20 21:11 ` [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS Pavel Hofman
@ 2021-12-21  7:59 ` Greg Kroah-Hartman
  2021-12-22 13:38   ` Pavel Hofman
  11 siblings, 1 reply; 40+ messages in thread
From: Greg Kroah-Hartman @ 2021-12-21  7:59 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet, Julian Scheel

On Mon, Dec 20, 2021 at 10:11:19PM +0100, Pavel Hofman wrote:
> Hi all,
> 
> This series implements:
> * Support for multiple rates in the audio gadget
> * Notification of gadget-side alsa processes about playback/capture
> start/stop on the host side via Playback/Capture Rate controls.
> * Detection of the USB cable disconnection by handling SUSPEND call
> in f_uac1/2. The disconnection generates a stop notification.
> * Dynamic bInterval calculation for HS and SS
> 
> Patches for the multirate support, originally authored by Julian Scheel,
> were rebased and modified for the current code base. Julian has
> acknowledged the presented patches.
> 
> The detection of cable disconnection was discussed with dwc2 maintainer
> Minas Harutyunyan who confirmed that the suspend event can be used
> (https://lore.kernel.org/all/5aada8e3-f385-0589-8d58-187abd1a924d@synopsys.com/T/).
> Tests on dwc2 have confirmed reliable detection, the gadget correctly
> reports playback/capture stop at cable disconnection.
> 
> The start/stop/current rate notification feature is accompanied by
> example implementation of audio gadget controller
> https://github.com/pavhofman/gaudio_ctl. The controller also handles
> debouncing fast start/stop events when USB host audio driver is loaded
> and/or audio daemon re/started.
> 
> Changes:
> --------
> 
> v2: Fixed compilation of "usb: gadget: f_uac1: Support multiple sampling
> rates" - added changes for CONFIG_GADGET_UAC1

I get the following build warning and error with this series applied to
my tree:

drivers/usb/gadget/legacy/audio.c: In function ‘audio_bind’:
drivers/usb/gadget/legacy/audio.c:251:21: error: unused variable ‘i’ [-Werror=unused-variable]
  251 |         int status, i;
      |                     ^
cc1: all warnings being treated as errors

Please fix up.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 02/11] usb: gadget: u_audio: Support multiple sampling rates
  2021-12-20 21:11 ` [PATCH v2 02/11] usb: gadget: u_audio: Support multiple sampling rates Pavel Hofman
@ 2021-12-21 11:35   ` John Keeping
  2021-12-22  7:13     ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2021-12-21 11:35 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

On Mon, Dec 20, 2021 at 10:11:21PM +0100, Pavel Hofman wrote:
> 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>
> ---

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

Why a new header for this - doesn't it belong in u_audio.h?

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 03/11] usb: gadget: f_uac2: Support multiple sampling rates
  2021-12-20 21:11 ` [PATCH v2 03/11] usb: gadget: f_uac2: " Pavel Hofman
@ 2021-12-21 11:59   ` John Keeping
  2021-12-22 10:01     ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2021-12-21 11:59 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

On Mon, Dec 20, 2021 at 10:11:22PM +0100, Pavel Hofman wrote:
> 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_uac2.
> 
> Signed-off-by: Julian Scheel <julian@jusst.de>
> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
> ---
>  .../ABI/testing/configfs-usb-gadget-uac2      |   4 +-
>  Documentation/usb/gadget-testing.rst          |   4 +-
>  drivers/usb/gadget/function/f_uac2.c          | 118 ++++++++++++++----
>  drivers/usb/gadget/function/u_uac2.h          |  62 +++++++++
>  drivers/usb/gadget/legacy/audio.c             |  28 +++--
>  5 files changed, 177 insertions(+), 39 deletions(-)
> 
> diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst
> index c18113077889..928f60a31544 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 1d6e426e5078..74e32bb146c7 100644
> --- a/drivers/usb/gadget/function/f_uac2.c
> +++ b/drivers/usb/gadget/function/f_uac2.c
> @@ -70,6 +70,7 @@ struct f_uac2 {
>  	/* Interrupt IN endpoint of AC interface */
>  	struct usb_ep	*int_ep;
>  	atomic_t	int_count;
> +	int ctl_id;

Control for what?  This is assigned from a variable called clock_id, so
shouldn't that be the name here?

I think this needs a comment to explain that it's transient state that
is only valid during the handling of a single control request.

>  };
>  
>  static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
> @@ -166,7 +167,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 +179,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,
>  };
>  
> @@ -635,12 +636,32 @@ struct cntrl_cur_lay3 {
>  };
>  
>  struct cntrl_range_lay3 {
> -	__le16	wNumSubRanges;
>  	__le32	dMIN;
>  	__le32	dMAX;
>  	__le32	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;

These structures are now inconsistent between cntrl_range_lay2 and
cntrl_range_lay3.  Would it be better to make these flex arrays?  I
guess that will make the code that uses it more complicated, but at the
moment it looks like these are trying to be generic while in reality
being quite specific to the one place that uses them at the moment.

> +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 +688,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 +933,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;
>  	}
> @@ -1210,7 +1231,8 @@ 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;
> +	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;
> @@ -1222,7 +1244,8 @@ 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;
> +	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;
> @@ -1502,28 +1525,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 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_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",
> @@ -1582,6 +1616,28 @@ 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);
> +	struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
> +	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->ctl_id == USB_IN_CLK_ID) {
> +		opts->p_srate = val;

Don't you need to hold opts->lock to change this?

I'm not sure opts should be changed here though - that's the setup phase
and this is "current state", so shouldn't it move to struct f_uac2?

> +		u_audio_set_playback_srate(agdev, opts->p_srate);
> +	} else if (uac2->ctl_id == USB_OUT_CLK_ID) {
> +		opts->c_srate = val;
> +		u_audio_set_capture_srate(agdev, opts->c_srate);
> +	}
> +}
> +
>  static void
>  out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
>  {
> @@ -1633,6 +1689,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);
> @@ -1642,10 +1699,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->ctl_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));
> @@ -1839,10 +1903,10 @@ end:									\
>  CONFIGFS_ATTR(f_uac2_opts_, name)
>  
>  UAC2_ATTRIBUTE(u32, p_chmask);
> -UAC2_ATTRIBUTE(u32, p_srate);
> +UAC_RATE2_ATTRIBUTE(p_srate);

UAC2_RATE_ATTRIBUTE() to match the pattern of the other values here?

>  UAC2_ATTRIBUTE(u32, p_ssize);
>  UAC2_ATTRIBUTE(u32, c_chmask);
> -UAC2_ATTRIBUTE(u32, c_srate);
> +UAC_RATE2_ATTRIBUTE(c_srate);
>  UAC2_ATTRIBUTE_SYNC(c_sync);
>  UAC2_ATTRIBUTE(u32, c_ssize);
>  UAC2_ATTRIBUTE(u32, req_number);
> @@ -1915,9 +1979,11 @@ static struct usb_function_instance *afunc_alloc_inst(void)
>  				    &f_uac2_func_type);
>  
>  	opts->p_chmask = UAC2_DEF_PCHMASK;
> +	opts->p_srates[0] = UAC2_DEF_PSRATE;
>  	opts->p_srate = UAC2_DEF_PSRATE;
>  	opts->p_ssize = UAC2_DEF_PSSIZE;
>  	opts->c_chmask = UAC2_DEF_CCHMASK;
> +	opts->c_srates[0] = UAC2_DEF_CSRATE;
>  	opts->c_srate = 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..8058217322f8 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,9 +36,11 @@
>  struct f_uac2_opts {
>  	struct usb_function_instance	func_inst;
>  	int				p_chmask;
> +	int				p_srates[UAC_MAX_RATES];
>  	int				p_srate;
>  	int				p_ssize;
>  	int				c_chmask;
> +	int				c_srates[UAC_MAX_RATES];
>  	int				c_srate;
>  	int				c_ssize;
>  	int				c_sync;
> @@ -62,4 +65,63 @@ struct f_uac2_opts {
>  	int				refcnt;
>  };
>  
> +#define UAC_RATE2_ATTRIBUTE(name)					\

Why define this in the header?  UAC2_ATTRIBUTE() is in the .c file and
this is only used in one place so no need for it to be in the .h.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names
  2021-12-20 21:11 ` [PATCH v2 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names Pavel Hofman
@ 2021-12-21 12:02   ` John Keeping
  2021-12-22 10:11     ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2021-12-21 12:02 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

The subject should be "Rename ...".

On Mon, Dec 20, 2021 at 10:11:24PM +0100, Pavel Hofman wrote:
> From: Julian Scheel <julian@jusst.de>
> 
> 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.
> 
> Signed-off-by: Julian Scheel <julian@jusst.de>
> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
> ---
>  drivers/usb/gadget/function/f_uac2.c | 10 ++--------
>  1 file changed, 2 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
> index 74e32bb146c7..ef8e39e80523 100644
> --- a/drivers/usb/gadget/function/f_uac2.c
> +++ b/drivers/usb/gadget/function/f_uac2.c
> @@ -105,14 +105,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",

Other values here use title case, so "Input Clock", "Output Clock".

>  	[STR_USB_IT].s = "USBH Out",
>  	[STR_IO_IT].s = "USBD Out",
>  	[STR_USB_OT].s = "USBH In",
> @@ -1058,9 +1055,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__);
> -- 
> 2.25.1
> 

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped)
  2021-12-20 21:11 ` [PATCH v2 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped) Pavel Hofman
@ 2021-12-21 12:07   ` John Keeping
  2021-12-22 10:41     ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2021-12-21 12:07 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

On Mon, Dec 20, 2021 at 10:11:25PM +0100, Pavel Hofman wrote:
> The Playback/Capture ctl currently reports rate value set by USB
> control selector UAC2_CS_CONTROL_SAM_FREQ (fixed for UAC1). When the
> host has stopped 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>
> ---
>  drivers/usb/gadget/function/u_audio.c | 46 ++++++++++++---------------
>  1 file changed, 21 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
> index e737a104156d..a6293415c071 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 rep_srate; /* srate reported by snd_kctl_rate */
>  
>    spinlock_t lock; /* lock for control transfers */
>  
> @@ -496,8 +497,6 @@ 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 = &uac->c_prm;
>  	int i;
>  
>  	dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
> @@ -516,8 +515,6 @@ EXPORT_SYMBOL_GPL(u_audio_set_capture_srate);
>  
>  int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
>  {
> -	struct snd_uac_chip *uac = audio_dev->uac;
> -	struct uac_rtd_params *prm = &uac->p_prm;
>  	struct uac_params *params = &audio_dev->params;
>  	int i;
>  
> @@ -535,6 +532,18 @@ int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
>  }
>  EXPORT_SYMBOL_GPL(u_audio_set_playback_srate);
>  
> +static void set_reported_srate(struct uac_rtd_params *prm, int srate)
> +{
> +	struct snd_kcontrol *kctl = prm->snd_kctl_rate;
> +
> +	if (prm->rep_srate != srate) {
> +		prm->rep_srate = srate;
> +		snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
> +				&kctl->id);
> +		pr_debug("Setting '%s' to %d", kctl->id.name, srate);
> +	}
> +}
> +
>  int u_audio_start_capture(struct g_audio *audio_dev)
>  {
>  	struct snd_uac_chip *uac = audio_dev->uac;
> @@ -574,6 +583,8 @@ int u_audio_start_capture(struct g_audio *audio_dev)
>  			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
>  	}
>  
> +	set_reported_srate(&uac->c_prm, params->c_srate);
> +
>  	ep_fback = audio_dev->in_ep_fback;
>  	if (!ep_fback)
>  		return 0;
> @@ -619,6 +630,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
>  {
>  	struct snd_uac_chip *uac = audio_dev->uac;
>  
> +	set_reported_srate(&uac->c_prm, 0);
>  	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);
> @@ -691,6 +703,8 @@ int u_audio_start_playback(struct g_audio *audio_dev)
>  			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
>  	}
>  
> +	set_reported_srate(&uac->p_prm, params->p_srate);
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(u_audio_start_playback);
> @@ -699,6 +713,7 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
>  {
>  	struct snd_uac_chip *uac = audio_dev->uac;
>  
> +	set_reported_srate(&uac->p_prm, 0);
>  	free_ep(&uac->p_prm, audio_dev->in_ep);
>  }
>  EXPORT_SYMBOL_GPL(u_audio_stop_playback);
> @@ -1001,19 +1016,6 @@ static int get_max_srate(const int *srates)
>  	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 uac_pcm_rate_info(struct snd_kcontrol *kcontrol,
>  				struct snd_ctl_elem_info *uinfo)
>  {
> @@ -1030,7 +1032,7 @@ static int uac_pcm_rate_info(struct snd_kcontrol *kcontrol,
>  		srates = params->c_srates;
>  	else
>  		srates = params->p_srates;
> -	uinfo->value.integer.min = get_min_srate(srates);
> +	uinfo->value.integer.min = 0;
>  	uinfo->value.integer.max = get_max_srate(srates);
>  	return 0;
>  }
> @@ -1039,14 +1041,8 @@ static int uac_pcm_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;
> +	ucontrol->value.integer.value[0] = prm->rep_srate;

This function was only added in patch 2, wouldn't it be better to add
the rate to the right place at that point?

I think uac_params is supposed to be the fixed (user selected)
parameters whereas uac_rtd_params are the varying values that may be
affected by the host, so the current rate belongs in uac_rtd_params.

>  	return 0;
>  }
> -- 
> 2.25.1
> 

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop
  2021-12-20 21:11 ` [PATCH v2 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop Pavel Hofman
@ 2021-12-21 12:18   ` John Keeping
  2021-12-22 12:26     ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2021-12-21 12:18 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

On Mon, Dec 20, 2021 at 10:11:26PM +0100, Pavel Hofman wrote:
> When the USB host stops capture/playback, the corresponding PCM
> substream (if activated and running) is stopped and drained.
> 
> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
> ---
>  drivers/usb/gadget/function/u_audio.c | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
> 
> diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
> index a6293415c071..9dbce51c2eb7 100644
> --- a/drivers/usb/gadget/function/u_audio.c
> +++ b/drivers/usb/gadget/function/u_audio.c
> @@ -544,6 +544,20 @@ static void set_reported_srate(struct uac_rtd_params *prm, int srate)
>  	}
>  }
>  
> +static void stop_substream(struct uac_rtd_params *prm)
> +{
> +	unsigned long _flags;
> +	struct snd_pcm_substream *substream;
> +
> +	substream = prm->ss;
> +	if (substream) {
> +		snd_pcm_stream_lock_irqsave(substream, _flags);
> +		if (snd_pcm_running(substream))
> +			snd_pcm_stop(substream, SNDRV_PCM_STATE_DRAINING);

I'm not sure if this is right (and the series should probably be CC'd to
alsa-devel to check the audio side of this).

DRAINING seems to be right for capture, but for playback should this end
up in state SETUP?  Does this need to handle resuming a paused stream
like snd_pcm_drain() does?

> +		snd_pcm_stream_unlock_irqrestore(substream, _flags);
> +	}
> +}
> +
>  int u_audio_start_capture(struct g_audio *audio_dev)
>  {
>  	struct snd_uac_chip *uac = audio_dev->uac;
> @@ -630,6 +644,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
>  {
>  	struct snd_uac_chip *uac = audio_dev->uac;
>  
> +	stop_substream(&uac->c_prm);
>  	set_reported_srate(&uac->c_prm, 0);
>  	if (audio_dev->in_ep_fback)
>  		free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback);
> @@ -713,6 +728,7 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
>  {
>  	struct snd_uac_chip *uac = audio_dev->uac;
>  
> +	stop_substream(&uac->p_prm);
>  	set_reported_srate(&uac->p_prm, 0);
>  	free_ep(&uac->p_prm, audio_dev->in_ep);
>  }
> -- 
> 2.25.1
> 

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS
  2021-12-20 21:11 ` [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS Pavel Hofman
@ 2021-12-21 12:29   ` John Keeping
  2021-12-22 13:35     ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2021-12-21 12:29 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

On Mon, Dec 20, 2021 at 10:11:30PM +0100, Pavel Hofman wrote:
> So far bInterval for HS and SS was fixed at 4, disallowing faster
> samplerates. The patch determines the largest bInterval (4 to 1) for
> which the required bandwidth of the max samplerate fits the max allowed
> packet size. If the required bandwidth exceeds max bandwidth for
> single-packet mode (ep->mc=1), bInterval is left at 1.

I'm not sure if this is desirable - there are more concerns around the
interval than just whether the bandwidth is available.

The nice thing about having the HS/SS interval at 4 when the FS value is
1 is that these both correspond to 1ms, which means the calculations for
minimum buffer & period sizes are the same for FS/HS/SS.

How do FS transfers work if the bandwidth requirements necessitate a
smaller interval for HS/SS?  Doesn't that mean the FS transfers must be
too big?

I don't think there has ever been a check that the configured sample
size, channel count and interval actually fit in the max packet size for
an endpoint.  Is that something that should be checked to give an error
on bind if the configuration can't work?

> The FS mode is left at fixed bInterval=1.
> 
> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 02/11] usb: gadget: u_audio: Support multiple sampling rates
  2021-12-21 11:35   ` John Keeping
@ 2021-12-22  7:13     ` Pavel Hofman
  2022-01-04 15:32       ` John Keeping
  0 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2021-12-22  7:13 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Jerome Brunet, Julian Scheel,
	Greg Kroah-Hartman

Dne 21. 12. 21 v 12:35 John Keeping napsal(a):
> On Mon, Dec 20, 2021 at 10:11:21PM +0100, Pavel Hofman wrote:
>> 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>
>> ---
> 
>> 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 */
> 
> Why a new header for this - doesn't it belong in u_audio.h?

The constant is used in subsequent patches in f_uac1.c, f_uac2.c, their 
headers u_uac1.h, u_uac2.h, and legacy/audio.c (which already includes 
u_uac1.h/u_uac2.h as needed). Since all occurences must use the same 
value, I did not know how to solve this without introducing a common 
header file, included in the existing headers u_audio.h, u_uac1.h, 
u_uac2.h. If there is a better way, I will be happy to use it, I do not 
like the extra common header file either. Thanks a lot for your help.

Pavel.


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 03/11] usb: gadget: f_uac2: Support multiple sampling rates
  2021-12-21 11:59   ` John Keeping
@ 2021-12-22 10:01     ` Pavel Hofman
  2022-01-04 15:33       ` John Keeping
  0 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2021-12-22 10:01 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman


Dne 21. 12. 21 v 12:59 John Keeping napsal(a):
> On Mon, Dec 20, 2021 at 10:11:22PM +0100, Pavel Hofman wrote:
>> 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_uac2.
>>
>> Signed-off-by: Julian Scheel <julian@jusst.de>
>> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
>> ---
>>   .../ABI/testing/configfs-usb-gadget-uac2      |   4 +-
>>   Documentation/usb/gadget-testing.rst          |   4 +-
>>   drivers/usb/gadget/function/f_uac2.c          | 118 ++++++++++++++----
>>   drivers/usb/gadget/function/u_uac2.h          |  62 +++++++++
>>   drivers/usb/gadget/legacy/audio.c             |  28 +++--
>>   5 files changed, 177 insertions(+), 39 deletions(-)
>>
>> diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst
>> index c18113077889..928f60a31544 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 1d6e426e5078..74e32bb146c7 100644
>> --- a/drivers/usb/gadget/function/f_uac2.c
>> +++ b/drivers/usb/gadget/function/f_uac2.c
>> @@ -70,6 +70,7 @@ struct f_uac2 {
>>   	/* Interrupt IN endpoint of AC interface */
>>   	struct usb_ep	*int_ep;
>>   	atomic_t	int_count;
>> +	int ctl_id;
> 
> Control for what?  This is assigned from a variable called clock_id, so
> shouldn't that be the name here?
> 
> I think this needs a comment to explain that it's transient state that
> is only valid during the handling of a single control request.

Fixed

> 
>>   };
>>   
>>   static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
>> @@ -166,7 +167,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 +179,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,
>>   };
>>   
>> @@ -635,12 +636,32 @@ struct cntrl_cur_lay3 {
>>   };
>>   
>>   struct cntrl_range_lay3 {
>> -	__le16	wNumSubRanges;
>>   	__le32	dMIN;
>>   	__le32	dMAX;
>>   	__le32	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;
> 
> These structures are now inconsistent between cntrl_range_lay2 and
> cntrl_range_lay3.  Would it be better to make these flex arrays?  I
> guess that will make the code that uses it more complicated, but at the
> moment it looks like these are trying to be generic while in reality
> being quite specific to the one place that uses them at the moment.

I am afraid I do not know exactly how to do that. Please can you post an 
example? The rate control requires u32 (u16 is too small). Thanks a lot.

> 
>> +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 +688,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 +933,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;
>>   	}
>> @@ -1210,7 +1231,8 @@ 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;
>> +	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;
>> @@ -1222,7 +1244,8 @@ 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;
>> +	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;
>> @@ -1502,28 +1525,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 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_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",
>> @@ -1582,6 +1616,28 @@ 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);
>> +	struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
>> +	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->ctl_id == USB_IN_CLK_ID) {
>> +		opts->p_srate = val;
> 
> Don't you need to hold opts->lock to change this?
> I'm not sure opts should be changed here though - that's the setup phase
> and this is "current state", so shouldn't it move to struct f_uac2?

OK. I moved the current p_srate/c_srate from struct opts to f_uac2, 
initialized with first value of opts->p_srates/c_srates[0] in 
afunc_bind. The struct f_uac2 has no lock yet. Should I add the lock 
mutex to f_uac2 and be locking f_uac2 access here in 
uac2_cs_control_sam_freq?

> 
>> +		u_audio_set_playback_srate(agdev, opts->p_srate);
>> +	} else if (uac2->ctl_id == USB_OUT_CLK_ID) {
>> +		opts->c_srate = val;
>> +		u_audio_set_capture_srate(agdev, opts->c_srate);
>> +	}
>> +}
>> +
>>   static void
>>   out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
>>   {
>> @@ -1633,6 +1689,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);
>> @@ -1642,10 +1699,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->ctl_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));
>> @@ -1839,10 +1903,10 @@ end:									\
>>   CONFIGFS_ATTR(f_uac2_opts_, name)
>>   
>>   UAC2_ATTRIBUTE(u32, p_chmask);
>> -UAC2_ATTRIBUTE(u32, p_srate);
>> +UAC_RATE2_ATTRIBUTE(p_srate);
> 
> UAC2_RATE_ATTRIBUTE() to match the pattern of the other values here?

Fixed

> 
>>   UAC2_ATTRIBUTE(u32, p_ssize);
>>   UAC2_ATTRIBUTE(u32, c_chmask);
>> -UAC2_ATTRIBUTE(u32, c_srate);
>> +UAC_RATE2_ATTRIBUTE(c_srate);
>>   UAC2_ATTRIBUTE_SYNC(c_sync);
>>   UAC2_ATTRIBUTE(u32, c_ssize);
>>   UAC2_ATTRIBUTE(u32, req_number);
>> @@ -1915,9 +1979,11 @@ static struct usb_function_instance *afunc_alloc_inst(void)
>>   				    &f_uac2_func_type);
>>   
>>   	opts->p_chmask = UAC2_DEF_PCHMASK;
>> +	opts->p_srates[0] = UAC2_DEF_PSRATE;
>>   	opts->p_srate = UAC2_DEF_PSRATE;
>>   	opts->p_ssize = UAC2_DEF_PSSIZE;
>>   	opts->c_chmask = UAC2_DEF_CCHMASK;
>> +	opts->c_srates[0] = UAC2_DEF_CSRATE;
>>   	opts->c_srate = 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..8058217322f8 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,9 +36,11 @@
>>   struct f_uac2_opts {
>>   	struct usb_function_instance	func_inst;
>>   	int				p_chmask;
>> +	int				p_srates[UAC_MAX_RATES];
>>   	int				p_srate;
>>   	int				p_ssize;
>>   	int				c_chmask;
>> +	int				c_srates[UAC_MAX_RATES];
>>   	int				c_srate;
>>   	int				c_ssize;
>>   	int				c_sync;
>> @@ -62,4 +65,63 @@ struct f_uac2_opts {
>>   	int				refcnt;
>>   };
>>   
>> +#define UAC_RATE2_ATTRIBUTE(name)					\
> 
> Why define this in the header?  UAC2_ATTRIBUTE() is in the .c file and
> this is only used in one place so no need for it to be in the .h.

Moved, renamed to UAC2_RATE_ATTRIBUTE.

Thanks,

Pavel.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names
  2021-12-21 12:02   ` John Keeping
@ 2021-12-22 10:11     ` Pavel Hofman
  0 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2021-12-22 10:11 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

Dne 21. 12. 21 v 13:02 John Keeping napsal(a):
> The subject should be "Rename ...".

OK

> 
> On Mon, Dec 20, 2021 at 10:11:24PM +0100, Pavel Hofman wrote:
>> From: Julian Scheel <julian@jusst.de>
>>
>> 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.
>>
>> Signed-off-by: Julian Scheel <julian@jusst.de>
>> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
>> ---
>>   drivers/usb/gadget/function/f_uac2.c | 10 ++--------
>>   1 file changed, 2 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
>> index 74e32bb146c7..ef8e39e80523 100644
>> --- a/drivers/usb/gadget/function/f_uac2.c
>> +++ b/drivers/usb/gadget/function/f_uac2.c
>> @@ -105,14 +105,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",
> 
> Other values here use title case, so "Input Clock", "Output Clock".
> 

OK

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped)
  2021-12-21 12:07   ` John Keeping
@ 2021-12-22 10:41     ` Pavel Hofman
  0 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2021-12-22 10:41 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

Dne 21. 12. 21 v 13:07 John Keeping napsal(a):
> On Mon, Dec 20, 2021 at 10:11:25PM +0100, Pavel Hofman wrote:
>> The Playback/Capture ctl currently reports rate value set by USB
>> control selector UAC2_CS_CONTROL_SAM_FREQ (fixed for UAC1). When the
>> host has stopped 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>
>> ---
>>   drivers/usb/gadget/function/u_audio.c | 46 ++++++++++++---------------
>>   1 file changed, 21 insertions(+), 25 deletions(-)
>>
>> diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
>> index e737a104156d..a6293415c071 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 rep_srate; /* srate reported by snd_kctl_rate */
>>   
>>     spinlock_t lock; /* lock for control transfers */
>>   
>> @@ -496,8 +497,6 @@ 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 = &uac->c_prm;
>>   	int i;
>>   
>>   	dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
>> @@ -516,8 +515,6 @@ EXPORT_SYMBOL_GPL(u_audio_set_capture_srate);
>>   
>>   int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
>>   {
>> -	struct snd_uac_chip *uac = audio_dev->uac;
>> -	struct uac_rtd_params *prm = &uac->p_prm;
>>   	struct uac_params *params = &audio_dev->params;
>>   	int i;
>>   
>> @@ -535,6 +532,18 @@ int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
>>   }
>>   EXPORT_SYMBOL_GPL(u_audio_set_playback_srate);
>>   
>> +static void set_reported_srate(struct uac_rtd_params *prm, int srate)
>> +{
>> +	struct snd_kcontrol *kctl = prm->snd_kctl_rate;
>> +
>> +	if (prm->rep_srate != srate) {
>> +		prm->rep_srate = srate;
>> +		snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
>> +				&kctl->id);
>> +		pr_debug("Setting '%s' to %d", kctl->id.name, srate);
>> +	}
>> +}
>> +
>>   int u_audio_start_capture(struct g_audio *audio_dev)
>>   {
>>   	struct snd_uac_chip *uac = audio_dev->uac;
>> @@ -574,6 +583,8 @@ int u_audio_start_capture(struct g_audio *audio_dev)
>>   			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
>>   	}
>>   
>> +	set_reported_srate(&uac->c_prm, params->c_srate);
>> +
>>   	ep_fback = audio_dev->in_ep_fback;
>>   	if (!ep_fback)
>>   		return 0;
>> @@ -619,6 +630,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
>>   {
>>   	struct snd_uac_chip *uac = audio_dev->uac;
>>   
>> +	set_reported_srate(&uac->c_prm, 0);
>>   	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);
>> @@ -691,6 +703,8 @@ int u_audio_start_playback(struct g_audio *audio_dev)
>>   			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
>>   	}
>>   
>> +	set_reported_srate(&uac->p_prm, params->p_srate);
>> +
>>   	return 0;
>>   }
>>   EXPORT_SYMBOL_GPL(u_audio_start_playback);
>> @@ -699,6 +713,7 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
>>   {
>>   	struct snd_uac_chip *uac = audio_dev->uac;
>>   
>> +	set_reported_srate(&uac->p_prm, 0);
>>   	free_ep(&uac->p_prm, audio_dev->in_ep);
>>   }
>>   EXPORT_SYMBOL_GPL(u_audio_stop_playback);
>> @@ -1001,19 +1016,6 @@ static int get_max_srate(const int *srates)
>>   	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 uac_pcm_rate_info(struct snd_kcontrol *kcontrol,
>>   				struct snd_ctl_elem_info *uinfo)
>>   {
>> @@ -1030,7 +1032,7 @@ static int uac_pcm_rate_info(struct snd_kcontrol *kcontrol,
>>   		srates = params->c_srates;
>>   	else
>>   		srates = params->p_srates;
>> -	uinfo->value.integer.min = get_min_srate(srates);
>> +	uinfo->value.integer.min = 0;
>>   	uinfo->value.integer.max = get_max_srate(srates);
>>   	return 0;
>>   }
>> @@ -1039,14 +1041,8 @@ static int uac_pcm_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;
>> +	ucontrol->value.integer.value[0] = prm->rep_srate;
> 
> This function was only added in patch 2, wouldn't it be better to add
> the rate to the right place at that point?

The patches 2-5 (adding multiple samplerates support to 
u_audio/UAC1/UAC2) set the kcontrol values when the UAC2_CS_CONTROL_SAM_FREQ
selector value is changed from the USB host (without ctl notifications). 
This patch changes the reported value to 0 when no playback/capture is 
running (therefore the new variable prm->rep_srate was added) + 
notifications. That is a new feature which was not in Julian's patchset 
and I believe it should have a separate commit.

> 
> I think uac_params is supposed to be the fixed (user selected)
> parameters whereas uac_rtd_params are the varying values that may be
> affected by the host, so the current rate belongs in uac_rtd_params.

The current u_audio uses uac_params->c_srate/p_srate on a number of 
places. I will insert a separate patch which moves the two fields from 
uac_params to corresponding uac_rtd_params->requested_srate. The patch 
will go just after Julian's "subseries" so that all subsequent patches 
(including this one) already work with uac_rtd_params->requested_srate 
instead of uac_params->c_srate/p_srate.

Thanks,

Pavel.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop
  2021-12-21 12:18   ` John Keeping
@ 2021-12-22 12:26     ` Pavel Hofman
  2021-12-28  9:04       ` [RFC: PATCH " Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2021-12-22 12:26 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

Dne 21. 12. 21 v 13:18 John Keeping napsal(a):
> On Mon, Dec 20, 2021 at 10:11:26PM +0100, Pavel Hofman wrote:
>> When the USB host stops capture/playback, the corresponding PCM
>> substream (if activated and running) is stopped and drained.
>>
>> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
>> ---
>>   drivers/usb/gadget/function/u_audio.c | 16 ++++++++++++++++
>>   1 file changed, 16 insertions(+)
>>
>> diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
>> index a6293415c071..9dbce51c2eb7 100644
>> --- a/drivers/usb/gadget/function/u_audio.c
>> +++ b/drivers/usb/gadget/function/u_audio.c
>> @@ -544,6 +544,20 @@ static void set_reported_srate(struct uac_rtd_params *prm, int srate)
>>   	}
>>   }
>>   
>> +static void stop_substream(struct uac_rtd_params *prm)
>> +{
>> +	unsigned long _flags;
>> +	struct snd_pcm_substream *substream;
>> +
>> +	substream = prm->ss;
>> +	if (substream) {
>> +		snd_pcm_stream_lock_irqsave(substream, _flags);
>> +		if (snd_pcm_running(substream))
>> +			snd_pcm_stop(substream, SNDRV_PCM_STATE_DRAINING);
> 
> I'm not sure if this is right (and the series should probably be CC'd to
> alsa-devel to check the audio side of this).
> 
> DRAINING seems to be right for capture, but for playback should this end
> up in state SETUP?  Does this need to handle resuming a paused stream
> like snd_pcm_drain() does?

Honestly, I do not know. This code comes from a SPDIF receiver driver 
where it handles interrupted incoming SPDIF stream. You are right it is 
related to capture. I will ask the alsa devs about the playback solution 
specifically.

Yes I will CC the next version to alsa-dev just in case.

> 
>> +		snd_pcm_stream_unlock_irqrestore(substream, _flags);
>> +	}
>> +}
>> +
>>   int u_audio_start_capture(struct g_audio *audio_dev)
>>   {
>>   	struct snd_uac_chip *uac = audio_dev->uac;
>> @@ -630,6 +644,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
>>   {
>>   	struct snd_uac_chip *uac = audio_dev->uac;
>>   
>> +	stop_substream(&uac->c_prm);
>>   	set_reported_srate(&uac->c_prm, 0);
>>   	if (audio_dev->in_ep_fback)
>>   		free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback);
>> @@ -713,6 +728,7 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
>>   {
>>   	struct snd_uac_chip *uac = audio_dev->uac;
>>   
>> +	stop_substream(&uac->p_prm);
>>   	set_reported_srate(&uac->p_prm, 0);
>>   	free_ep(&uac->p_prm, audio_dev->in_ep);
>>   }

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS
  2021-12-21 12:29   ` John Keeping
@ 2021-12-22 13:35     ` Pavel Hofman
  2021-12-22 19:50       ` John Keeping
  0 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2021-12-22 13:35 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

Dne 21. 12. 21 v 13:29 John Keeping napsal(a):
> On Mon, Dec 20, 2021 at 10:11:30PM +0100, Pavel Hofman wrote:
>> So far bInterval for HS and SS was fixed at 4, disallowing faster 
>> samplerates. The patch determines the largest bInterval (4 to 1) 
>> for which the required bandwidth of the max samplerate fits the
>> max allowed packet size. If the required bandwidth exceeds max 
>> bandwidth for single-packet mode (ep->mc=1), bInterval is left at 
>> 1.
> 
> I'm not sure if this is desirable - there are more concerns around 
> the interval than just whether the bandwidth is available.
> 
> The nice thing about having the HS/SS interval at 4 when the FS
> value is 1 is that these both correspond to 1ms, which means the 
> calculations for minimum buffer & period sizes are the same for 
> FS/HS/SS.

Please do you see any specific place in u_audio.c where the interval of
1ms is assumed?

* Buffer/period size max limits are fixed
* Bufer min size is calculated from the max_packet_size
* snd_pcm_period_elapsed() is called when the current request fill
overlaps the period boundary:

if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
		snd_pcm_period_elapsed(substream);


The fixed HS bInterval=4 severely limits the available bandwidth,
disallowing even the very basic 192kHz/2ch/24bits config.

In f_uac2.c both HS/SS the max packet size, async EP OUT feedback value, 
as well as async EP IN momentary packet size calculations already take 
into account the bInterval of the respective endpoint.

I have been using bInterval < 4 in most of my tests for almost a year,
testing packet sizes at up to 1024 bytes per 125us uframe, both
directions, and the gadget has been bitperfect for samplerates up to
4MHz (including correctly working async feedback, tested on linux (up to 
4MHz) and windows 10 WASAPI exclusive (up to 1.5MHz). For larger 
samplerates tests I increased the buffers like in the patch below but I 
did it just in case to minimize probability of xruns. It's not part of 
this patchset and should be configured dynamically too, if actually 
needed at all:


diff --git a/drivers/usb/gadget/function/u_audio.c 
b/drivers/usb/gadget/function/u_audio.c
index 58e18952953b..1dedbf324141 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -23,8 +23,8 @@

  #include "u_audio.h"

-#define BUFF_SIZE_MAX  (PAGE_SIZE * 16)
-#define PRD_SIZE_MAX   PAGE_SIZE
+#define BUFF_SIZE_MAX  (PAGE_SIZE * 16 * 64)
+#define PRD_SIZE_MAX   PAGE_SIZE * 64
  #define MIN_PERIODS    4

  enum {
diff --git a/drivers/usb/gadget/function/u_uac2.h 
b/drivers/usb/gadget/function/u_uac2.h
index 8058217322f8..ae485913db46 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -30,7 +30,7 @@
  #define UAC2_DEF_MAX_DB                0               /* 0 dB */
  #define UAC2_DEF_RES_DB                (1*256)         /* 1 dB */

-#define UAC2_DEF_REQ_NUM 2
+#define UAC2_DEF_REQ_NUM 8
  #define UAC2_DEF_INT_REQ_NUM   10

  struct f_uac2_opts {


> 
> How do FS transfers work if the bandwidth requirements necessitate a
>  smaller interval for HS/SS?  Doesn't that mean the FS transfers
> must be too big?

Only UAC2 HS/SS bIntervals are dynamic with this patch, FS stays fixed
at 1ms. For HS/SS  the max packet size is calculated together with the
bInterval, so that the largest bInterval possible to fit the ISOC max
packetsize limits is chosen.

> 
> I don't think there has ever been a check that the configured sample
>  size, channel count and interval actually fit in the max packet
> size for an endpoint.  Is that something that should be checked to
> give an error on bind if the configuration can't work?

The existing code has never had checks for any of that. Actually the
dynamic bInterval calculation in this patch handles the bInterval and
packetsize for configured parameters up to maximum ISOC bandwidth. Next 
version of this patch will at least warn about exceeding the overall 
available bandwidth.

There are many patches to go before the audio gadget becomes fool-proof,
but at least it should be practically usable with these patches (when 
finalized) and the gaudio controller example implementation.

I will send the new patchset version with notes for each patch hopefully 
tomorrow.

Thanks a lot for your help,

Pavel.

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval
  2021-12-21  7:59 ` [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Greg Kroah-Hartman
@ 2021-12-22 13:38   ` Pavel Hofman
  0 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2021-12-22 13:38 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet, Julian Scheel


Dne 21. 12. 21 v 8:59 Greg Kroah-Hartman napsal(a):
> On Mon, Dec 20, 2021 at 10:11:19PM +0100, Pavel Hofman wrote:
>> Hi all,
>>
>> This series implements:
>> * Support for multiple rates in the audio gadget
>> * Notification of gadget-side alsa processes about playback/capture
>> start/stop on the host side via Playback/Capture Rate controls.
>> * Detection of the USB cable disconnection by handling SUSPEND call
>> in f_uac1/2. The disconnection generates a stop notification.
>> * Dynamic bInterval calculation for HS and SS
>>
>> Patches for the multirate support, originally authored by Julian Scheel,
>> were rebased and modified for the current code base. Julian has
>> acknowledged the presented patches.
>>
>> The detection of cable disconnection was discussed with dwc2 maintainer
>> Minas Harutyunyan who confirmed that the suspend event can be used
>> (https://lore.kernel.org/all/5aada8e3-f385-0589-8d58-187abd1a924d@synopsys.com/T/).
>> Tests on dwc2 have confirmed reliable detection, the gadget correctly
>> reports playback/capture stop at cable disconnection.
>>
>> The start/stop/current rate notification feature is accompanied by
>> example implementation of audio gadget controller
>> https://github.com/pavhofman/gaudio_ctl. The controller also handles
>> debouncing fast start/stop events when USB host audio driver is loaded
>> and/or audio daemon re/started.
>>
>> Changes:
>> --------
>>
>> v2: Fixed compilation of "usb: gadget: f_uac1: Support multiple sampling
>> rates" - added changes for CONFIG_GADGET_UAC1
> 
> I get the following build warning and error with this series applied to
> my tree:
> 
> drivers/usb/gadget/legacy/audio.c: In function ‘audio_bind’:
> drivers/usb/gadget/legacy/audio.c:251:21: error: unused variable ‘i’ [-Werror=unused-variable]
>    251 |         int status, i;
>        |                     ^
> cc1: all warnings being treated as errors
> 
> Please fix up.

I am sorry for the inconvenience. I  will send fixed v3 tomorrow.

Thanks for your patience.

Pavel.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS
  2021-12-22 13:35     ` Pavel Hofman
@ 2021-12-22 19:50       ` John Keeping
  2021-12-23  7:09         ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2021-12-22 19:50 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

On Wed, 22 Dec 2021 14:35:07 +0100
Pavel Hofman <pavel.hofman@ivitera.com> wrote:

> Dne 21. 12. 21 v 13:29 John Keeping napsal(a):
> > On Mon, Dec 20, 2021 at 10:11:30PM +0100, Pavel Hofman wrote:  
> >> So far bInterval for HS and SS was fixed at 4, disallowing faster 
> >> samplerates. The patch determines the largest bInterval (4 to 1) 
> >> for which the required bandwidth of the max samplerate fits the
> >> max allowed packet size. If the required bandwidth exceeds max 
> >> bandwidth for single-packet mode (ep->mc=1), bInterval is left at 
> >> 1.  
> > 
> > I'm not sure if this is desirable - there are more concerns around 
> > the interval than just whether the bandwidth is available.
> > 
> > The nice thing about having the HS/SS interval at 4 when the FS
> > value is 1 is that these both correspond to 1ms, which means the 
> > calculations for minimum buffer & period sizes are the same for 
> > FS/HS/SS.  
> 
> Please do you see any specific place in u_audio.c where the interval of
> 1ms is assumed?
> 
> * Buffer/period size max limits are fixed
> * Bufer min size is calculated from the max_packet_size
> * snd_pcm_period_elapsed() is called when the current request fill
> overlaps the period boundary:
> 
> if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
> 		snd_pcm_period_elapsed(substream);
> 
> 
> The fixed HS bInterval=4 severely limits the available bandwidth,
> disallowing even the very basic 192kHz/2ch/24bits config.

Yes, but the problem is if the device enumerates as full-speed the
capability is no longer there.

I agree that is unlikely to be a problem in real use, but I think it
deserves consideration.

For the last few years I've been using bInterval == 1 but I also have a
hack to disable full-speed operation completely.  In my case this is
because I want to minimise latency and with the 1ms interval for FS the
minimum ALSA period size is too large.

Basically, I agree with wanting a smaller bInterval, but I want it for a
different reason and I'd like to see a patch that addresses both our use
cases ;-)

> In f_uac2.c both HS/SS the max packet size, async EP OUT feedback value, 
> as well as async EP IN momentary packet size calculations already take 
> into account the bInterval of the respective endpoint.
> 
> I have been using bInterval < 4 in most of my tests for almost a year,
> testing packet sizes at up to 1024 bytes per 125us uframe, both
> directions, and the gadget has been bitperfect for samplerates up to
> 4MHz (including correctly working async feedback, tested on linux (up to 
> 4MHz) and windows 10 WASAPI exclusive (up to 1.5MHz). For larger 
> samplerates tests I increased the buffers like in the patch below but I 
> did it just in case to minimize probability of xruns. It's not part of 
> this patchset and should be configured dynamically too, if actually 
> needed at all:

This is another case of a different trade-off - I use PREEMPT_RT to
minimise xruns and run with a period of 16 samples.

> > How do FS transfers work if the bandwidth requirements necessitate a
> >  smaller interval for HS/SS?  Doesn't that mean the FS transfers
> > must be too big?  
> 
> Only UAC2 HS/SS bIntervals are dynamic with this patch, FS stays fixed
> at 1ms. For HS/SS  the max packet size is calculated together with the
> bInterval, so that the largest bInterval possible to fit the ISOC max
> packetsize limits is chosen.

I'd really like to see FS mode become unsupported when the packet size
is too big.  This is a slight issue right now (for 1023 vs 1024) but
this patch makes it significantly worse for the high bandwidth case.

Right now I have this patch which is a hack but does at least result in
an error for the host when trying to enable audio at FS.  It would be
really nice to properly handle this in the composite gadget core so that
the audio function is exposed only at HS/SS with proper
DT_OTHER_SPEED_CONFIG handling, but currently that code assumes that the
same number of descriptors is provided for each speed.

-- 8< --
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 36fa6ef0581b..b4946409b38a 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1356,6 +1356,9 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
 		return 0;
 	}
 
+	if (gadget->speed < USB_SPEED_HIGH && alt)
+		return -EINVAL;
+
 	if (intf == uac2->as_out_intf) {
 		uac2->as_out_alt = alt;
 
-- >8 --

> > I don't think there has ever been a check that the configured sample
> >  size, channel count and interval actually fit in the max packet
> > size for an endpoint.  Is that something that should be checked to
> > give an error on bind if the configuration can't work?  
> 
> The existing code has never had checks for any of that. Actually the
> dynamic bInterval calculation in this patch handles the bInterval and
> packetsize for configured parameters up to maximum ISOC bandwidth. Next 
> version of this patch will at least warn about exceeding the overall 
> available bandwidth.
> 
> There are many patches to go before the audio gadget becomes fool-proof,
> but at least it should be practically usable with these patches (when 
> finalized) and the gaudio controller example implementation.

Agreed, and I really appreciate the improvements you're making here.

The reason I suggested the new checks here is that it makes a lot of
sense if the bInterval value is exposed as part of the configfs
interface.  It means there's one extra value to set for high bandwidth
operation, rather than having it "just work", but I think the
latency/bandwidth tradeoffs here mean that there's no way for the kernel
to select the right value for all scenarios, so really we need to let
the user tell us what they want.


Regards,
John

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS
  2021-12-22 19:50       ` John Keeping
@ 2021-12-23  7:09         ` Pavel Hofman
  2022-01-04 15:33           ` John Keeping
  0 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2021-12-23  7:09 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman


Dne 22. 12. 21 v 20:50 John Keeping napsal(a):
> On Wed, 22 Dec 2021 14:35:07 +0100
> Pavel Hofman <pavel.hofman@ivitera.com> wrote:
> 
>> Dne 21. 12. 21 v 13:29 John Keeping napsal(a):
>>> On Mon, Dec 20, 2021 at 10:11:30PM +0100, Pavel Hofman wrote:
>>>> So far bInterval for HS and SS was fixed at 4, disallowing faster
>>>> samplerates. The patch determines the largest bInterval (4 to 1)
>>>> for which the required bandwidth of the max samplerate fits the
>>>> max allowed packet size. If the required bandwidth exceeds max
>>>> bandwidth for single-packet mode (ep->mc=1), bInterval is left at
>>>> 1.
>>>
>>> I'm not sure if this is desirable - there are more concerns around
>>> the interval than just whether the bandwidth is available.
>>>
>>> The nice thing about having the HS/SS interval at 4 when the FS
>>> value is 1 is that these both correspond to 1ms, which means the
>>> calculations for minimum buffer & period sizes are the same for
>>> FS/HS/SS.
>>
>> Please do you see any specific place in u_audio.c where the interval of
>> 1ms is assumed?
>>
>> * Buffer/period size max limits are fixed
>> * Bufer min size is calculated from the max_packet_size
>> * snd_pcm_period_elapsed() is called when the current request fill
>> overlaps the period boundary:
>>
>> if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
>> 		snd_pcm_period_elapsed(substream);
>>
>>
>> The fixed HS bInterval=4 severely limits the available bandwidth,
>> disallowing even the very basic 192kHz/2ch/24bits config.
> 
> Yes, but the problem is if the device enumerates as full-speed the
> capability is no longer there.
> 
> I agree that is unlikely to be a problem in real use, but I think it
> deserves consideration.

Please can you elaborate more on that? If the device enumerates as FS, 
it's automatically limited to bInterval=1 fullspeed frame. Not much more 
to do, IIUC.

> 
> For the last few years I've been using bInterval == 1 but I also have a
> hack to disable full-speed operation completely.  In my case this is
> because I want to minimise latency and with the 1ms interval for FS the
> minimum ALSA period size is too large.
> 
> Basically, I agree with wanting a smaller bInterval, but I want it for a
> different reason and I'd like to see a patch that addresses both our use
> cases ;-)
> 
>> In f_uac2.c both HS/SS the max packet size, async EP OUT feedback value,
>> as well as async EP IN momentary packet size calculations already take
>> into account the bInterval of the respective endpoint.
>>
>> I have been using bInterval < 4 in most of my tests for almost a year,
>> testing packet sizes at up to 1024 bytes per 125us uframe, both
>> directions, and the gadget has been bitperfect for samplerates up to
>> 4MHz (including correctly working async feedback, tested on linux (up to
>> 4MHz) and windows 10 WASAPI exclusive (up to 1.5MHz). For larger
>> samplerates tests I increased the buffers like in the patch below but I
>> did it just in case to minimize probability of xruns. It's not part of
>> this patchset and should be configured dynamically too, if actually
>> needed at all:
> 
> This is another case of a different trade-off - I use PREEMPT_RT to
> minimise xruns and run with a period of 16 samples.
> 
>>> How do FS transfers work if the bandwidth requirements necessitate a
>>>   smaller interval for HS/SS?  Doesn't that mean the FS transfers
>>> must be too big?
>>
>> Only UAC2 HS/SS bIntervals are dynamic with this patch, FS stays fixed
>> at 1ms. For HS/SS  the max packet size is calculated together with the
>> bInterval, so that the largest bInterval possible to fit the ISOC max
>> packetsize limits is chosen.
> 
> I'd really like to see FS mode become unsupported when the packet size
> is too big.  This is a slight issue right now (for 1023 vs 1024) but
> this patch makes it significantly worse for the high bandwidth case.

I am afraid I do not understand what the patch makes worse. For FS it 
always yields bInterval=1 and the corresponding maxPacketSize, a 
calculation of which has not been changed by the patch.
> 
> Right now I have this patch which is a hack but does at least result in
> an error for the host when trying to enable audio at FS.  It would be
> really nice to properly handle this in the composite gadget core so that
> the audio function is exposed only at HS/SS with proper
> DT_OTHER_SPEED_CONFIG handling, but currently that code assumes that the
> same number of descriptors is provided for each speed.
> 
> -- 8< --
> diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
> index 36fa6ef0581b..b4946409b38a 100644
> --- a/drivers/usb/gadget/function/f_uac2.c
> +++ b/drivers/usb/gadget/function/f_uac2.c
> @@ -1356,6 +1356,9 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
>   		return 0;
>   	}
>   
> +	if (gadget->speed < USB_SPEED_HIGH && alt)
> +		return -EINVAL;
> +
>   	if (intf == uac2->as_out_intf) {
>   		uac2->as_out_alt = alt;
>   
> -- >8 --
> 
>>> I don't think there has ever been a check that the configured sample
>>>   size, channel count and interval actually fit in the max packet
>>> size for an endpoint.  Is that something that should be checked to
>>> give an error on bind if the configuration can't work?
>>
>> The existing code has never had checks for any of that. Actually the
>> dynamic bInterval calculation in this patch handles the bInterval and
>> packetsize for configured parameters up to maximum ISOC bandwidth. Next
>> version of this patch will at least warn about exceeding the overall
>> available bandwidth.
>>
>> There are many patches to go before the audio gadget becomes fool-proof,
>> but at least it should be practically usable with these patches (when
>> finalized) and the gaudio controller example implementation.
> 
> Agreed, and I really appreciate the improvements you're making here.
> 
> The reason I suggested the new checks here is that it makes a lot of
> sense if the bInterval value is exposed as part of the configfs
> interface.  It means there's one extra value to set for high bandwidth
> operation, rather than having it "just work", but I think the
> latency/bandwidth tradeoffs here mean that there's no way for the kernel
> to select the right value for all scenarios, so really we need to let
> the user tell us what they want.

OK. IMO it could be easily resolved by having the upper bInterval limit 
for the largest-fitting bInterval check of my patch configurable by new 
configfs max_bint, defaulting to the existing value of 4. I would leave 
the default (4), minimizing CPU load, you would set max_bint=1, 
minimizing latency. Any max_bint value in between would work, while 
still having available the automated calculation if lower bint value was 
required for the given parameters.

In addition, the final check dev_warn can be chanched to dev_err + 
returning EINVAL, providing the discussed sanity check. The check would 
work for FS as well as for HS/SS.

This change could be split to three patches:

1. the automated calculation with fixed max_bint=4 - my current patch, 
dev_warn if max_size_bw > max_size_ep, max_size_bw limited to 
max_size_ep, no error, only warning.

2. adding the uac2_opts max_bint, using in set_ep_max_packet_size_bint

3. turning the sanity check warning to failing error: changing the 
dev_warn in the final check to dev_err+ returning error.

So the final version could look like this:


static int set_ep_max_packet_size_bint(struct device *dev, const struct 
f_uac2_opts *uac2_opts,
	struct usb_endpoint_descriptor *ep_desc,
	enum usb_device_speed speed, bool is_playback)
{
	u16 max_size_bw, max_size_ep;
	u8 bint;

	switch (speed) {
	case USB_SPEED_FULL:
		max_size_ep = 1023;
		// fixed
		bint = 1;
		max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 1000, is_playback);
		break;

	case USB_SPEED_HIGH:
	case USB_SPEED_SUPER:
		max_size_ep = 1024;
		// checking bInterval from max configured bInterval to 1 if the 
required bandwidth fits
		for (bint = uac2_opts->max_bint; bint > 0; --bint) {
			max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 8000, is_playback);
			if (max_size_bw <= max_size_ep)
				break;
		}
		break;

	default:
		return -EINVAL;
	}

	if (max_size_bw > max_size_ep) {
		dev_err(dev,
			"Req. maxpcktsize %d at bInterval 1= > max ISOC %d, cannot comply!\n",
			max_size_bw, max_size_ep);
		return -EINVAL;
	}

	ep_desc->wMaxPacketSize = cpu_to_le16(max_size_bw);
	ep_desc->bInterval = bint;

	return 0;
}

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [RFC: PATCH v2 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop
  2021-12-22 12:26     ` Pavel Hofman
@ 2021-12-28  9:04       ` Pavel Hofman
  0 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2021-12-28  9:04 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman, Yunhao Tian, Jack Pham



Dne 22. 12. 21 v 13:26 Pavel Hofman napsal(a):
> Dne 21. 12. 21 v 13:18 John Keeping napsal(a):
>> On Mon, Dec 20, 2021 at 10:11:26PM +0100, Pavel Hofman wrote:
>>> When the USB host stops capture/playback, the corresponding PCM
>>> substream (if activated and running) is stopped and drained.
>>>
>>> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
>>> ---
>>>   drivers/usb/gadget/function/u_audio.c | 16 ++++++++++++++++
>>>   1 file changed, 16 insertions(+)
>>>
>>> diff --git a/drivers/usb/gadget/function/u_audio.c 
>>> b/drivers/usb/gadget/function/u_audio.c
>>> index a6293415c071..9dbce51c2eb7 100644
>>> --- a/drivers/usb/gadget/function/u_audio.c
>>> +++ b/drivers/usb/gadget/function/u_audio.c
>>> @@ -544,6 +544,20 @@ static void set_reported_srate(struct 
>>> uac_rtd_params *prm, int srate)
>>>       }
>>>   }
>>> +static void stop_substream(struct uac_rtd_params *prm)
>>> +{
>>> +    unsigned long _flags;
>>> +    struct snd_pcm_substream *substream;
>>> +
>>> +    substream = prm->ss;
>>> +    if (substream) {
>>> +        snd_pcm_stream_lock_irqsave(substream, _flags);
>>> +        if (snd_pcm_running(substream))
>>> +            snd_pcm_stop(substream, SNDRV_PCM_STATE_DRAINING);
>>
>> I'm not sure if this is right (and the series should probably be CC'd to
>> alsa-devel to check the audio side of this).
>>
>> DRAINING seems to be right for capture, but for playback should this end
>> up in state SETUP?  Does this need to handle resuming a paused stream
>> like snd_pcm_drain() does?
> 
> Honestly, I do not know. This code comes from a SPDIF receiver driver 
> where it handles interrupted incoming SPDIF stream. You are right it is 
> related to capture. I will ask the alsa devs about the playback solution 
> specifically.
> 
> Yes I will CC the next version to alsa-dev just in case.

I have not received a response from the alsa devels yet, however based 
on existing variants in the other drivers in the kernel there are two 
options:

if (substream) {
   snd_pcm_stream_lock_irqsave(substream, _flags);
   if (snd_pcm_running(substream)) {
     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
       snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
     else
       snd_pcm_stop(substream, SNDRV_PCM_STATE_DRAINING);
   }
   snd_pcm_stream_unlock_irqrestore(substream, _flags);
}


When testing playback/capture, sox does not exit with this version (only 
reporting an error), recovering if playback/capture resume within 
standard alsa blocking read/write timeout. Aplay/arecord exit.

Another option is

if (substream) {
   snd_pcm_stream_lock_irqsave(substream, _flags);
   if (snd_pcm_running(substream))
     snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
   snd_pcm_stream_unlock_irqrestore(substream, _flags);
}

Here both sox and aplay/arecord exit when the pcm stream is stopped. 
 From my use case I like this option better.

Or the gadget can use SETUP/DRAINING for host-side playback/capture stop 
and DISCONNECTED for USB cable unplugging. But I am not sure whether 
from the gadget side POW there is a any real difference between the host 
stopping playback/capture and cable disconnection.

Also this patch would change behaviour of the gadget for existing 
installations, as the existing version just stops data 
delivery/consumption. Maybe some installations already count on this 
behaviour. Perhaps the snd_pcm_stop call should be an opt-in via a new 
configfs parameter?

Please can existing gadget users comment on this?

Thanks a lot,

Pavel.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 02/11] usb: gadget: u_audio: Support multiple sampling rates
  2021-12-22  7:13     ` Pavel Hofman
@ 2022-01-04 15:32       ` John Keeping
  2022-01-05 10:55         ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2022-01-04 15:32 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Jerome Brunet, Julian Scheel,
	Greg Kroah-Hartman

On Wed, Dec 22, 2021 at 08:13:01AM +0100, Pavel Hofman wrote:
> Dne 21. 12. 21 v 12:35 John Keeping napsal(a):
> > On Mon, Dec 20, 2021 at 10:11:21PM +0100, Pavel Hofman wrote:
> > > 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>
> > > ---
> > 
> > > 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 */
> > 
> > Why a new header for this - doesn't it belong in u_audio.h?
> 
> The constant is used in subsequent patches in f_uac1.c, f_uac2.c, their
> headers u_uac1.h, u_uac2.h, and legacy/audio.c (which already includes
> u_uac1.h/u_uac2.h as needed). Since all occurences must use the same value,
> I did not know how to solve this without introducing a common header file,
> included in the existing headers u_audio.h, u_uac1.h, u_uac2.h. If there is
> a better way, I will be happy to use it, I do not like the extra common
> header file either. Thanks a lot for your help.

Ah, right - I hadn't accounted for UAC1.

Do you think anyone is using UAC1 these days?  I wonder if it makes
sense to just drop those changes and focus on UAC2.


John

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 03/11] usb: gadget: f_uac2: Support multiple sampling rates
  2021-12-22 10:01     ` Pavel Hofman
@ 2022-01-04 15:33       ` John Keeping
  2022-01-05 12:20         ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2022-01-04 15:33 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

On Wed, Dec 22, 2021 at 11:01:16AM +0100, Pavel Hofman wrote:
> 
> Dne 21. 12. 21 v 12:59 John Keeping napsal(a):
> > On Mon, Dec 20, 2021 at 10:11:22PM +0100, Pavel Hofman wrote:
> > > 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_uac2.
> > > 
> > > Signed-off-by: Julian Scheel <julian@jusst.de>
> > > Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
> > > ---
[snip]
> > >   };
> > >   static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
> > > @@ -166,7 +167,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 +179,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,
> > >   };
> > > @@ -635,12 +636,32 @@ struct cntrl_cur_lay3 {
> > >   };
> > >   struct cntrl_range_lay3 {
> > > -	__le16	wNumSubRanges;
> > >   	__le32	dMIN;
> > >   	__le32	dMAX;
> > >   	__le32	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;
> > 
> > These structures are now inconsistent between cntrl_range_lay2 and
> > cntrl_range_lay3.  Would it be better to make these flex arrays?  I
> > guess that will make the code that uses it more complicated, but at the
> > moment it looks like these are trying to be generic while in reality
> > being quite specific to the one place that uses them at the moment.
> 
> I am afraid I do not know exactly how to do that. Please can you post an
> example? The rate control requires u32 (u16 is too small). Thanks a lot.

After the change in this patch, we end up with:

	struct cntrl_range_lay2 {
		__le16	wNumSubRanges;
		__le16	wMIN;
		__le16	wMAX;
		__le16	wRES;
	} __packed;

	struct cntrl_range_lay3 {
		__le32	dMIN;
		__le32	dMAX;
		__le32	dRES;
	} __packed;

so there are two structures with similar names but totally different
structure, which I think risks confusion in the future.

I wonder if DECLARE_UAC2_FEATURE_UNIT_DESCRIPTOR in linux/usb/audio-v2.h
provides inspiration here, so potentially something like:

	#define DECLARE_UAC2_CNTRL_RANGE_LAY3(n)	\
		struct uac2_cntrl_range_lay3_##n {	\
			__le16 wNumSubRanges;		\
			struct cntrl_range_le32 r[n];	\
		} __packed;

	DECLARE_UAC2_CNTRL_RANGE_LAY3(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 +688,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 +933,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;
> > >   	}
> > > @@ -1210,7 +1231,8 @@ 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;
> > > +	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;
> > > @@ -1222,7 +1244,8 @@ 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;
> > > +	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;
> > > @@ -1502,28 +1525,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 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_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",
> > > @@ -1582,6 +1616,28 @@ 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);
> > > +	struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
> > > +	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->ctl_id == USB_IN_CLK_ID) {
> > > +		opts->p_srate = val;
> > 
> > Don't you need to hold opts->lock to change this?
> > I'm not sure opts should be changed here though - that's the setup phase
> > and this is "current state", so shouldn't it move to struct f_uac2?
> 
> OK. I moved the current p_srate/c_srate from struct opts to f_uac2,
> initialized with first value of opts->p_srates/c_srates[0] in afunc_bind.
> The struct f_uac2 has no lock yet. Should I add the lock mutex to f_uac2 and
> be locking f_uac2 access here in uac2_cs_control_sam_freq?

Could we move this into struct uac_rtd_params and use the existing lock
there to guard it?

It would need accessor functions as that structure's local to u_audio.c,
but there's already u_audio_set_playback_srate() so that isn't a big
change.


John

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS
  2021-12-23  7:09         ` Pavel Hofman
@ 2022-01-04 15:33           ` John Keeping
  2022-01-05 11:31             ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2022-01-04 15:33 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

On Thu, Dec 23, 2021 at 08:09:39AM +0100, Pavel Hofman wrote:
> 
> Dne 22. 12. 21 v 20:50 John Keeping napsal(a):
> > On Wed, 22 Dec 2021 14:35:07 +0100
> > Pavel Hofman <pavel.hofman@ivitera.com> wrote:
> > 
> > > Dne 21. 12. 21 v 13:29 John Keeping napsal(a):
> > > > On Mon, Dec 20, 2021 at 10:11:30PM +0100, Pavel Hofman wrote:
> > > > > So far bInterval for HS and SS was fixed at 4, disallowing faster
> > > > > samplerates. The patch determines the largest bInterval (4 to 1)
> > > > > for which the required bandwidth of the max samplerate fits the
> > > > > max allowed packet size. If the required bandwidth exceeds max
> > > > > bandwidth for single-packet mode (ep->mc=1), bInterval is left at
> > > > > 1.
> > > > 
> > > > I'm not sure if this is desirable - there are more concerns around
> > > > the interval than just whether the bandwidth is available.
> > > > 
> > > > The nice thing about having the HS/SS interval at 4 when the FS
> > > > value is 1 is that these both correspond to 1ms, which means the
> > > > calculations for minimum buffer & period sizes are the same for
> > > > FS/HS/SS.
> > > 
> > > Please do you see any specific place in u_audio.c where the interval of
> > > 1ms is assumed?
> > > 
> > > * Buffer/period size max limits are fixed
> > > * Bufer min size is calculated from the max_packet_size
> > > * snd_pcm_period_elapsed() is called when the current request fill
> > > overlaps the period boundary:
> > > 
> > > if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
> > > 		snd_pcm_period_elapsed(substream);
> > > 
> > > 
> > > The fixed HS bInterval=4 severely limits the available bandwidth,
> > > disallowing even the very basic 192kHz/2ch/24bits config.
> > 
> > Yes, but the problem is if the device enumerates as full-speed the
> > capability is no longer there.
> > 
> > I agree that is unlikely to be a problem in real use, but I think it
> > deserves consideration.
> 
> Please can you elaborate more on that? If the device enumerates as FS, it's
> automatically limited to bInterval=1 fullspeed frame. Not much more to do,
> IIUC.

Say we have 8 channels of 32-bit audio at 96kHz which requires 3072000
bytes per second, and IIUC we need bInterval == 2 for this to work at
HS.

But for FS there is no way to provide that bandwidth, so if the gadget
happens to be connected to a host that is only capable of FS then the
configuration just doesn't work.  I think what actually happens given
the current code is that each packet ends up truncated and parts of the
audio data are just dropped.

> > For the last few years I've been using bInterval == 1 but I also have a
> > hack to disable full-speed operation completely.  In my case this is
> > because I want to minimise latency and with the 1ms interval for FS the
> > minimum ALSA period size is too large.
> > 
> > Basically, I agree with wanting a smaller bInterval, but I want it for a
> > different reason and I'd like to see a patch that addresses both our use
> > cases ;-)
> > 
> > > In f_uac2.c both HS/SS the max packet size, async EP OUT feedback value,
> > > as well as async EP IN momentary packet size calculations already take
> > > into account the bInterval of the respective endpoint.
> > > 
> > > I have been using bInterval < 4 in most of my tests for almost a year,
> > > testing packet sizes at up to 1024 bytes per 125us uframe, both
> > > directions, and the gadget has been bitperfect for samplerates up to
> > > 4MHz (including correctly working async feedback, tested on linux (up to
> > > 4MHz) and windows 10 WASAPI exclusive (up to 1.5MHz). For larger
> > > samplerates tests I increased the buffers like in the patch below but I
> > > did it just in case to minimize probability of xruns. It's not part of
> > > this patchset and should be configured dynamically too, if actually
> > > needed at all:
> > 
> > This is another case of a different trade-off - I use PREEMPT_RT to
> > minimise xruns and run with a period of 16 samples.
> > 
> > > > How do FS transfers work if the bandwidth requirements necessitate a
> > > >   smaller interval for HS/SS?  Doesn't that mean the FS transfers
> > > > must be too big?
> > > 
> > > Only UAC2 HS/SS bIntervals are dynamic with this patch, FS stays fixed
> > > at 1ms. For HS/SS  the max packet size is calculated together with the
> > > bInterval, so that the largest bInterval possible to fit the ISOC max
> > > packetsize limits is chosen.
> > 
> > I'd really like to see FS mode become unsupported when the packet size
> > is too big.  This is a slight issue right now (for 1023 vs 1024) but
> > this patch makes it significantly worse for the high bandwidth case.
> 
> I am afraid I do not understand what the patch makes worse. For FS it always
> yields bInterval=1 and the corresponding maxPacketSize, a calculation of
> which has not been changed by the patch.

See my comment above - before the difference was really 1023 vs 1024 so
it's possible to hit a problematic configuration but it's a smaller
window.

I really think we should avoid a configuration that mostly works but
fails in surprising ways (for example, working at HS but resulting in
corrupt data at FS because there just isn't sufficient bandwidth for the
sample rate, sample size and channel configuration selected).

> > Right now I have this patch which is a hack but does at least result in
> > an error for the host when trying to enable audio at FS.  It would be
> > really nice to properly handle this in the composite gadget core so that
> > the audio function is exposed only at HS/SS with proper
> > DT_OTHER_SPEED_CONFIG handling, but currently that code assumes that the
> > same number of descriptors is provided for each speed.
> > 
> > -- 8< --
> > diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
> > index 36fa6ef0581b..b4946409b38a 100644
> > --- a/drivers/usb/gadget/function/f_uac2.c
> > +++ b/drivers/usb/gadget/function/f_uac2.c
> > @@ -1356,6 +1356,9 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
> >   		return 0;
> >   	}
> > +	if (gadget->speed < USB_SPEED_HIGH && alt)
> > +		return -EINVAL;
> > +
> >   	if (intf == uac2->as_out_intf) {
> >   		uac2->as_out_alt = alt;
> > -- >8 --
> > 
> > > > I don't think there has ever been a check that the configured sample
> > > >   size, channel count and interval actually fit in the max packet
> > > > size for an endpoint.  Is that something that should be checked to
> > > > give an error on bind if the configuration can't work?
> > > 
> > > The existing code has never had checks for any of that. Actually the
> > > dynamic bInterval calculation in this patch handles the bInterval and
> > > packetsize for configured parameters up to maximum ISOC bandwidth. Next
> > > version of this patch will at least warn about exceeding the overall
> > > available bandwidth.
> > > 
> > > There are many patches to go before the audio gadget becomes fool-proof,
> > > but at least it should be practically usable with these patches (when
> > > finalized) and the gaudio controller example implementation.
> > 
> > Agreed, and I really appreciate the improvements you're making here.
> > 
> > The reason I suggested the new checks here is that it makes a lot of
> > sense if the bInterval value is exposed as part of the configfs
> > interface.  It means there's one extra value to set for high bandwidth
> > operation, rather than having it "just work", but I think the
> > latency/bandwidth tradeoffs here mean that there's no way for the kernel
> > to select the right value for all scenarios, so really we need to let
> > the user tell us what they want.
> 
> OK. IMO it could be easily resolved by having the upper bInterval limit for
> the largest-fitting bInterval check of my patch configurable by new configfs
> max_bint, defaulting to the existing value of 4. I would leave the default
> (4), minimizing CPU load, you would set max_bint=1, minimizing latency. Any
> max_bint value in between would work, while still having available the
> automated calculation if lower bint value was required for the given
> parameters.
> 
> In addition, the final check dev_warn can be chanched to dev_err + returning
> EINVAL, providing the discussed sanity check. The check would work for FS as
> well as for HS/SS.
> 
> This change could be split to three patches:
> 
> 1. the automated calculation with fixed max_bint=4 - my current patch,
> dev_warn if max_size_bw > max_size_ep, max_size_bw limited to max_size_ep,
> no error, only warning.
> 
> 2. adding the uac2_opts max_bint, using in set_ep_max_packet_size_bint
> 
> 3. turning the sanity check warning to failing error: changing the dev_warn
> in the final check to dev_err+ returning error.
> 
> So the final version could look like this:

This sounds good to me.

But I think you'll hit the FS vs HS bandwidth issue described above when
trying anything that requires a lower bInterval ;-)

I really think the answer to this is an extra patch/series that disables
operation at full speed when more bandwidth is required.  Ideally that
would include enhancing the gadget core to support different descriptors
for different speeds (which is already somewhat supported as other speed
config descriptors are returned correctly, but IIRC there's an
assumption that the number of descriptors is the same across all
speeds).

> static int set_ep_max_packet_size_bint(struct device *dev, const struct
> f_uac2_opts *uac2_opts,
> 	struct usb_endpoint_descriptor *ep_desc,
> 	enum usb_device_speed speed, bool is_playback)
> {
> 	u16 max_size_bw, max_size_ep;
> 	u8 bint;
> 
> 	switch (speed) {
> 	case USB_SPEED_FULL:
> 		max_size_ep = 1023;
> 		// fixed
> 		bint = 1;
> 		max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 1000, is_playback);
> 		break;
> 
> 	case USB_SPEED_HIGH:
> 	case USB_SPEED_SUPER:
> 		max_size_ep = 1024;
> 		// checking bInterval from max configured bInterval to 1 if the required
> bandwidth fits
> 		for (bint = uac2_opts->max_bint; bint > 0; --bint) {
> 			max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 8000, is_playback);
> 			if (max_size_bw <= max_size_ep)
> 				break;
> 		}
> 		break;
> 
> 	default:
> 		return -EINVAL;
> 	}
> 
> 	if (max_size_bw > max_size_ep) {
> 		dev_err(dev,
> 			"Req. maxpcktsize %d at bInterval 1= > max ISOC %d, cannot comply!\n",
> 			max_size_bw, max_size_ep);
> 		return -EINVAL;
> 	}
> 
> 	ep_desc->wMaxPacketSize = cpu_to_le16(max_size_bw);
> 	ep_desc->bInterval = bint;
> 
> 	return 0;
> }

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 02/11] usb: gadget: u_audio: Support multiple sampling rates
  2022-01-04 15:32       ` John Keeping
@ 2022-01-05 10:55         ` Pavel Hofman
  0 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2022-01-05 10:55 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Jerome Brunet, Julian Scheel,
	Greg Kroah-Hartman



Dne 04. 01. 22 v 16:32 John Keeping napsal(a):
> On Wed, Dec 22, 2021 at 08:13:01AM +0100, Pavel Hofman wrote:
>> Dne 21. 12. 21 v 12:35 John Keeping napsal(a):
>>> On Mon, Dec 20, 2021 at 10:11:21PM +0100, Pavel Hofman wrote:
>>>> 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>
>>>> ---
>>>
>>>> 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 */
>>>
>>> Why a new header for this - doesn't it belong in u_audio.h?
>>
>> The constant is used in subsequent patches in f_uac1.c, f_uac2.c, their
>> headers u_uac1.h, u_uac2.h, and legacy/audio.c (which already includes
>> u_uac1.h/u_uac2.h as needed). Since all occurences must use the same value,
>> I did not know how to solve this without introducing a common header file,
>> included in the existing headers u_audio.h, u_uac1.h, u_uac2.h. If there is
>> a better way, I will be happy to use it, I do not like the extra common
>> header file either. Thanks a lot for your help.
> 
> Ah, right - I hadn't accounted for UAC1.
> 
> Do you think anyone is using UAC1 these days?  I wonder if it makes
> sense to just drop those changes and focus on UAC2.
> 

I have no problem with dropping the UAC1 support from my current 
patches. That fact is UAC2 will likely receive further features and 
porting to UAC1 and testing takes effort. I have no need for UAC1, but 
its future course is not for me to decide :-)

Any opinion from UAC1 users?

Thanks,

Pavel.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS
  2022-01-04 15:33           ` John Keeping
@ 2022-01-05 11:31             ` Pavel Hofman
  2022-01-06 14:32               ` John Keeping
  0 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2022-01-05 11:31 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman


Dne 04. 01. 22 v 16:33 John Keeping napsal(a):
> On Thu, Dec 23, 2021 at 08:09:39AM +0100, Pavel Hofman wrote:
>>
>> Dne 22. 12. 21 v 20:50 John Keeping napsal(a):
>>> On Wed, 22 Dec 2021 14:35:07 +0100
>>> Pavel Hofman <pavel.hofman@ivitera.com> wrote:
>>>
>>>> Dne 21. 12. 21 v 13:29 John Keeping napsal(a):
>>>>> On Mon, Dec 20, 2021 at 10:11:30PM +0100, Pavel Hofman wrote:
>>>>>> So far bInterval for HS and SS was fixed at 4, disallowing faster
>>>>>> samplerates. The patch determines the largest bInterval (4 to 1)
>>>>>> for which the required bandwidth of the max samplerate fits the
>>>>>> max allowed packet size. If the required bandwidth exceeds max
>>>>>> bandwidth for single-packet mode (ep->mc=1), bInterval is left at
>>>>>> 1.
>>>>>
>>>>> I'm not sure if this is desirable - there are more concerns around
>>>>> the interval than just whether the bandwidth is available.
>>>>>
>>>>> The nice thing about having the HS/SS interval at 4 when the FS
>>>>> value is 1 is that these both correspond to 1ms, which means the
>>>>> calculations for minimum buffer & period sizes are the same for
>>>>> FS/HS/SS.
>>>>
>>>> Please do you see any specific place in u_audio.c where the interval of
>>>> 1ms is assumed?
>>>>
>>>> * Buffer/period size max limits are fixed
>>>> * Bufer min size is calculated from the max_packet_size
>>>> * snd_pcm_period_elapsed() is called when the current request fill
>>>> overlaps the period boundary:
>>>>
>>>> if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
>>>> 		snd_pcm_period_elapsed(substream);
>>>>
>>>>
>>>> The fixed HS bInterval=4 severely limits the available bandwidth,
>>>> disallowing even the very basic 192kHz/2ch/24bits config.
>>>
>>> Yes, but the problem is if the device enumerates as full-speed the
>>> capability is no longer there.
>>>
>>> I agree that is unlikely to be a problem in real use, but I think it
>>> deserves consideration.
>>
>> Please can you elaborate more on that? If the device enumerates as FS, it's
>> automatically limited to bInterval=1 fullspeed frame. Not much more to do,
>> IIUC.
> 
> Say we have 8 channels of 32-bit audio at 96kHz which requires 3072000
> bytes per second, and IIUC we need bInterval == 2 for this to work at
> HS.
> 
> But for FS there is no way to provide that bandwidth, so if the gadget
> happens to be connected to a host that is only capable of FS then the
> configuration just doesn't work.  I think what actually happens given
> the current code is that each packet ends up truncated and parts of the
> audio data are just dropped.

Yes. The current version will drop data for both FS (inevitably) and HS, 
eventhough there is no technical reason to drop data for HS as the 
bandwidth is available.

> 
>>> For the last few years I've been using bInterval == 1 but I also have a
>>> hack to disable full-speed operation completely.  In my case this is
>>> because I want to minimise latency and with the 1ms interval for FS the
>>> minimum ALSA period size is too large.
>>>
>>> Basically, I agree with wanting a smaller bInterval, but I want it for a
>>> different reason and I'd like to see a patch that addresses both our use
>>> cases ;-)
>>>
>>>> In f_uac2.c both HS/SS the max packet size, async EP OUT feedback value,
>>>> as well as async EP IN momentary packet size calculations already take
>>>> into account the bInterval of the respective endpoint.
>>>>
>>>> I have been using bInterval < 4 in most of my tests for almost a year,
>>>> testing packet sizes at up to 1024 bytes per 125us uframe, both
>>>> directions, and the gadget has been bitperfect for samplerates up to
>>>> 4MHz (including correctly working async feedback, tested on linux (up to
>>>> 4MHz) and windows 10 WASAPI exclusive (up to 1.5MHz). For larger
>>>> samplerates tests I increased the buffers like in the patch below but I
>>>> did it just in case to minimize probability of xruns. It's not part of
>>>> this patchset and should be configured dynamically too, if actually
>>>> needed at all:
>>>
>>> This is another case of a different trade-off - I use PREEMPT_RT to
>>> minimise xruns and run with a period of 16 samples.
>>>
>>>>> How do FS transfers work if the bandwidth requirements necessitate a
>>>>>    smaller interval for HS/SS?  Doesn't that mean the FS transfers
>>>>> must be too big?
>>>>
>>>> Only UAC2 HS/SS bIntervals are dynamic with this patch, FS stays fixed
>>>> at 1ms. For HS/SS  the max packet size is calculated together with the
>>>> bInterval, so that the largest bInterval possible to fit the ISOC max
>>>> packetsize limits is chosen.
>>>
>>> I'd really like to see FS mode become unsupported when the packet size
>>> is too big.  This is a slight issue right now (for 1023 vs 1024) but
>>> this patch makes it significantly worse for the high bandwidth case.
>>
>> I am afraid I do not understand what the patch makes worse. For FS it always
>> yields bInterval=1 and the corresponding maxPacketSize, a calculation of
>> which has not been changed by the patch.
> 
> See my comment above - before the difference was really 1023 vs 1024 so
> it's possible to hit a problematic configuration but it's a smaller
> window.

For me the problematic configuration is the one which does not work, 
which this feature actually tries to reduce (for HS).

> 
> I really think we should avoid a configuration that mostly works but
> fails in surprising ways (for example, working at HS but resulting in
> corrupt data at FS because there just isn't sufficient bandwidth for the
> sample rate, sample size and channel configuration selected).


I understand your reasoning. See below.

> 
>>> Right now I have this patch which is a hack but does at least result in
>>> an error for the host when trying to enable audio at FS.  It would be
>>> really nice to properly handle this in the composite gadget core so that
>>> the audio function is exposed only at HS/SS with proper
>>> DT_OTHER_SPEED_CONFIG handling, but currently that code assumes that the
>>> same number of descriptors is provided for each speed.
>>>
>>> -- 8< --
>>> diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
>>> index 36fa6ef0581b..b4946409b38a 100644
>>> --- a/drivers/usb/gadget/function/f_uac2.c
>>> +++ b/drivers/usb/gadget/function/f_uac2.c
>>> @@ -1356,6 +1356,9 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
>>>    		return 0;
>>>    	}
>>> +	if (gadget->speed < USB_SPEED_HIGH && alt)
>>> +		return -EINVAL;
>>> +
>>>    	if (intf == uac2->as_out_intf) {
>>>    		uac2->as_out_alt = alt;
>>> -- >8 --
>>>
>>>>> I don't think there has ever been a check that the configured sample
>>>>>    size, channel count and interval actually fit in the max packet
>>>>> size for an endpoint.  Is that something that should be checked to
>>>>> give an error on bind if the configuration can't work?
>>>>
>>>> The existing code has never had checks for any of that. Actually the
>>>> dynamic bInterval calculation in this patch handles the bInterval and
>>>> packetsize for configured parameters up to maximum ISOC bandwidth. Next
>>>> version of this patch will at least warn about exceeding the overall
>>>> available bandwidth.
>>>>
>>>> There are many patches to go before the audio gadget becomes fool-proof,
>>>> but at least it should be practically usable with these patches (when
>>>> finalized) and the gaudio controller example implementation.
>>>
>>> Agreed, and I really appreciate the improvements you're making here.
>>>
>>> The reason I suggested the new checks here is that it makes a lot of
>>> sense if the bInterval value is exposed as part of the configfs
>>> interface.  It means there's one extra value to set for high bandwidth
>>> operation, rather than having it "just work", but I think the
>>> latency/bandwidth tradeoffs here mean that there's no way for the kernel
>>> to select the right value for all scenarios, so really we need to let
>>> the user tell us what they want.
>>
>> OK. IMO it could be easily resolved by having the upper bInterval limit for
>> the largest-fitting bInterval check of my patch configurable by new configfs
>> max_bint, defaulting to the existing value of 4. I would leave the default
>> (4), minimizing CPU load, you would set max_bint=1, minimizing latency. Any
>> max_bint value in between would work, while still having available the
>> automated calculation if lower bint value was required for the given
>> parameters.
>>
>> In addition, the final check dev_warn can be chanched to dev_err + returning
>> EINVAL, providing the discussed sanity check. The check would work for FS as
>> well as for HS/SS.
>>
>> This change could be split to three patches:
>>
>> 1. the automated calculation with fixed max_bint=4 - my current patch,
>> dev_warn if max_size_bw > max_size_ep, max_size_bw limited to max_size_ep,
>> no error, only warning.
>>
>> 2. adding the uac2_opts max_bint, using in set_ep_max_packet_size_bint
>>
>> 3. turning the sanity check warning to failing error: changing the dev_warn
>> in the final check to dev_err+ returning error.
>>
>> So the final version could look like this:
> 
> This sounds good to me.
> 
> But I think you'll hit the FS vs HS bandwidth issue described above when
> trying anything that requires a lower bInterval ;-)

Well, the FS bandwidth is just too small, but IMO it's not a reason to 
limit HS too, as is limited now.

> 
> I really think the answer to this is an extra patch/series that disables
> operation at full speed when more bandwidth is required.  Ideally that
> would include enhancing the gadget core to support different descriptors
> for different speeds (which is already somewhat supported as other speed
> config descriptors are returned correctly, but IIRC there's an
> assumption that the number of descriptors is the same across all
> speeds).

More patches in that area are certainly required.

What I can do:

* Separate the maxbandwidth patch series from the 
multiple-rates/rate-notification series to:

* patch 1 - the automated bint/maxPacketSize calculation with fixed 
max_bint=4 (for HS/SS), warning if bandwidth exceeded
* patch 2 - adding the uac2_opts max_bint to allow lower latency (I 
already have this prepared)
* patch 3 - failing f_uac2 load when requested params exceed HS/SS 
limits (for bInterval=1). The calculation method is always called for 
all FS/HS/SS now, so I cannot fail the FS check as it would break HS/SS.

I do not know how (and at what point) to disable operation for FS. 
Perhaps you could follow up with suitable patch, tested on your 
FS-capable HW?

Thanks a lot,

Pavel.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 03/11] usb: gadget: f_uac2: Support multiple sampling rates
  2022-01-04 15:33       ` John Keeping
@ 2022-01-05 12:20         ` Pavel Hofman
  2022-01-05 12:44           ` John Keeping
  0 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2022-01-05 12:20 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

Dne 04. 01. 22 v 16:33 John Keeping napsal(a):
> On Wed, Dec 22, 2021 at 11:01:16AM +0100, Pavel Hofman wrote:
>>
>> Dne 21. 12. 21 v 12:59 John Keeping napsal(a):
>>> On Mon, Dec 20, 2021 at 10:11:22PM +0100, Pavel Hofman wrote:
>>>> 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_uac2.
>>>>
>>>> Signed-off-by: Julian Scheel <julian@jusst.de>
>>>> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
>>>> ---
> [snip]
>>>>    };
>>>>    static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
>>>> @@ -166,7 +167,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 +179,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,
>>>>    };
>>>> @@ -635,12 +636,32 @@ struct cntrl_cur_lay3 {
>>>>    };
>>>>    struct cntrl_range_lay3 {
>>>> -	__le16	wNumSubRanges;
>>>>    	__le32	dMIN;
>>>>    	__le32	dMAX;
>>>>    	__le32	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;
>>>
>>> These structures are now inconsistent between cntrl_range_lay2 and
>>> cntrl_range_lay3.  Would it be better to make these flex arrays?  I
>>> guess that will make the code that uses it more complicated, but at the
>>> moment it looks like these are trying to be generic while in reality
>>> being quite specific to the one place that uses them at the moment.
>>
>> I am afraid I do not know exactly how to do that. Please can you post an
>> example? The rate control requires u32 (u16 is too small). Thanks a lot.
> 
> After the change in this patch, we end up with:
> 
> 	struct cntrl_range_lay2 {
> 		__le16	wNumSubRanges;
> 		__le16	wMIN;
> 		__le16	wMAX;
> 		__le16	wRES;
> 	} __packed;
> 
> 	struct cntrl_range_lay3 {
> 		__le32	dMIN;
> 		__le32	dMAX;
> 		__le32	dRES;
> 	} __packed;
> 
> so there are two structures with similar names but totally different
> structure, which I think risks confusion in the future.
> 
> I wonder if DECLARE_UAC2_FEATURE_UNIT_DESCRIPTOR in linux/usb/audio-v2.h
> provides inspiration here, so potentially something like:
> 
> 	#define DECLARE_UAC2_CNTRL_RANGE_LAY3(n)	\
> 		struct uac2_cntrl_range_lay3_##n {	\
> 			__le16 wNumSubRanges;		\
> 			struct cntrl_range_le32 r[n];	\
> 		} __packed;
> 
> 	DECLARE_UAC2_CNTRL_RANGE_LAY3(UAC_MAX_RATES);

Thanks, I will try to follow your suggestion in the next patchset version.

> 
>>>> +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 +688,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 +933,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;
>>>>    	}
>>>> @@ -1210,7 +1231,8 @@ 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;
>>>> +	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;
>>>> @@ -1222,7 +1244,8 @@ 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;
>>>> +	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;
>>>> @@ -1502,28 +1525,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 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_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",
>>>> @@ -1582,6 +1616,28 @@ 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);
>>>> +	struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
>>>> +	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->ctl_id == USB_IN_CLK_ID) {
>>>> +		opts->p_srate = val;
>>>
>>> Don't you need to hold opts->lock to change this?
>>> I'm not sure opts should be changed here though - that's the setup phase
>>> and this is "current state", so shouldn't it move to struct f_uac2?
>>
>> OK. I moved the current p_srate/c_srate from struct opts to f_uac2,
>> initialized with first value of opts->p_srates/c_srates[0] in afunc_bind.
>> The struct f_uac2 has no lock yet. Should I add the lock mutex to f_uac2 and
>> be locking f_uac2 access here in uac2_cs_control_sam_freq?
> 
> Could we move this into struct uac_rtd_params and use the existing lock
> there to guard it?
> 
> It would need accessor functions as that structure's local to u_audio.c,
> but there's already u_audio_set_playback_srate() so that isn't a big
> change.

I have already moved p_/c_srate from uac_params to uac_rtd_params in 
u_audio.c in the next version of the patchset. But IIUC the currently 
selected playback/capture rate is required within f_uac2 too, in 
in_rq_cur() in:

if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
	...
	if (entity_id == USB_IN_CLK_ID)
		c.dCUR = cpu_to_le32(p_srate);
	else if (entity_id == USB_OUT_CLK_ID)
		c.dCUR = cpu_to_le32(c_srate);
	...
}

Thanks,

Pavel.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 03/11] usb: gadget: f_uac2: Support multiple sampling rates
  2022-01-05 12:20         ` Pavel Hofman
@ 2022-01-05 12:44           ` John Keeping
  2022-01-05 14:05             ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2022-01-05 12:44 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

On Wed, Jan 05, 2022 at 01:20:01PM +0100, Pavel Hofman wrote:
> Dne 04. 01. 22 v 16:33 John Keeping napsal(a):
> > On Wed, Dec 22, 2021 at 11:01:16AM +0100, Pavel Hofman wrote:
> > > 
> > > Dne 21. 12. 21 v 12:59 John Keeping napsal(a):
> > > > On Mon, Dec 20, 2021 at 10:11:22PM +0100, Pavel Hofman wrote:
> > > > > 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_uac2.
> > > > > 
> > > > > Signed-off-by: Julian Scheel <julian@jusst.de>
> > > > > Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
> > > > > ---
> > [snip]
> > > > >    };
> > > > >    static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
> > > > > @@ -166,7 +167,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 +179,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,
> > > > >    };
> > > > > @@ -635,12 +636,32 @@ struct cntrl_cur_lay3 {
> > > > >    };
> > > > >    struct cntrl_range_lay3 {
> > > > > -	__le16	wNumSubRanges;
> > > > >    	__le32	dMIN;
> > > > >    	__le32	dMAX;
> > > > >    	__le32	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;
> > > > 
> > > > These structures are now inconsistent between cntrl_range_lay2 and
> > > > cntrl_range_lay3.  Would it be better to make these flex arrays?  I
> > > > guess that will make the code that uses it more complicated, but at the
> > > > moment it looks like these are trying to be generic while in reality
> > > > being quite specific to the one place that uses them at the moment.
> > > 
> > > I am afraid I do not know exactly how to do that. Please can you post an
> > > example? The rate control requires u32 (u16 is too small). Thanks a lot.
> > 
> > After the change in this patch, we end up with:
> > 
> > 	struct cntrl_range_lay2 {
> > 		__le16	wNumSubRanges;
> > 		__le16	wMIN;
> > 		__le16	wMAX;
> > 		__le16	wRES;
> > 	} __packed;
> > 
> > 	struct cntrl_range_lay3 {
> > 		__le32	dMIN;
> > 		__le32	dMAX;
> > 		__le32	dRES;
> > 	} __packed;
> > 
> > so there are two structures with similar names but totally different
> > structure, which I think risks confusion in the future.
> > 
> > I wonder if DECLARE_UAC2_FEATURE_UNIT_DESCRIPTOR in linux/usb/audio-v2.h
> > provides inspiration here, so potentially something like:
> > 
> > 	#define DECLARE_UAC2_CNTRL_RANGE_LAY3(n)	\
> > 		struct uac2_cntrl_range_lay3_##n {	\
> > 			__le16 wNumSubRanges;		\
> > 			struct cntrl_range_le32 r[n];	\
> > 		} __packed;
> > 
> > 	DECLARE_UAC2_CNTRL_RANGE_LAY3(UAC_MAX_RATES);
> 
> Thanks, I will try to follow your suggestion in the next patchset version.
> 
> > 
> > > > > +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 +688,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 +933,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;
> > > > >    	}
> > > > > @@ -1210,7 +1231,8 @@ 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;
> > > > > +	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;
> > > > > @@ -1222,7 +1244,8 @@ 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;
> > > > > +	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;
> > > > > @@ -1502,28 +1525,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 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_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",
> > > > > @@ -1582,6 +1616,28 @@ 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);
> > > > > +	struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
> > > > > +	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->ctl_id == USB_IN_CLK_ID) {
> > > > > +		opts->p_srate = val;
> > > > 
> > > > Don't you need to hold opts->lock to change this?
> > > > I'm not sure opts should be changed here though - that's the setup phase
> > > > and this is "current state", so shouldn't it move to struct f_uac2?
> > > 
> > > OK. I moved the current p_srate/c_srate from struct opts to f_uac2,
> > > initialized with first value of opts->p_srates/c_srates[0] in afunc_bind.
> > > The struct f_uac2 has no lock yet. Should I add the lock mutex to f_uac2 and
> > > be locking f_uac2 access here in uac2_cs_control_sam_freq?
> > 
> > Could we move this into struct uac_rtd_params and use the existing lock
> > there to guard it?
> > 
> > It would need accessor functions as that structure's local to u_audio.c,
> > but there's already u_audio_set_playback_srate() so that isn't a big
> > change.
> 
> I have already moved p_/c_srate from uac_params to uac_rtd_params in
> u_audio.c in the next version of the patchset. But IIUC the currently
> selected playback/capture rate is required within f_uac2 too, in in_rq_cur()
> in:
> 
> if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
> 	...
> 	if (entity_id == USB_IN_CLK_ID)
> 		c.dCUR = cpu_to_le32(p_srate);
> 	else if (entity_id == USB_OUT_CLK_ID)
> 		c.dCUR = cpu_to_le32(c_srate);
> 	...
> }

Can this can be u_audio_get_playback_srate(agdev) (and equivalent for
capture)?


John

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 03/11] usb: gadget: f_uac2: Support multiple sampling rates
  2022-01-05 12:44           ` John Keeping
@ 2022-01-05 14:05             ` Pavel Hofman
  2022-01-06  8:45               ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: Pavel Hofman @ 2022-01-05 14:05 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

Dne 05. 01. 22 v 13:44 John Keeping napsal(a):
> On Wed, Jan 05, 2022 at 01:20:01PM +0100, Pavel Hofman wrote:
>> Dne 04. 01. 22 v 16:33 John Keeping napsal(a):
>>> On Wed, Dec 22, 2021 at 11:01:16AM +0100, Pavel Hofman wrote:
>>>>
>>>> Dne 21. 12. 21 v 12:59 John Keeping napsal(a):
>>>>> On Mon, Dec 20, 2021 at 10:11:22PM +0100, Pavel Hofman wrote:
>>>>>> 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_uac2.
>>>>>>
>>>>>> Signed-off-by: Julian Scheel <julian@jusst.de>
>>>>>> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
>>>>>> ---
>>> [snip]
>>>>>>     };
>>>>>>     static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
>>>>>> @@ -166,7 +167,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 +179,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,
>>>>>>     };
>>>>>> @@ -635,12 +636,32 @@ struct cntrl_cur_lay3 {
>>>>>>     };
>>>>>>     struct cntrl_range_lay3 {
>>>>>> -	__le16	wNumSubRanges;
>>>>>>     	__le32	dMIN;
>>>>>>     	__le32	dMAX;
>>>>>>     	__le32	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;
>>>>>
>>>>> These structures are now inconsistent between cntrl_range_lay2 and
>>>>> cntrl_range_lay3.  Would it be better to make these flex arrays?  I
>>>>> guess that will make the code that uses it more complicated, but at the
>>>>> moment it looks like these are trying to be generic while in reality
>>>>> being quite specific to the one place that uses them at the moment.
>>>>
>>>> I am afraid I do not know exactly how to do that. Please can you post an
>>>> example? The rate control requires u32 (u16 is too small). Thanks a lot.
>>>
>>> After the change in this patch, we end up with:
>>>
>>> 	struct cntrl_range_lay2 {
>>> 		__le16	wNumSubRanges;
>>> 		__le16	wMIN;
>>> 		__le16	wMAX;
>>> 		__le16	wRES;
>>> 	} __packed;
>>>
>>> 	struct cntrl_range_lay3 {
>>> 		__le32	dMIN;
>>> 		__le32	dMAX;
>>> 		__le32	dRES;
>>> 	} __packed;
>>>
>>> so there are two structures with similar names but totally different
>>> structure, which I think risks confusion in the future.
>>>
>>> I wonder if DECLARE_UAC2_FEATURE_UNIT_DESCRIPTOR in linux/usb/audio-v2.h
>>> provides inspiration here, so potentially something like:
>>>
>>> 	#define DECLARE_UAC2_CNTRL_RANGE_LAY3(n)	\
>>> 		struct uac2_cntrl_range_lay3_##n {	\
>>> 			__le16 wNumSubRanges;		\
>>> 			struct cntrl_range_le32 r[n];	\
>>> 		} __packed;
>>>
>>> 	DECLARE_UAC2_CNTRL_RANGE_LAY3(UAC_MAX_RATES);
>>
>> Thanks, I will try to follow your suggestion in the next patchset version.
>>
>>>
>>>>>> +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 +688,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 +933,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;
>>>>>>     	}
>>>>>> @@ -1210,7 +1231,8 @@ 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;
>>>>>> +	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;
>>>>>> @@ -1222,7 +1244,8 @@ 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;
>>>>>> +	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;
>>>>>> @@ -1502,28 +1525,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 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_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",
>>>>>> @@ -1582,6 +1616,28 @@ 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);
>>>>>> +	struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
>>>>>> +	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->ctl_id == USB_IN_CLK_ID) {
>>>>>> +		opts->p_srate = val;
>>>>>
>>>>> Don't you need to hold opts->lock to change this?
>>>>> I'm not sure opts should be changed here though - that's the setup phase
>>>>> and this is "current state", so shouldn't it move to struct f_uac2?
>>>>
>>>> OK. I moved the current p_srate/c_srate from struct opts to f_uac2,
>>>> initialized with first value of opts->p_srates/c_srates[0] in afunc_bind.
>>>> The struct f_uac2 has no lock yet. Should I add the lock mutex to f_uac2 and
>>>> be locking f_uac2 access here in uac2_cs_control_sam_freq?
>>>
>>> Could we move this into struct uac_rtd_params and use the existing lock
>>> there to guard it?
>>>
>>> It would need accessor functions as that structure's local to u_audio.c,
>>> but there's already u_audio_set_playback_srate() so that isn't a big
>>> change.
>>
>> I have already moved p_/c_srate from uac_params to uac_rtd_params in
>> u_audio.c in the next version of the patchset. But IIUC the currently
>> selected playback/capture rate is required within f_uac2 too, in in_rq_cur()
>> in:
>>
>> if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
>> 	...
>> 	if (entity_id == USB_IN_CLK_ID)
>> 		c.dCUR = cpu_to_le32(p_srate);
>> 	else if (entity_id == USB_OUT_CLK_ID)
>> 		c.dCUR = cpu_to_le32(c_srate);
>> 	...
>> }
> 
> Can this can be u_audio_get_playback_srate(agdev) (and equivalent for
> capture)?
> 

We certainly can add the rate getter API call. But I do not know whether 
master should store and query slave for data required for proper 
operation of the master. IMO f_uac2 should be in control of the params 
requested by the real master - the USB host.

Currently the gadget-side app can open the gadget alsa device at the 
last-used rate (or channel count if/when multiple channels config is 
implemented) at any time, without knowing what params the user will 
actually request. IMO in some use cases the gadget alsa device should 
not allow opening before the host starts playback/capture, i.e. before 
the user actually requests what params to use. This would require more 
patches, nothing critical now and not in my pipeline. I (will) handle 
this issue with the side-channel gaudio_ctl application controller which 
receives the currently required params (srate as of now) via ctl 
notifications.

The question is whether it's correct for f_uac2 to ask u_audio about 
current rate at any time, even when u_audio is inactive and the only 
party doing something with the rate is actually f_uac2.

Maybe the ultimate data flow should be the other way round. But IIUC 
u_audio currently does not have any hooks back to f_uac1/2. The USB 
functions store all the params to uac_param for u_audio to use.

But I can add the u_audio_get_playback/capture_srate API method (in a 
separate patch, I guess) to avoid keeping the same values in two places, 
if found convenient.

I think we all should discuss our use cases in order to delineate the 
path along which the audio gadget should evolve.

Thanks,

Pavel.


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 03/11] usb: gadget: f_uac2: Support multiple sampling rates
  2022-01-05 14:05             ` Pavel Hofman
@ 2022-01-06  8:45               ` Pavel Hofman
  0 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2022-01-06  8:45 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

Dne 05. 01. 22 v 15:05 Pavel Hofman napsal(a):
> Dne 05. 01. 22 v 13:44 John Keeping napsal(a):
>> On Wed, Jan 05, 2022 at 01:20:01PM +0100, Pavel Hofman wrote:
>>> Dne 04. 01. 22 v 16:33 John Keeping napsal(a):
>>>> On Wed, Dec 22, 2021 at 11:01:16AM +0100, Pavel Hofman wrote:
>>>>> 
>>>>> Dne 21. 12. 21 v 12:59 John Keeping napsal(a):
>>>>>> On Mon, Dec 20, 2021 at 10:11:22PM +0100, Pavel Hofman
>>>>>> wrote:
>>>>>>> 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_uac2.
>>>>>>> 
>>>>>>> Signed-off-by: Julian Scheel <julian@jusst.de> 
>>>>>>> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com> 
>>>>>>> ---
>>>> [snip]
>>>>>>> }; static inline struct f_uac2 *func_to_uac2(struct
>>>>>>> usb_function *f) @@ -166,7 +167,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 +179,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, }; 
>>>>>>> @@ -635,12 +636,32 @@ struct cntrl_cur_lay3 { }; struct
>>>>>>> cntrl_range_lay3 { -    __le16    wNumSubRanges; __le32
>>>>>>> dMIN; __le32    dMAX; __le32    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;
>>>>>> 
>>>>>> These structures are now inconsistent between
>>>>>> cntrl_range_lay2 and cntrl_range_lay3.  Would it be better
>>>>>> to make these flex arrays?  I guess that will make the code
>>>>>> that uses it more complicated, but at the moment it looks
>>>>>> like these are trying to be generic while in reality being
>>>>>> quite specific to the one place that uses them at the
>>>>>> moment.
>>>>> 
>>>>> I am afraid I do not know exactly how to do that. Please can
>>>>> you post an example? The rate control requires u32 (u16 is
>>>>> too small). Thanks a lot.
>>>> 
>>>> After the change in this patch, we end up with:
>>>> 
>>>> struct cntrl_range_lay2 { __le16    wNumSubRanges; __le16
>>>> wMIN; __le16    wMAX; __le16    wRES; } __packed;
>>>> 
>>>> struct cntrl_range_lay3 { __le32    dMIN; __le32    dMAX; 
>>>> __le32    dRES; } __packed;
>>>> 
>>>> so there are two structures with similar names but totally
>>>> different structure, which I think risks confusion in the
>>>> future.
>>>> 
>>>> I wonder if DECLARE_UAC2_FEATURE_UNIT_DESCRIPTOR in 
>>>> linux/usb/audio-v2.h provides inspiration here, so potentially
>>>> something like:
>>>> 
>>>> #define DECLARE_UAC2_CNTRL_RANGE_LAY3(n)    \ struct
>>>> uac2_cntrl_range_lay3_##n {    \ __le16 wNumSubRanges;
>>>> \ struct cntrl_range_le32 r[n];    \ } __packed;
>>>> 
>>>> DECLARE_UAC2_CNTRL_RANGE_LAY3(UAC_MAX_RATES);
>>> 
>>> Thanks, I will try to follow your suggestion in the next patchset
>>>  version.
>>> 
>>>> 
>>>>>>> +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
>>>>>>> +688,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 +933,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; } @@ -1210,7
>>>>>>> +1231,8 @@ 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; +    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; @@ -1222,7 +1244,8
>>>>>>> @@ 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; +    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; @@ -1502,28
>>>>>>> +1525,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
>>>>>>> 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_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", @@ -1582,6 +1616,28 @@ 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); +    struct f_uac2_opts *opts =
>>>>>>> g_audio_to_uac2_opts(agdev); +    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->ctl_id == USB_IN_CLK_ID) { +
>>>>>>> opts->p_srate = val;
>>>>>> 
>>>>>> Don't you need to hold opts->lock to change this? I'm not
>>>>>> sure opts should be changed here though - that's the setup
>>>>>>  phase and this is "current state", so shouldn't it move to
>>>>>> struct f_uac2?
>>>>> 
>>>>> OK. I moved the current p_srate/c_srate from struct opts to
>>>>> f_uac2, initialized with first value of
>>>>> opts->p_srates/c_srates[0] in afunc_bind. The struct f_uac2
>>>>> has no lock yet. Should I add the lock mutex to f_uac2 and be
>>>>> locking f_uac2 access here in uac2_cs_control_sam_freq?
>>>> 
>>>> Could we move this into struct uac_rtd_params and use the
>>>> existing lock there to guard it?
>>>> 
>>>> It would need accessor functions as that structure's local to 
>>>> u_audio.c, but there's already u_audio_set_playback_srate() so
>>>> that isn't a big change.
>>> 
>>> I have already moved p_/c_srate from uac_params to uac_rtd_params
>>> in u_audio.c in the next version of the patchset. But IIUC the
>>> currently selected playback/capture rate is required within
>>> f_uac2 too, in in_rq_cur() in:
>>> 
>>> if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { ... if
>>> (entity_id == USB_IN_CLK_ID) c.dCUR = cpu_to_le32(p_srate); else
>>> if (entity_id == USB_OUT_CLK_ID) c.dCUR = cpu_to_le32(c_srate); 
>>> ... }
>> 
>> Can this can be u_audio_get_playback_srate(agdev) (and equivalent
>> for capture)?
>> 
> 
> We certainly can add the rate getter API call. But I do not know
> whether master should store and query slave for data required for
> proper operation of the master. IMO f_uac2 should be in control of
> the params requested by the real master - the USB host.
> 
> Currently the gadget-side app can open the gadget alsa device at the
>  last-used rate (or channel count if/when multiple channels config is
>  implemented) at any time, without knowing what params the user will
>  actually request. IMO in some use cases the gadget alsa device
> should not allow opening before the host starts playback/capture,
> i.e. before the user actually requests what params to use. This would
> require more patches, nothing critical now and not in my pipeline. I
> (will) handle this issue with the side-channel gaudio_ctl application
> controller which receives the currently required params (srate as of
> now) via ctl notifications.
> 
> The question is whether it's correct for f_uac2 to ask u_audio about
>  current rate at any time, even when u_audio is inactive and the only
>  party doing something with the rate is actually f_uac2.
> 
> Maybe the ultimate data flow should be the other way round. But IIUC
>  u_audio currently does not have any hooks back to f_uac1/2. The USB
>  functions store all the params to uac_param for u_audio to use.
> 
> But I can add the u_audio_get_playback/capture_srate API method (in a
>  separate patch, I guess) to avoid keeping the same values in two
> places, if found convenient.
> 
> I think we all should discuss our use cases in order to delineate the
>  path along which the audio gadget should evolve.
> 

Actually the host is not always the ultimate master. E.g. should the
gadget capture an SPDIF stream and pass it to the host, the u_audio part
should limit the available samplerates for f_uacX down to the currently
receiving samplerate. That suggests the
u_audio_get_playback/capture_srate API is probably in the right
direction. I do so and remove the c_srate/p_srate fields from f_uacX struct.


Thanks,

Pavel.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS
  2022-01-05 11:31             ` Pavel Hofman
@ 2022-01-06 14:32               ` John Keeping
  2022-01-07 10:30                 ` Pavel Hofman
  0 siblings, 1 reply; 40+ messages in thread
From: John Keeping @ 2022-01-06 14:32 UTC (permalink / raw)
  To: Pavel Hofman
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman

On Wed, Jan 05, 2022 at 12:31:01PM +0100, Pavel Hofman wrote:
> 
> Dne 04. 01. 22 v 16:33 John Keeping napsal(a):
> > On Thu, Dec 23, 2021 at 08:09:39AM +0100, Pavel Hofman wrote:
> > > 
> > > Dne 22. 12. 21 v 20:50 John Keeping napsal(a):
> > > > On Wed, 22 Dec 2021 14:35:07 +0100
> > > > Pavel Hofman <pavel.hofman@ivitera.com> wrote:
> > > > 
> > > > > Dne 21. 12. 21 v 13:29 John Keeping napsal(a):
> > > > > > On Mon, Dec 20, 2021 at 10:11:30PM +0100, Pavel Hofman wrote:
> > > > > > > So far bInterval for HS and SS was fixed at 4, disallowing faster
> > > > > > > samplerates. The patch determines the largest bInterval (4 to 1)
> > > > > > > for which the required bandwidth of the max samplerate fits the
> > > > > > > max allowed packet size. If the required bandwidth exceeds max
> > > > > > > bandwidth for single-packet mode (ep->mc=1), bInterval is left at
> > > > > > > 1.
> > > > > > 
> > > > > > I'm not sure if this is desirable - there are more concerns around
> > > > > > the interval than just whether the bandwidth is available.
> > > > > > 
> > > > > > The nice thing about having the HS/SS interval at 4 when the FS
> > > > > > value is 1 is that these both correspond to 1ms, which means the
> > > > > > calculations for minimum buffer & period sizes are the same for
> > > > > > FS/HS/SS.
> > > > > 
> > > > > Please do you see any specific place in u_audio.c where the interval of
> > > > > 1ms is assumed?
> > > > > 
> > > > > * Buffer/period size max limits are fixed
> > > > > * Bufer min size is calculated from the max_packet_size
> > > > > * snd_pcm_period_elapsed() is called when the current request fill
> > > > > overlaps the period boundary:
> > > > > 
> > > > > if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
> > > > > 		snd_pcm_period_elapsed(substream);
> > > > > 
> > > > > 
> > > > > The fixed HS bInterval=4 severely limits the available bandwidth,
> > > > > disallowing even the very basic 192kHz/2ch/24bits config.
> > > > 
> > > > Yes, but the problem is if the device enumerates as full-speed the
> > > > capability is no longer there.
> > > > 
> > > > I agree that is unlikely to be a problem in real use, but I think it
> > > > deserves consideration.
> > > 
> > > Please can you elaborate more on that? If the device enumerates as FS, it's
> > > automatically limited to bInterval=1 fullspeed frame. Not much more to do,
> > > IIUC.
> > 
> > Say we have 8 channels of 32-bit audio at 96kHz which requires 3072000
> > bytes per second, and IIUC we need bInterval == 2 for this to work at
> > HS.
> > 
> > But for FS there is no way to provide that bandwidth, so if the gadget
> > happens to be connected to a host that is only capable of FS then the
> > configuration just doesn't work.  I think what actually happens given
> > the current code is that each packet ends up truncated and parts of the
> > audio data are just dropped.
> 
> Yes. The current version will drop data for both FS (inevitably) and HS,
> eventhough there is no technical reason to drop data for HS as the bandwidth
> is available.
> 
> > 
> > > > For the last few years I've been using bInterval == 1 but I also have a
> > > > hack to disable full-speed operation completely.  In my case this is
> > > > because I want to minimise latency and with the 1ms interval for FS the
> > > > minimum ALSA period size is too large.
> > > > 
> > > > Basically, I agree with wanting a smaller bInterval, but I want it for a
> > > > different reason and I'd like to see a patch that addresses both our use
> > > > cases ;-)
> > > > 
> > > > > In f_uac2.c both HS/SS the max packet size, async EP OUT feedback value,
> > > > > as well as async EP IN momentary packet size calculations already take
> > > > > into account the bInterval of the respective endpoint.
> > > > > 
> > > > > I have been using bInterval < 4 in most of my tests for almost a year,
> > > > > testing packet sizes at up to 1024 bytes per 125us uframe, both
> > > > > directions, and the gadget has been bitperfect for samplerates up to
> > > > > 4MHz (including correctly working async feedback, tested on linux (up to
> > > > > 4MHz) and windows 10 WASAPI exclusive (up to 1.5MHz). For larger
> > > > > samplerates tests I increased the buffers like in the patch below but I
> > > > > did it just in case to minimize probability of xruns. It's not part of
> > > > > this patchset and should be configured dynamically too, if actually
> > > > > needed at all:
> > > > 
> > > > This is another case of a different trade-off - I use PREEMPT_RT to
> > > > minimise xruns and run with a period of 16 samples.
> > > > 
> > > > > > How do FS transfers work if the bandwidth requirements necessitate a
> > > > > >    smaller interval for HS/SS?  Doesn't that mean the FS transfers
> > > > > > must be too big?
> > > > > 
> > > > > Only UAC2 HS/SS bIntervals are dynamic with this patch, FS stays fixed
> > > > > at 1ms. For HS/SS  the max packet size is calculated together with the
> > > > > bInterval, so that the largest bInterval possible to fit the ISOC max
> > > > > packetsize limits is chosen.
> > > > 
> > > > I'd really like to see FS mode become unsupported when the packet size
> > > > is too big.  This is a slight issue right now (for 1023 vs 1024) but
> > > > this patch makes it significantly worse for the high bandwidth case.
> > > 
> > > I am afraid I do not understand what the patch makes worse. For FS it always
> > > yields bInterval=1 and the corresponding maxPacketSize, a calculation of
> > > which has not been changed by the patch.
> > 
> > See my comment above - before the difference was really 1023 vs 1024 so
> > it's possible to hit a problematic configuration but it's a smaller
> > window.
> 
> For me the problematic configuration is the one which does not work, which
> this feature actually tries to reduce (for HS).
> 
> > 
> > I really think we should avoid a configuration that mostly works but
> > fails in surprising ways (for example, working at HS but resulting in
> > corrupt data at FS because there just isn't sufficient bandwidth for the
> > sample rate, sample size and channel configuration selected).
> 
> 
> I understand your reasoning. See below.
> 
> > 
> > > > Right now I have this patch which is a hack but does at least result in
> > > > an error for the host when trying to enable audio at FS.  It would be
> > > > really nice to properly handle this in the composite gadget core so that
> > > > the audio function is exposed only at HS/SS with proper
> > > > DT_OTHER_SPEED_CONFIG handling, but currently that code assumes that the
> > > > same number of descriptors is provided for each speed.
> > > > 
> > > > -- 8< --
> > > > diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
> > > > index 36fa6ef0581b..b4946409b38a 100644
> > > > --- a/drivers/usb/gadget/function/f_uac2.c
> > > > +++ b/drivers/usb/gadget/function/f_uac2.c
> > > > @@ -1356,6 +1356,9 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
> > > >    		return 0;
> > > >    	}
> > > > +	if (gadget->speed < USB_SPEED_HIGH && alt)
> > > > +		return -EINVAL;
> > > > +
> > > >    	if (intf == uac2->as_out_intf) {
> > > >    		uac2->as_out_alt = alt;
> > > > -- >8 --
> > > > 
> > > > > > I don't think there has ever been a check that the configured sample
> > > > > >    size, channel count and interval actually fit in the max packet
> > > > > > size for an endpoint.  Is that something that should be checked to
> > > > > > give an error on bind if the configuration can't work?
> > > > > 
> > > > > The existing code has never had checks for any of that. Actually the
> > > > > dynamic bInterval calculation in this patch handles the bInterval and
> > > > > packetsize for configured parameters up to maximum ISOC bandwidth. Next
> > > > > version of this patch will at least warn about exceeding the overall
> > > > > available bandwidth.
> > > > > 
> > > > > There are many patches to go before the audio gadget becomes fool-proof,
> > > > > but at least it should be practically usable with these patches (when
> > > > > finalized) and the gaudio controller example implementation.
> > > > 
> > > > Agreed, and I really appreciate the improvements you're making here.
> > > > 
> > > > The reason I suggested the new checks here is that it makes a lot of
> > > > sense if the bInterval value is exposed as part of the configfs
> > > > interface.  It means there's one extra value to set for high bandwidth
> > > > operation, rather than having it "just work", but I think the
> > > > latency/bandwidth tradeoffs here mean that there's no way for the kernel
> > > > to select the right value for all scenarios, so really we need to let
> > > > the user tell us what they want.
> > > 
> > > OK. IMO it could be easily resolved by having the upper bInterval limit for
> > > the largest-fitting bInterval check of my patch configurable by new configfs
> > > max_bint, defaulting to the existing value of 4. I would leave the default
> > > (4), minimizing CPU load, you would set max_bint=1, minimizing latency. Any
> > > max_bint value in between would work, while still having available the
> > > automated calculation if lower bint value was required for the given
> > > parameters.
> > > 
> > > In addition, the final check dev_warn can be chanched to dev_err + returning
> > > EINVAL, providing the discussed sanity check. The check would work for FS as
> > > well as for HS/SS.
> > > 
> > > This change could be split to three patches:
> > > 
> > > 1. the automated calculation with fixed max_bint=4 - my current patch,
> > > dev_warn if max_size_bw > max_size_ep, max_size_bw limited to max_size_ep,
> > > no error, only warning.
> > > 
> > > 2. adding the uac2_opts max_bint, using in set_ep_max_packet_size_bint
> > > 
> > > 3. turning the sanity check warning to failing error: changing the dev_warn
> > > in the final check to dev_err+ returning error.
> > > 
> > > So the final version could look like this:
> > 
> > This sounds good to me.
> > 
> > But I think you'll hit the FS vs HS bandwidth issue described above when
> > trying anything that requires a lower bInterval ;-)
> 
> Well, the FS bandwidth is just too small, but IMO it's not a reason to limit
> HS too, as is limited now.
> 
> > 
> > I really think the answer to this is an extra patch/series that disables
> > operation at full speed when more bandwidth is required.  Ideally that
> > would include enhancing the gadget core to support different descriptors
> > for different speeds (which is already somewhat supported as other speed
> > config descriptors are returned correctly, but IIRC there's an
> > assumption that the number of descriptors is the same across all
> > speeds).
> 
> More patches in that area are certainly required.
> 
> What I can do:
> 
> * Separate the maxbandwidth patch series from the
> multiple-rates/rate-notification series to:
> 
> * patch 1 - the automated bint/maxPacketSize calculation with fixed
> max_bint=4 (for HS/SS), warning if bandwidth exceeded
> * patch 2 - adding the uac2_opts max_bint to allow lower latency (I already
> have this prepared)

I think I'd rather just provide the raw HS bInterval value here, any
userspace is already selecting all the parameters and given the
tradeoffs on (interrupt) load vs. latency I'd prefer to limit the magic
calculation here and give userspace full control.

> * patch 3 - failing f_uac2 load when requested params exceed HS/SS limits
> (for bInterval=1). The calculation method is always called for all FS/HS/SS
> now, so I cannot fail the FS check as it would break HS/SS.

But doesn't skipping the FS check break FS?

See below for discussion about the "proper" way to disable FS, but I
wonder if we could just patch afunc_set_alt() to return an error when
trying to switch to alt 1 at anything slower than HS.

That's a bit ugly, but I really doubt anyone is ever using FS these days
and the explicit failure is much nicer than just allowing data
corruption.

> I do not know how (and at what point) to disable operation for FS. Perhaps
> you could follow up with suitable patch, tested on your FS-capable HW?

You can use the max_speed configfs file to limit the gadget to full
speed for testing this (that's what I'm doing).  I think I've seen
you're using dwc2 and I know this works for dwc2, although you may need
to apply [1] from the list.

I've had a brief look and unfortunately it's not simple to properly
disable FS operation.  With one minor fix, passing NULL fs_descriptors
to usb_assign_descriptors() does basically work, but the generated
descriptors are now invalid because bNumInterfaces is incorrect.

Recalculating bNumInterfaces itself is easy, but if there are multiple
functions attached then the interfaces also need renumbering as they
must be contiguous starting from zero.  I don't see any way to handle
this renumbering without modifying every function driver, given that the
interface index may be used in fields like the interface association
descriptor's bFirstInterface.

[1] https://lore.kernel.org/linux-usb/20220106115731.1473909-1-john@metanate.com/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS
  2022-01-06 14:32               ` John Keeping
@ 2022-01-07 10:30                 ` Pavel Hofman
  0 siblings, 0 replies; 40+ messages in thread
From: Pavel Hofman @ 2022-01-07 10:30 UTC (permalink / raw)
  To: John Keeping
  Cc: linux-usb, Ruslan Bilovol, Felipe Balbi, Jerome Brunet,
	Julian Scheel, Greg Kroah-Hartman


Dne 06. 01. 22 v 15:32 John Keeping napsal(a):
> On Wed, Jan 05, 2022 at 12:31:01PM +0100, Pavel Hofman wrote:
>>
>> Dne 04. 01. 22 v 16:33 John Keeping napsal(a):
>>> On Thu, Dec 23, 2021 at 08:09:39AM +0100, Pavel Hofman wrote:
>>>>
>>>> Dne 22. 12. 21 v 20:50 John Keeping napsal(a):
>>>>> On Wed, 22 Dec 2021 14:35:07 +0100
>>>>> Pavel Hofman <pavel.hofman@ivitera.com> wrote:
>>>>>
>>>>>> Dne 21. 12. 21 v 13:29 John Keeping napsal(a):
>>>>>>> On Mon, Dec 20, 2021 at 10:11:30PM +0100, Pavel Hofman wrote:
>>>>>>>> So far bInterval for HS and SS was fixed at 4, disallowing faster
>>>>>>>> samplerates. The patch determines the largest bInterval (4 to 1)
>>>>>>>> for which the required bandwidth of the max samplerate fits the
>>>>>>>> max allowed packet size. If the required bandwidth exceeds max
>>>>>>>> bandwidth for single-packet mode (ep->mc=1), bInterval is left at
>>>>>>>> 1.
>>>>>>>
>>>>>>> I'm not sure if this is desirable - there are more concerns around
>>>>>>> the interval than just whether the bandwidth is available.
>>>>>>>
>>>>>>> The nice thing about having the HS/SS interval at 4 when the FS
>>>>>>> value is 1 is that these both correspond to 1ms, which means the
>>>>>>> calculations for minimum buffer & period sizes are the same for
>>>>>>> FS/HS/SS.
>>>>>>
>>>>>> Please do you see any specific place in u_audio.c where the interval of
>>>>>> 1ms is assumed?
>>>>>>
>>>>>> * Buffer/period size max limits are fixed
>>>>>> * Bufer min size is calculated from the max_packet_size
>>>>>> * snd_pcm_period_elapsed() is called when the current request fill
>>>>>> overlaps the period boundary:
>>>>>>
>>>>>> if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
>>>>>> 		snd_pcm_period_elapsed(substream);
>>>>>>
>>>>>>
>>>>>> The fixed HS bInterval=4 severely limits the available bandwidth,
>>>>>> disallowing even the very basic 192kHz/2ch/24bits config.
>>>>>
>>>>> Yes, but the problem is if the device enumerates as full-speed the
>>>>> capability is no longer there.
>>>>>
>>>>> I agree that is unlikely to be a problem in real use, but I think it
>>>>> deserves consideration.
>>>>
>>>> Please can you elaborate more on that? If the device enumerates as FS, it's
>>>> automatically limited to bInterval=1 fullspeed frame. Not much more to do,
>>>> IIUC.
>>>
>>> Say we have 8 channels of 32-bit audio at 96kHz which requires 3072000
>>> bytes per second, and IIUC we need bInterval == 2 for this to work at
>>> HS.
>>>
>>> But for FS there is no way to provide that bandwidth, so if the gadget
>>> happens to be connected to a host that is only capable of FS then the
>>> configuration just doesn't work.  I think what actually happens given
>>> the current code is that each packet ends up truncated and parts of the
>>> audio data are just dropped.
>>
>> Yes. The current version will drop data for both FS (inevitably) and HS,
>> eventhough there is no technical reason to drop data for HS as the bandwidth
>> is available.
>>
>>>
>>>>> For the last few years I've been using bInterval == 1 but I also have a
>>>>> hack to disable full-speed operation completely.  In my case this is
>>>>> because I want to minimise latency and with the 1ms interval for FS the
>>>>> minimum ALSA period size is too large.
>>>>>
>>>>> Basically, I agree with wanting a smaller bInterval, but I want it for a
>>>>> different reason and I'd like to see a patch that addresses both our use
>>>>> cases ;-)
>>>>>
>>>>>> In f_uac2.c both HS/SS the max packet size, async EP OUT feedback value,
>>>>>> as well as async EP IN momentary packet size calculations already take
>>>>>> into account the bInterval of the respective endpoint.
>>>>>>
>>>>>> I have been using bInterval < 4 in most of my tests for almost a year,
>>>>>> testing packet sizes at up to 1024 bytes per 125us uframe, both
>>>>>> directions, and the gadget has been bitperfect for samplerates up to
>>>>>> 4MHz (including correctly working async feedback, tested on linux (up to
>>>>>> 4MHz) and windows 10 WASAPI exclusive (up to 1.5MHz). For larger
>>>>>> samplerates tests I increased the buffers like in the patch below but I
>>>>>> did it just in case to minimize probability of xruns. It's not part of
>>>>>> this patchset and should be configured dynamically too, if actually
>>>>>> needed at all:
>>>>>
>>>>> This is another case of a different trade-off - I use PREEMPT_RT to
>>>>> minimise xruns and run with a period of 16 samples.
>>>>>
>>>>>>> How do FS transfers work if the bandwidth requirements necessitate a
>>>>>>>     smaller interval for HS/SS?  Doesn't that mean the FS transfers
>>>>>>> must be too big?
>>>>>>
>>>>>> Only UAC2 HS/SS bIntervals are dynamic with this patch, FS stays fixed
>>>>>> at 1ms. For HS/SS  the max packet size is calculated together with the
>>>>>> bInterval, so that the largest bInterval possible to fit the ISOC max
>>>>>> packetsize limits is chosen.
>>>>>
>>>>> I'd really like to see FS mode become unsupported when the packet size
>>>>> is too big.  This is a slight issue right now (for 1023 vs 1024) but
>>>>> this patch makes it significantly worse for the high bandwidth case.
>>>>
>>>> I am afraid I do not understand what the patch makes worse. For FS it always
>>>> yields bInterval=1 and the corresponding maxPacketSize, a calculation of
>>>> which has not been changed by the patch.
>>>
>>> See my comment above - before the difference was really 1023 vs 1024 so
>>> it's possible to hit a problematic configuration but it's a smaller
>>> window.
>>
>> For me the problematic configuration is the one which does not work, which
>> this feature actually tries to reduce (for HS).
>>
>>>
>>> I really think we should avoid a configuration that mostly works but
>>> fails in surprising ways (for example, working at HS but resulting in
>>> corrupt data at FS because there just isn't sufficient bandwidth for the
>>> sample rate, sample size and channel configuration selected).
>>
>>
>> I understand your reasoning. See below.
>>
>>>
>>>>> Right now I have this patch which is a hack but does at least result in
>>>>> an error for the host when trying to enable audio at FS.  It would be
>>>>> really nice to properly handle this in the composite gadget core so that
>>>>> the audio function is exposed only at HS/SS with proper
>>>>> DT_OTHER_SPEED_CONFIG handling, but currently that code assumes that the
>>>>> same number of descriptors is provided for each speed.
>>>>>
>>>>> -- 8< --
>>>>> diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
>>>>> index 36fa6ef0581b..b4946409b38a 100644
>>>>> --- a/drivers/usb/gadget/function/f_uac2.c
>>>>> +++ b/drivers/usb/gadget/function/f_uac2.c
>>>>> @@ -1356,6 +1356,9 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
>>>>>     		return 0;
>>>>>     	}
>>>>> +	if (gadget->speed < USB_SPEED_HIGH && alt)
>>>>> +		return -EINVAL;
>>>>> +
>>>>>     	if (intf == uac2->as_out_intf) {
>>>>>     		uac2->as_out_alt = alt;
>>>>> -- >8 --
>>>>>
>>>>>>> I don't think there has ever been a check that the configured sample
>>>>>>>     size, channel count and interval actually fit in the max packet
>>>>>>> size for an endpoint.  Is that something that should be checked to
>>>>>>> give an error on bind if the configuration can't work?
>>>>>>
>>>>>> The existing code has never had checks for any of that. Actually the
>>>>>> dynamic bInterval calculation in this patch handles the bInterval and
>>>>>> packetsize for configured parameters up to maximum ISOC bandwidth. Next
>>>>>> version of this patch will at least warn about exceeding the overall
>>>>>> available bandwidth.
>>>>>>
>>>>>> There are many patches to go before the audio gadget becomes fool-proof,
>>>>>> but at least it should be practically usable with these patches (when
>>>>>> finalized) and the gaudio controller example implementation.
>>>>>
>>>>> Agreed, and I really appreciate the improvements you're making here.
>>>>>
>>>>> The reason I suggested the new checks here is that it makes a lot of
>>>>> sense if the bInterval value is exposed as part of the configfs
>>>>> interface.  It means there's one extra value to set for high bandwidth
>>>>> operation, rather than having it "just work", but I think the
>>>>> latency/bandwidth tradeoffs here mean that there's no way for the kernel
>>>>> to select the right value for all scenarios, so really we need to let
>>>>> the user tell us what they want.
>>>>
>>>> OK. IMO it could be easily resolved by having the upper bInterval limit for
>>>> the largest-fitting bInterval check of my patch configurable by new configfs
>>>> max_bint, defaulting to the existing value of 4. I would leave the default
>>>> (4), minimizing CPU load, you would set max_bint=1, minimizing latency. Any
>>>> max_bint value in between would work, while still having available the
>>>> automated calculation if lower bint value was required for the given
>>>> parameters.
>>>>
>>>> In addition, the final check dev_warn can be chanched to dev_err + returning
>>>> EINVAL, providing the discussed sanity check. The check would work for FS as
>>>> well as for HS/SS.
>>>>
>>>> This change could be split to three patches:
>>>>
>>>> 1. the automated calculation with fixed max_bint=4 - my current patch,
>>>> dev_warn if max_size_bw > max_size_ep, max_size_bw limited to max_size_ep,
>>>> no error, only warning.
>>>>
>>>> 2. adding the uac2_opts max_bint, using in set_ep_max_packet_size_bint
>>>>
>>>> 3. turning the sanity check warning to failing error: changing the dev_warn
>>>> in the final check to dev_err+ returning error.
>>>>
>>>> So the final version could look like this:
>>>
>>> This sounds good to me.
>>>
>>> But I think you'll hit the FS vs HS bandwidth issue described above when
>>> trying anything that requires a lower bInterval ;-)
>>
>> Well, the FS bandwidth is just too small, but IMO it's not a reason to limit
>> HS too, as is limited now.
>>
>>>
>>> I really think the answer to this is an extra patch/series that disables
>>> operation at full speed when more bandwidth is required.  Ideally that
>>> would include enhancing the gadget core to support different descriptors
>>> for different speeds (which is already somewhat supported as other speed
>>> config descriptors are returned correctly, but IIRC there's an
>>> assumption that the number of descriptors is the same across all
>>> speeds).
>>
>> More patches in that area are certainly required.
>>
>> What I can do:
>>
>> * Separate the maxbandwidth patch series from the
>> multiple-rates/rate-notification series to:
>>
>> * patch 1 - the automated bint/maxPacketSize calculation with fixed
>> max_bint=4 (for HS/SS), warning if bandwidth exceeded
>> * patch 2 - adding the uac2_opts max_bint to allow lower latency (I already
>> have this prepared)
> 
> I think I'd rather just provide the raw HS bInterval value here, any
> userspace is already selecting all the parameters and given the
> tradeoffs on (interrupt) load vs. latency I'd prefer to limit the magic
> calculation here and give userspace full control.

OK, how about a compromise:

hs_bint 4 .. 1 - raw HS/SS bInterval, no adjustment, full control to the 
user
hs_bint 0 - HS/SS bInterval optimizer starting at 4 for minimum CPU load 
but maximum flexibility, used as default

> 
>> * patch 3 - failing f_uac2 load when requested params exceed HS/SS limits
>> (for bInterval=1). The calculation method is always called for all FS/HS/SS
>> now, so I cannot fail the FS check as it would break HS/SS.
> 
> But doesn't skipping the FS check break FS?

The current check in my patch is always called for FS/HS/SS by the 
gadget. But IMO a better solution would be a common point of failure for 
all of FS/HS/SS at some suitable place, not in that method. The method 
would just warn. The failure check could call that method (likely a bit 
modified) internally. The failure patch would not be included in this 
patch series.

> 
> See below for discussion about the "proper" way to disable FS, but I
> wonder if we could just patch afunc_set_alt() to return an error when
> trying to switch to alt 1 at anything slower than HS.
> 
> That's a bit ugly, but I really doubt anyone is ever using FS these days
> and the explicit failure is much nicer than just allowing data
> corruption.
> 
>> I do not know how (and at what point) to disable operation for FS. Perhaps
>> you could follow up with suitable patch, tested on your FS-capable HW?
> 
> You can use the max_speed configfs file to limit the gadget to full
> speed for testing this (that's what I'm doing).  I think I've seen
> you're using dwc2 and I know this works for dwc2, although you may need
> to apply [1] from the list.

Thanks for the hint and for the dwc2 patch.

> 
> I've had a brief look and unfortunately it's not simple to properly
> disable FS operation.  With one minor fix, passing NULL fs_descriptors
> to usb_assign_descriptors() does basically work, but the generated
> descriptors are now invalid because bNumInterfaces is incorrect.
> 
> Recalculating bNumInterfaces itself is easy, but if there are multiple
> functions attached then the interfaces also need renumbering as they
> must be contiguous starting from zero.  I don't see any way to handle
> this renumbering without modifying every function driver, given that the
> interface index may be used in fields like the interface association
> descriptor's bFirstInterface.

Thanks for looking at the issue.

Regards,

Pavel.

^ permalink raw reply	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2022-01-07 10:30 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-20 21:11 [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
2021-12-20 21:11 ` [PATCH v2 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls Pavel Hofman
2021-12-20 21:11 ` [PATCH v2 02/11] usb: gadget: u_audio: Support multiple sampling rates Pavel Hofman
2021-12-21 11:35   ` John Keeping
2021-12-22  7:13     ` Pavel Hofman
2022-01-04 15:32       ` John Keeping
2022-01-05 10:55         ` Pavel Hofman
2021-12-20 21:11 ` [PATCH v2 03/11] usb: gadget: f_uac2: " Pavel Hofman
2021-12-21 11:59   ` John Keeping
2021-12-22 10:01     ` Pavel Hofman
2022-01-04 15:33       ` John Keeping
2022-01-05 12:20         ` Pavel Hofman
2022-01-05 12:44           ` John Keeping
2022-01-05 14:05             ` Pavel Hofman
2022-01-06  8:45               ` Pavel Hofman
2021-12-20 21:11 ` [PATCH v2 04/11] usb: gadget: f_uac1: " Pavel Hofman
2021-12-20 21:11 ` [PATCH v2 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names Pavel Hofman
2021-12-21 12:02   ` John Keeping
2021-12-22 10:11     ` Pavel Hofman
2021-12-20 21:11 ` [PATCH v2 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped) Pavel Hofman
2021-12-21 12:07   ` John Keeping
2021-12-22 10:41     ` Pavel Hofman
2021-12-20 21:11 ` [PATCH v2 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop Pavel Hofman
2021-12-21 12:18   ` John Keeping
2021-12-22 12:26     ` Pavel Hofman
2021-12-28  9:04       ` [RFC: PATCH " Pavel Hofman
2021-12-20 21:11 ` [PATCH v2 08/11] usb: gadget: u_audio: Adding suspend call Pavel Hofman
2021-12-20 21:11 ` [PATCH v2 09/11] usb: gadget: f_uac2: Adding suspend callback Pavel Hofman
2021-12-20 21:11 ` [PATCH v2 10/11] usb: gadget: f_uac1: " Pavel Hofman
2021-12-20 21:11 ` [PATCH v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS Pavel Hofman
2021-12-21 12:29   ` John Keeping
2021-12-22 13:35     ` Pavel Hofman
2021-12-22 19:50       ` John Keeping
2021-12-23  7:09         ` Pavel Hofman
2022-01-04 15:33           ` John Keeping
2022-01-05 11:31             ` Pavel Hofman
2022-01-06 14:32               ` John Keeping
2022-01-07 10:30                 ` Pavel Hofman
2021-12-21  7:59 ` [PATCH v2 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Greg Kroah-Hartman
2021-12-22 13:38   ` Pavel Hofman

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.