Commit 695d39ff authored by Julian Scheel's avatar Julian Scheel Committed by Greg Kroah-Hartman
Browse files

usb: gadget: f_uac1: Support multiple sampling rates



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: default avatarJulian Scheel <julian@jusst.de>
Signed-off-by: default avatarPavel Hofman <pavel.hofman@ivitera.com>
Link: https://lore.kernel.org/r/20220121155308.48794-7-pavel.hofman@ivitera.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a7339e4f
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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
+2 −2
Original line number Diff line number Diff line
@@ -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
+155 −26
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
 * f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
 *
 * Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
 * Copyright (C) 2021 Julian Scheel <julian@jusst.de>
 *
 * This driver doesn't expect any real Audio codec to be present
 * on the device - the audio streams are simply sinked to and
@@ -42,6 +43,9 @@ struct f_uac1 {
	/* Interrupt IN endpoint of AC interface */
	struct usb_ep	*int_ep;
	atomic_t	int_count;
	int ctl_id;		/* EP id */
	int c_srate;	/* current capture srate */
	int p_srate;	/* current playback prate */
};

static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
@@ -188,16 +192,18 @@ static struct uac1_as_header_descriptor as_in_header_desc = {
	.wFormatTag =		cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
};

DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
#define uac_format_type_i_discrete_descriptor			\
	uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES

static struct uac_format_type_i_discrete_descriptor_1 as_out_type_i_desc = {
	.bLength =		UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = {
	.bLength =		0, /* filled on rate setup */
	.bDescriptorType =	USB_DT_CS_INTERFACE,
	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
	.bFormatType =		UAC_FORMAT_TYPE_I,
	.bSubframeSize =	2,
	.bBitResolution =	16,
	.bSamFreqType =		1,
	.bSamFreqType =		0, /* filled on rate setup */
};

/* Standard ISO OUT Endpoint Descriptor */
@@ -221,14 +227,14 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
	.wLockDelay =		cpu_to_le16(1),
};

static struct uac_format_type_i_discrete_descriptor_1 as_in_type_i_desc = {
	.bLength =		UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = {
	.bLength =		0, /* filled on rate setup */
	.bDescriptorType =	USB_DT_CS_INTERFACE,
	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
	.bFormatType =		UAC_FORMAT_TYPE_I,
	.bSubframeSize =	2,
	.bBitResolution =	16,
	.bSamFreqType =		1,
	.bSamFreqType =		0, /* filled on rate setup */
};

/* Standard ISO OUT Endpoint Descriptor */
@@ -333,6 +339,30 @@ static struct usb_gadget_strings *uac1_strings[] = {
 * This function is an ALSA sound card following USB Audio Class Spec 1.0.
 */

static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req)
{
	struct usb_function *fn = ep->driver_data;
	struct usb_composite_dev *cdev = fn->config->cdev;
	struct g_audio *agdev = func_to_g_audio(fn);
	struct f_uac1 *uac1 = func_to_uac1(fn);
	u8 *buf = (u8 *)req->buf;
	u32 val = 0;

	if (req->actual != 3) {
		WARN(cdev, "Invalid data size for UAC_EP_CS_ATTR_SAMPLE_RATE.\n");
		return;
	}

	val = buf[0] | (buf[1] << 8) | (buf[2] << 16);
	if (uac1->ctl_id == (USB_DIR_IN | 2)) {
		uac1->p_srate = val;
		u_audio_set_playback_srate(agdev, uac1->p_srate);
	} else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
		uac1->c_srate = val;
		u_audio_set_capture_srate(agdev, uac1->c_srate);
	}
}

