All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval
@ 2021-12-20  8:25 Pavel Hofman
  2021-12-20  8:25 ` [PATCH 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls Pavel Hofman
                   ` (10 more replies)
  0 siblings, 11 replies; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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.


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] 14+ messages in thread

* [PATCH 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls
  2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
@ 2021-12-20  8:25 ` Pavel Hofman
  2021-12-20  8:25 ` [PATCH 02/11] usb: gadget: u_audio: Support multiple sampling rates Pavel Hofman
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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] 14+ messages in thread

* [PATCH 02/11] usb: gadget: u_audio: Support multiple sampling rates
  2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
  2021-12-20  8:25 ` [PATCH 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls Pavel Hofman
@ 2021-12-20  8:25 ` Pavel Hofman
  2021-12-20  8:25 ` [PATCH 03/11] usb: gadget: f_uac2: " Pavel Hofman
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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] 14+ messages in thread

* [PATCH 03/11] usb: gadget: f_uac2: Support multiple sampling rates
  2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
  2021-12-20  8:25 ` [PATCH 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls Pavel Hofman
  2021-12-20  8:25 ` [PATCH 02/11] usb: gadget: u_audio: Support multiple sampling rates Pavel Hofman
@ 2021-12-20  8:25 ` Pavel Hofman
  2021-12-20  8:25 ` [PATCH 04/11] usb: gadget: f_uac1: " Pavel Hofman
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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] 14+ messages in thread

* [PATCH 04/11] usb: gadget: f_uac1: Support multiple sampling rates
  2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (2 preceding siblings ...)
  2021-12-20  8:25 ` [PATCH 03/11] usb: gadget: f_uac2: " Pavel Hofman
@ 2021-12-20  8:25 ` Pavel Hofman
  2021-12-20 20:26     ` kernel test robot
  2021-12-20  8:25 ` [PATCH 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names Pavel Hofman
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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             |  12 +-
 5 files changed, 168 insertions(+), 29 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..f3cae72bcd4c 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -283,10 +283,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] 14+ messages in thread

* [PATCH 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names
  2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (3 preceding siblings ...)
  2021-12-20  8:25 ` [PATCH 04/11] usb: gadget: f_uac1: " Pavel Hofman
@ 2021-12-20  8:25 ` Pavel Hofman
  2021-12-20  8:25 ` [PATCH 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped) Pavel Hofman
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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] 14+ messages in thread

* [PATCH 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped)
  2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (4 preceding siblings ...)
  2021-12-20  8:25 ` [PATCH 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names Pavel Hofman
@ 2021-12-20  8:25 ` Pavel Hofman
  2021-12-20  8:25 ` [PATCH 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop Pavel Hofman
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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] 14+ messages in thread

* [PATCH 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop
  2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (5 preceding siblings ...)
  2021-12-20  8:25 ` [PATCH 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped) Pavel Hofman
@ 2021-12-20  8:25 ` Pavel Hofman
  2021-12-20  8:25 ` [PATCH 08/11] usb: gadget: u_audio: Adding suspend call Pavel Hofman
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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] 14+ messages in thread

* [PATCH 08/11] usb: gadget: u_audio: Adding suspend call
  2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (6 preceding siblings ...)
  2021-12-20  8:25 ` [PATCH 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop Pavel Hofman
@ 2021-12-20  8:25 ` Pavel Hofman
  2021-12-20  8:25 ` [PATCH 09/11] usb: gadget: f_uac2: Adding suspend callback Pavel Hofman
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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] 14+ messages in thread

* [PATCH 09/11] usb: gadget: f_uac2: Adding suspend callback
  2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (7 preceding siblings ...)
  2021-12-20  8:25 ` [PATCH 08/11] usb: gadget: u_audio: Adding suspend call Pavel Hofman
@ 2021-12-20  8:25 ` Pavel Hofman
  2021-12-20  8:25 ` [PATCH 10/11] usb: gadget: f_uac1: " Pavel Hofman
  2021-12-20  8:25 ` [PATCH 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS Pavel Hofman
  10 siblings, 0 replies; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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] 14+ messages in thread

* [PATCH 10/11] usb: gadget: f_uac1: Adding suspend callback
  2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (8 preceding siblings ...)
  2021-12-20  8:25 ` [PATCH 09/11] usb: gadget: f_uac2: Adding suspend callback Pavel Hofman
@ 2021-12-20  8:25 ` Pavel Hofman
  2021-12-20  8:25 ` [PATCH 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS Pavel Hofman
  10 siblings, 0 replies; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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] 14+ messages in thread