static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
{
	struct g_audio *audio = req->context;
@@ -707,18 +737,27 @@ static int audio_set_endpoint_req(struct usb_function *f,
		const struct usb_ctrlrequest *ctrl)
{
	struct usb_composite_dev *cdev = f->config->cdev;
	struct usb_request	*req = f->config->cdev->req;
	struct f_uac1		*uac1 = func_to_uac1(f);
	int			value = -EOPNOTSUPP;
	u16			ep = le16_to_cpu(ctrl->wIndex);
	u16			len = le16_to_cpu(ctrl->wLength);
	u16			w_value = le16_to_cpu(ctrl->wValue);
	u8			cs = w_value >> 8;

	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
			ctrl->bRequest, w_value, len, ep);

	switch (ctrl->bRequest) {
	case UAC_SET_CUR:
	case UAC_SET_CUR: {
		if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
			cdev->gadget->ep0->driver_data = f;
			uac1->ctl_id = ep;
			req->complete = uac_cs_attr_sample_rate;
		}
		value = len;
		break;
	}

	case UAC_SET_MIN:
		break;
@@ -743,16 +782,33 @@ static int audio_get_endpoint_req(struct usb_function *f,
		const struct usb_ctrlrequest *ctrl)
{
	struct usb_composite_dev *cdev = f->config->cdev;
	struct usb_request *req = f->config->cdev->req;
	struct f_uac1 *uac1 = func_to_uac1(f);
	u8 *buf = (u8 *)req->buf;
	int value = -EOPNOTSUPP;
	u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
	u8 ep = le16_to_cpu(ctrl->wIndex);
	u16 len = le16_to_cpu(ctrl->wLength);
	u16 w_value = le16_to_cpu(ctrl->wValue);
	u8 cs = w_value >> 8;
	u32 val = 0;

	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
			ctrl->bRequest, w_value, len, ep);

	switch (ctrl->bRequest) {
	case UAC_GET_CUR:
	case UAC_GET_CUR: {
		if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
			if (ep == (USB_DIR_IN | 2))
				val = uac1->p_srate;
			else if (ep == (USB_DIR_OUT | 1))
				val = uac1->c_srate;
			buf[2] = (val >> 16) & 0xff;
			buf[1] = (val >> 8) & 0xff;
			buf[0] = val & 0xff;
		}
		value = len;
		break;
	}
	case UAC_GET_MIN:
	case UAC_GET_MAX:
	case UAC_GET_RES:
@@ -1074,10 +1130,10 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
	} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
		dev_err(dev, "Error: incorrect capture sample size\n");
		return -EINVAL;
	} else if (!opts->p_srate) {
	} else if (!opts->p_srates[0]) {
		dev_err(dev, "Error: incorrect playback sampling rate\n");
		return -EINVAL;
	} else if (!opts->c_srate) {
	} else if (!opts->c_srates[0]) {
		dev_err(dev, "Error: incorrect capture sampling rate\n");
		return -EINVAL;
	}
@@ -1118,10 +1174,9 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
	struct f_uac1_opts		*audio_opts;
	struct usb_ep			*ep = NULL;
	struct usb_string		*us;
	u8				*sam_freq;
	int				rate;
	int				ba_iface_id;
	int				status;
	int				idx, i;

	status = f_audio_validate_opts(audio, dev);
	if (status)
@@ -1213,12 +1268,25 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
	}

	/* Set sample rates */
	rate = audio_opts->c_srate;
	sam_freq = as_out_type_i_desc.tSamFreq[0];
	memcpy(sam_freq, &rate, 3);
	rate = audio_opts->p_srate;
	sam_freq = as_in_type_i_desc.tSamFreq[0];
	memcpy(sam_freq, &rate, 3);
	for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
		if (audio_opts->c_srates[i] == 0)
			break;
		memcpy(as_out_type_i_desc.tSamFreq[idx++],
				&audio_opts->c_srates[i], 3);
	}
	as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
	as_out_type_i_desc.bSamFreqType = idx;

	for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
		if (audio_opts->p_srates[i] == 0)
			break;
		memcpy(as_in_type_i_desc.tSamFreq[idx++],
				&audio_opts->p_srates[i], 3);
	}
	as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
	as_in_type_i_desc.bSamFreqType = idx;
	uac1->p_srate = audio_opts->p_srates[0];
	uac1->c_srate = audio_opts->c_srates[0];

	/* allocate instance-specific interface IDs, and patch descriptors */
	status = usb_interface_id(c, f);
@@ -1297,7 +1365,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
	audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
	audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
	audio->params.c_chmask = audio_opts->c_chmask;
	audio->params.c_srates[0] = audio_opts->c_srate;
	memcpy(audio->params.c_srates, audio_opts->c_srates,
			sizeof(audio->params.c_srates));
	audio->params.c_ssize = audio_opts->c_ssize;
	if (FUIN_EN(audio_opts)) {
		audio->params.p_fu.id = USB_IN_FU_ID;
@@ -1309,7 +1378,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
		audio->params.p_fu.volume_res = audio_opts->p_volume_res;
	}
	audio->params.p_chmask = audio_opts->p_chmask;
	audio->params.p_srates[0] = audio_opts->p_srate;
	memcpy(audio->params.p_srates, audio_opts->p_srates,
			sizeof(audio->params.p_srates));
	audio->params.p_ssize = audio_opts->p_ssize;
	if (FUOUT_EN(audio_opts)) {
		audio->params.c_fu.id = USB_OUT_FU_ID;
@@ -1414,11 +1484,70 @@ end: \
									\
CONFIGFS_ATTR(f_uac1_opts_, name)

#define UAC1_RATE_ATTRIBUTE(name)					\
static ssize_t f_uac1_opts_##name##_show(struct config_item *item,	\
					 char *page)			\
{									\
	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
	int result = 0;							\
	int i;								\
									\
	mutex_lock(&opts->lock);					\
	page[0] = '\0';							\
	for (i = 0; i < UAC_MAX_RATES; i++) {				\
		if (opts->name##s[i] == 0)				\
			break;						\
		result += sprintf(page + strlen(page), "%u,",		\
				opts->name##s[i]);			\
	}								\
	if (strlen(page) > 0)						\
		page[strlen(page) - 1] = '\n';				\
	mutex_unlock(&opts->lock);					\
									\
	return result;							\
}									\
									\
static ssize_t f_uac1_opts_##name##_store(struct config_item *item,	\
					  const char *page, size_t len)	\
{									\
	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
	char *split_page = NULL;					\
	int ret = -EINVAL;						\
	char *token;							\
	u32 num;							\
	int i;								\
									\
	mutex_lock(&opts->lock);					\
	if (opts->refcnt) {						\
		ret = -EBUSY;						\
		goto end;						\
	}								\
									\
	i = 0;								\
	memset(opts->name##s, 0x00, sizeof(opts->name##s));		\
	split_page = kstrdup(page, GFP_KERNEL);				\
	while ((token = strsep(&split_page, ",")) != NULL) {		\
		ret = kstrtou32(token, 0, &num);			\
		if (ret)						\
			goto end;					\
									\
		opts->name##s[i++] = num;				\
		ret = len;						\
	};								\
									\
end:									\
	kfree(split_page);						\
	mutex_unlock(&opts->lock);					\
	return ret;							\
}									\
									\
CONFIGFS_ATTR(f_uac1_opts_, name)

UAC1_ATTRIBUTE(u32, c_chmask);
UAC1_ATTRIBUTE(u32, c_srate);
UAC1_RATE_ATTRIBUTE(c_srate);
UAC1_ATTRIBUTE(u32, c_ssize);
UAC1_ATTRIBUTE(u32, p_chmask);
UAC1_ATTRIBUTE(u32, p_srate);
UAC1_RATE_ATTRIBUTE(p_srate);
UAC1_ATTRIBUTE(u32, p_ssize);
UAC1_ATTRIBUTE(u32, req_number);

@@ -1487,10 +1616,10 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
				    &f_uac1_func_type);

	opts->c_chmask = UAC1_DEF_CCHMASK;
	opts->c_srate = UAC1_DEF_CSRATE;
	opts->c_srates[0] = UAC1_DEF_CSRATE;
	opts->c_ssize = UAC1_DEF_CSSIZE;
	opts->p_chmask = UAC1_DEF_PCHMASK;
	opts->p_srate = UAC1_DEF_PSRATE;
	opts->p_srates[0] = UAC1_DEF_PSRATE;
	opts->p_ssize = UAC1_DEF_PSSIZE;

	opts->p_mute_present = UAC1_DEF_MUTE_PRESENT;
+3 −2
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#define __U_UAC1_H

#include <linux/usb/composite.h>
#include "uac_common.h"

#define UAC1_OUT_EP_MAX_PACKET_SIZE	200
#define UAC1_DEF_CCHMASK	0x3
@@ -30,10 +31,10 @@
struct f_uac1_opts {
	struct usb_function_instance	func_inst;
	int				c_chmask;
	int				c_srate;
	int				c_srates[UAC_MAX_RATES];
	int				c_ssize;
	int				p_chmask;
	int				p_srate;
	int				p_srates[UAC_MAX_RATES];
	int				p_ssize;

	bool			p_mute_present;
+17 −8
Original line number Diff line number Diff line
@@ -61,9 +61,10 @@ module_param(p_chmask, uint, 0444);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");

/* Playback Default 48 KHz */
static int p_srate = UAC1_DEF_PSRATE;
module_param(p_srate, uint, 0444);
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
static int p_srates[UAC_MAX_RATES] = {UAC1_DEF_PSRATE};
static int p_srates_cnt = 1;
module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, 0444);
MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)");

/* Playback Default 16bits/sample */
static int p_ssize = UAC1_DEF_PSSIZE;
@@ -76,9 +77,10 @@ module_param(c_chmask, uint, 0444);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");

/* Capture Default 48 KHz */
static int c_srate = UAC1_DEF_CSRATE;
module_param(c_srate, uint, 0444);
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
static int c_srates[UAC_MAX_RATES] = {UAC1_DEF_CSRATE};
static int c_srates_cnt = 1;
module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, 0444);
MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");

/* Capture Default 16bits/sample */
static int c_ssize = UAC1_DEF_CSSIZE;
@@ -243,6 +245,7 @@ static int audio_bind(struct usb_composite_dev *cdev)
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
	struct f_uac1_opts	*uac1_opts;
	int i;
#else
	struct f_uac1_legacy_opts	*uac1_opts;
#endif
@@ -282,10 +285,16 @@ static int audio_bind(struct usb_composite_dev *cdev)
#ifndef CONFIG_GADGET_UAC1_LEGACY
	uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
	uac1_opts->p_chmask = p_chmask;
	uac1_opts->p_srate = p_srate;

	for (i = 0; i < p_srates_cnt; ++i)
		uac1_opts->p_srates[i] = p_srates[i];

	uac1_opts->p_ssize = p_ssize;
	uac1_opts->c_chmask = c_chmask;
	uac1_opts->c_srate = c_srate;

	for (i = 0; i < c_srates_cnt; ++i)
		uac1_opts->c_srates[i] = c_srates[i];

	uac1_opts->c_ssize = c_ssize;
	uac1_opts->req_number = UAC1_DEF_REQ_NUM;
#else /* CONFIG_GADGET_UAC1_LEGACY */