* [PATCH 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS
  2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
                   ` (9 preceding siblings ...)
  2021-12-20  8:25 ` [PATCH 10/11] usb: gadget: f_uac1: " Pavel Hofman
@ 2021-12-20  8:25 ` Pavel Hofman
  10 siblings, 0 replies; 14+ messages in thread
From: Pavel Hofman @ 2021-12-20  8:25 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] 14+ messages in thread

* Re: [PATCH 04/11] usb: gadget: f_uac1: Support multiple sampling rates
  2021-12-20  8:25 ` [PATCH 04/11] usb: gadget: f_uac1: " Pavel Hofman
@ 2021-12-20 20:26     ` kernel test robot
  0 siblings, 0 replies; 14+ messages in thread
From: kernel test robot @ 2021-12-20 20:26 UTC (permalink / raw)
  To: Pavel Hofman, linux-usb
  Cc: kbuild-all, Pavel Hofman, Ruslan Bilovol, Felipe Balbi,
	Jerome Brunet, Julian Scheel, Greg Kroah-Hartman

Hi Pavel,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on usb/usb-testing]
[also build test ERROR on linus/master v5.16-rc6 next-20211220]
[cannot apply to balbi-usb/testing/next peter-chen-usb/for-usb-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Pavel-Hofman/usb-gadget-audio-Multiple-rates-dyn-bInterval/20211220-162736
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: riscv-randconfig-r003-20211220 (https://download.01.org/0day-ci/archive/20211221/202112210422.0fC1uDBA-lkp@intel.com/config)
compiler: riscv32-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/2b868f914e557058c0b5e749db604db56b77e353
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Pavel-Hofman/usb-gadget-audio-Multiple-rates-dyn-bInterval/20211220-162736
        git checkout 2b868f914e557058c0b5e749db604db56b77e353
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=riscv SHELL=/bin/bash drivers/usb/gadget/legacy/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/usb/gadget/legacy/audio.c: In function 'audio_bind':
>> drivers/usb/gadget/legacy/audio.c:287:25: error: 'p_srates_cnt' undeclared (first use in this function)
     287 |         for (i = 0; i < p_srates_cnt; ++i)
         |                         ^~~~~~~~~~~~
   drivers/usb/gadget/legacy/audio.c:287:25: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/usb/gadget/legacy/audio.c:288:42: error: 'p_srates' undeclared (first use in this function); did you mean 'p_srate'?
     288 |                 uac1_opts->p_srates[i] = p_srates[i];
         |                                          ^~~~~~~~
         |                                          p_srate
>> drivers/usb/gadget/legacy/audio.c:294:25: error: 'c_srates_cnt' undeclared (first use in this function)
     294 |         for (i = 0; i < c_srates_cnt; ++i)
         |                         ^~~~~~~~~~~~
>> drivers/usb/gadget/legacy/audio.c:295:42: error: 'c_srates' undeclared (first use in this function); did you mean 'c_srate'?
     295 |                 uac1_opts->c_srates[i] = c_srates[i];
         |                                          ^~~~~~~~
         |                                          c_srate


vim +/p_srates_cnt +287 drivers/usb/gadget/legacy/audio.c

   264	
   265	#ifndef CONFIG_GADGET_UAC1
   266		uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
   267		uac2_opts->p_chmask = p_chmask;
   268	
   269		for (i = 0; i < p_srates_cnt; ++i)
   270			uac2_opts->p_srates[i] = p_srates[i];
   271		uac2_opts->p_srate = p_srates[0];
   272	
   273		uac2_opts->p_ssize = p_ssize;
   274		uac2_opts->c_chmask = c_chmask;
   275	
   276		for (i = 0; i < c_srates_cnt; ++i)
   277			uac2_opts->c_srates[i] = c_srates[i];
   278		uac2_opts->c_srate = c_srates[0];
   279	
   280		uac2_opts->c_ssize = c_ssize;
   281		uac2_opts->req_number = UAC2_DEF_REQ_NUM;
   282	#else
   283	#ifndef CONFIG_GADGET_UAC1_LEGACY
   284		uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
   285		uac1_opts->p_chmask = p_chmask;
   286	
 > 287		for (i = 0; i < p_srates_cnt; ++i)
 > 288			uac1_opts->p_srates[i] = p_srates[i];
   289		uac1_opts->p_srate = p_srates[0];
   290	
   291		uac1_opts->p_ssize = p_ssize;
   292		uac1_opts->c_chmask = c_chmask;
   293	
 > 294		for (i = 0; i < c_srates_cnt; ++i)
 > 295			uac1_opts->c_srates[i] = c_srates[i];
   296		uac1_opts->c_srate = c_srates[0];
   297	
   298		uac1_opts->c_ssize = c_ssize;
   299		uac1_opts->req_number = UAC1_DEF_REQ_NUM;
   300	#else /* CONFIG_GADGET_UAC1_LEGACY */
   301		uac1_opts = container_of(fi_uac1, struct f_uac1_legacy_opts, func_inst);
   302		uac1_opts->fn_play = fn_play;
   303		uac1_opts->fn_cap = fn_cap;
   304		uac1_opts->fn_cntl = fn_cntl;
   305		uac1_opts->req_buf_size = req_buf_size;
   306		uac1_opts->req_count = req_count;
   307		uac1_opts->audio_buf_size = audio_buf_size;
   308	#endif /* CONFIG_GADGET_UAC1_LEGACY */
   309	#endif
   310	
   311		status = usb_string_ids_tab(cdev, strings_dev);
   312		if (status < 0)
   313			goto fail;
   314		device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
   315		device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
   316	
   317		if (gadget_is_otg(cdev->gadget) && !otg_desc[0]) {
   318			struct usb_descriptor_header *usb_desc;
   319	
   320			usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
   321			if (!usb_desc) {
   322				status = -ENOMEM;
   323				goto fail;
   324			}
   325			usb_otg_descriptor_init(cdev->gadget, usb_desc);
   326			otg_desc[0] = usb_desc;
   327			otg_desc[1] = NULL;
   328		}
   329	
   330		status = usb_add_config(cdev, &audio_config_driver, audio_do_config);
   331		if (status < 0)
   332			goto fail_otg_desc;
   333		usb_composite_overwrite_options(cdev, &coverwrite);
   334	
   335		INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
   336		return 0;
   337	
   338	fail_otg_desc:
   339		kfree(otg_desc[0]);
   340		otg_desc[0] = NULL;
   341	fail:
   342	#ifndef CONFIG_GADGET_UAC1
   343		usb_put_function_instance(fi_uac2);
   344	#else
   345		usb_put_function_instance(fi_uac1);
   346	#endif
   347		return status;
   348	}
   349	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

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

* Re: [PATCH 04/11] usb: gadget: f_uac1: Support multiple sampling rates
@ 2021-12-20 20:26     ` kernel test robot
  0 siblings, 0 replies; 14+ messages in thread
From: kernel test robot @ 2021-12-20 20:26 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 6170 bytes --]

Hi Pavel,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on usb/usb-testing]
[also build test ERROR on linus/master v5.16-rc6 next-20211220]
[cannot apply to balbi-usb/testing/next peter-chen-usb/for-usb-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Pavel-Hofman/usb-gadget-audio-Multiple-rates-dyn-bInterval/20211220-162736
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: riscv-randconfig-r003-20211220 (https://download.01.org/0day-ci/archive/20211221/202112210422.0fC1uDBA-lkp(a)intel.com/config)
compiler: riscv32-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/2b868f914e557058c0b5e749db604db56b77e353
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Pavel-Hofman/usb-gadget-audio-Multiple-rates-dyn-bInterval/20211220-162736
        git checkout 2b868f914e557058c0b5e749db604db56b77e353
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=riscv SHELL=/bin/bash drivers/usb/gadget/legacy/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/usb/gadget/legacy/audio.c: In function 'audio_bind':
>> drivers/usb/gadget/legacy/audio.c:287:25: error: 'p_srates_cnt' undeclared (first use in this function)
     287 |         for (i = 0; i < p_srates_cnt; ++i)
         |                         ^~~~~~~~~~~~
   drivers/usb/gadget/legacy/audio.c:287:25: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/usb/gadget/legacy/audio.c:288:42: error: 'p_srates' undeclared (first use in this function); did you mean 'p_srate'?
     288 |                 uac1_opts->p_srates[i] = p_srates[i];
         |                                          ^~~~~~~~
         |                                          p_srate
>> drivers/usb/gadget/legacy/audio.c:294:25: error: 'c_srates_cnt' undeclared (first use in this function)
     294 |         for (i = 0; i < c_srates_cnt; ++i)
         |                         ^~~~~~~~~~~~
>> drivers/usb/gadget/legacy/audio.c:295:42: error: 'c_srates' undeclared (first use in this function); did you mean 'c_srate'?
     295 |                 uac1_opts->c_srates[i] = c_srates[i];
         |                                          ^~~~~~~~
         |                                          c_srate


vim +/p_srates_cnt +287 drivers/usb/gadget/legacy/audio.c

   264	
   265	#ifndef CONFIG_GADGET_UAC1
   266		uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
   267		uac2_opts->p_chmask = p_chmask;
   268	
   269		for (i = 0; i < p_srates_cnt; ++i)
   270			uac2_opts->p_srates[i] = p_srates[i];
   271		uac2_opts->p_srate = p_srates[0];
   272	
   273		uac2_opts->p_ssize = p_ssize;
   274		uac2_opts->c_chmask = c_chmask;
   275	
   276		for (i = 0; i < c_srates_cnt; ++i)
   277			uac2_opts->c_srates[i] = c_srates[i];
   278		uac2_opts->c_srate = c_srates[0];
   279	
   280		uac2_opts->c_ssize = c_ssize;
   281		uac2_opts->req_number = UAC2_DEF_REQ_NUM;
   282	#else
   283	#ifndef CONFIG_GADGET_UAC1_LEGACY
   284		uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
   285		uac1_opts->p_chmask = p_chmask;
   286	
 > 287		for (i = 0; i < p_srates_cnt; ++i)
 > 288			uac1_opts->p_srates[i] = p_srates[i];
   289		uac1_opts->p_srate = p_srates[0];
   290	
   291		uac1_opts->p_ssize = p_ssize;
   292		uac1_opts->c_chmask = c_chmask;
   293	
 > 294		for (i = 0; i < c_srates_cnt; ++i)
 > 295			uac1_opts->c_srates[i] = c_srates[i];
   296		uac1_opts->c_srate = c_srates[0];
   297	
   298		uac1_opts->c_ssize = c_ssize;
   299		uac1_opts->req_number = UAC1_DEF_REQ_NUM;
   300	#else /* CONFIG_GADGET_UAC1_LEGACY */
   301		uac1_opts = container_of(fi_uac1, struct f_uac1_legacy_opts, func_inst);
   302		uac1_opts->fn_play = fn_play;
   303		uac1_opts->fn_cap = fn_cap;
   304		uac1_opts->fn_cntl = fn_cntl;
   305		uac1_opts->req_buf_size = req_buf_size;
   306		uac1_opts->req_count = req_count;
   307		uac1_opts->audio_buf_size = audio_buf_size;
   308	#endif /* CONFIG_GADGET_UAC1_LEGACY */
   309	#endif
   310	
   311		status = usb_string_ids_tab(cdev, strings_dev);
   312		if (status < 0)
   313			goto fail;
   314		device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
   315		device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
   316	
   317		if (gadget_is_otg(cdev->gadget) && !otg_desc[0]) {
   318			struct usb_descriptor_header *usb_desc;
   319	
   320			usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
   321			if (!usb_desc) {
   322				status = -ENOMEM;
   323				goto fail;
   324			}
   325			usb_otg_descriptor_init(cdev->gadget, usb_desc);
   326			otg_desc[0] = usb_desc;
   327			otg_desc[1] = NULL;
   328		}
   329	
   330		status = usb_add_config(cdev, &audio_config_driver, audio_do_config);
   331		if (status < 0)
   332			goto fail_otg_desc;
   333		usb_composite_overwrite_options(cdev, &coverwrite);
   334	
   335		INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
   336		return 0;
   337	
   338	fail_otg_desc:
   339		kfree(otg_desc[0]);
   340		otg_desc[0] = NULL;
   341	fail:
   342	#ifndef CONFIG_GADGET_UAC1
   343		usb_put_function_instance(fi_uac2);
   344	#else
   345		usb_put_function_instance(fi_uac1);
   346	#endif
   347		return status;
   348	}
   349	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

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

end of thread, other threads:[~2021-12-20 20:26 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-20  8:25 [PATCH 00/11] usb: gadget: audio: Multiple rates, dyn. bInterval Pavel Hofman
2021-12-20  8:25 ` [PATCH 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls Pavel Hofman
2021-12-20  8:25 ` [PATCH 02/11] usb: gadget: u_audio: Support multiple sampling rates Pavel Hofman
2021-12-20  8:25 ` [PATCH 03/11] usb: gadget: f_uac2: " Pavel Hofman
2021-12-20  8:25 ` [PATCH 04/11] usb: gadget: f_uac1: " Pavel Hofman
2021-12-20 20:26   ` kernel test robot
2021-12-20 20:26     ` kernel test robot
2021-12-20  8:25 ` [PATCH 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names Pavel Hofman
2021-12-20  8:25 ` [PATCH 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped) Pavel Hofman
2021-12-20  8:25 ` [PATCH 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop Pavel Hofman
2021-12-20  8:25 ` [PATCH 08/11] usb: gadget: u_audio: Adding suspend call Pavel Hofman
2021-12-20  8:25 ` [PATCH 09/11] usb: gadget: f_uac2: Adding suspend callback Pavel Hofman
2021-12-20  8:25 ` [PATCH 10/11] usb: gadget: f_uac1: " Pavel Hofman
2021-12-20  8:25 ` [PATCH 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS 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.