Commit 355a05dc authored by Pavel Hofman's avatar Pavel Hofman Committed by Greg Kroah-Hartman
Browse files

usb: gadget: f_uac2: Optionally determine bInterval for HS and SS



Allow setting configfs params p_hs_bint/c_hs_bint to 0. If they are set
to 0, determine 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), keep bInterval at 1.

The FS speed is left at fixed bInterval=1.

If for any speed the required bandwidth exceeds the max bandwidth
corresponding to the selected/determined bInterval, print a warning.

Signed-off-by: default avatarPavel Hofman <pavel.hofman@ivitera.com>
Link: https://lore.kernel.org/r/20220127114331.41367-4-pavel.hofman@ivitera.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a857c42f
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ Description:
		c_chmask		capture channel mask
		c_srate			list of capture sampling rates (comma-separated)
		c_ssize			capture sample size (bytes)
		c_hs_bint		capture bInterval for HS/SS (1-4)
		c_hs_bint		capture bInterval for HS/SS (1-4: fixed, 0: auto)
		c_sync			capture synchronization type
					(async/adaptive)
		c_mute_present		capture mute control enable
@@ -23,7 +23,7 @@ Description:
		p_chmask		playback channel mask
		p_srate			list of playback sampling rates (comma-separated)
		p_ssize			playback sample size (bytes)
		p_hs_bint		playback bInterval for HS/SS (1-4)
		p_hs_bint		playback bInterval for HS/SS (1-4: fixed, 0: auto)
		p_mute_present		playback mute control enable
		p_volume_present	playback volume control enable
		p_volume_min		playback volume control min value
+2 −2
Original line number Diff line number Diff line
@@ -734,7 +734,7 @@ The uac2 function provides these attributes in its function directory:
	c_volume_min     capture volume control min value (in 1/256 dB)
	c_volume_max     capture volume control max value (in 1/256 dB)
	c_volume_res     capture volume control resolution (in 1/256 dB)
	c_hs_bint        capture bInterval for HS/SS (1-4)
	c_hs_bint        capture bInterval for HS/SS (1-4: fixed, 0: auto)
	fb_max           maximum extra bandwidth in async mode
	p_chmask         playback channel mask
	p_srate          list of playback sampling rates (comma-separated)
@@ -744,7 +744,7 @@ The uac2 function provides these attributes in its function directory:
	p_volume_min     playback volume control min value (in 1/256 dB)
	p_volume_max     playback volume control max value (in 1/256 dB)
	p_volume_res     playback volume control resolution (in 1/256 dB)
	p_hs_bint        playback bInterval for HS/SS (1-4)
	p_hs_bint        playback bInterval for HS/SS (1-4: fixed, 0: auto)
	req_number       the number of pre-allocated request for both capture
	                 and playback
	function_name    name of the interface
+85 −41
Original line number Diff line number Diff line
@@ -664,29 +664,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_bint(const struct f_uac2_opts *uac2_opts,
	u8 bint, 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;
@@ -704,14 +686,76 @@ 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 << (bint - 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 << (bint - 1))) + 1);
	}
	return max_size_bw;
}

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

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

	case USB_SPEED_HIGH:
	case USB_SPEED_SUPER:
		max_size_ep = 1024;
		if (is_playback)
			opts_bint = uac2_opts->p_hs_bint;
		else
			opts_bint = uac2_opts->c_hs_bint;

		if (opts_bint > 0) {
			/* fixed bint */
			bint = opts_bint;
			max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 8000, is_playback);
		} else {
			/* checking bInterval from 4 to 1 whether the required bandwidth fits */
			for (bint = 4; bint > 0; --bint) {
				max_size_bw = get_max_bw_for_bint(
					uac2_opts, bint, 8000, is_playback);
				if (max_size_bw <= max_size_ep)
					break;
			}
		}
		break;

	default:
		return -EINVAL;
	}

	if (is_playback)
		dir = "Playback";
	else
		dir = "Capture";

	if (max_size_bw <= max_size_ep)
		dev_dbg(dev,
			"%s: Will use maxpctksize %d and bInterval %d\n",
			dir, max_size_bw, bint);
	else {
		dev_warn(dev,
			"%s: Req. maxpcktsize %d at bInterval %d > max ISOC %d, may drop data!\n",
			dir, max_size_bw, bint, max_size_ep);
		max_size_bw = max_size_ep;
	}
	ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw,
						    max_size_ep));

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

	return 0;
}
@@ -965,13 +1009,13 @@ static int afunc_validate_opts(struct g_audio *agdev, struct device *dev)
			return -EINVAL;
	}

	if ((opts->p_hs_bint < 1) || (opts->p_hs_bint > 4)) {
		dev_err(dev, "Error: incorrect playback HS/SS bInterval (1-4)\n");
	if ((opts->p_hs_bint < 0) || (opts->p_hs_bint > 4)) {
		dev_err(dev, "Error: incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)\n");
		return -EINVAL;
	}

	if ((opts->c_hs_bint < 1) || (opts->c_hs_bint > 4)) {
		dev_err(dev, "Error: incorrect capture HS/SS bInterval (1-4)\n");
	if ((opts->c_hs_bint < 0) || (opts->c_hs_bint > 4)) {
		dev_err(dev, "Error: incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)\n");
		return -EINVAL;
	}

@@ -1141,43 +1185,43 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
	ss_epout_desc.bInterval = uac2_opts->c_hs_bint;

	/* Calculate wMaxPacketSize according to audio bandwidth */
	ret = set_ep_max_packet_size(uac2_opts, &fs_epin_desc, USB_SPEED_FULL,
				     true);
	ret = set_ep_max_packet_size_bint(dev, 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,
				     false);
	ret = set_ep_max_packet_size_bint(dev, 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,
				     true);
	ret = set_ep_max_packet_size_bint(dev, 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,
				     false);
	ret = set_ep_max_packet_size_bint(dev, 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,
				     true);
	ret = set_ep_max_packet_size_bint(dev, 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,
				     false);
	ret = set_ep_max_packet_size_bint(dev, uac2_opts, &ss_epout_desc,
					USB_SPEED_SUPER, false);
	if (ret < 0) {
		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
		return ret;
+2 −2
Original line number Diff line number Diff line
@@ -19,11 +19,11 @@
#define UAC2_DEF_PCHMASK 0x3
#define UAC2_DEF_PSRATE 48000
#define UAC2_DEF_PSSIZE 2
#define UAC2_DEF_PHSBINT 4
#define UAC2_DEF_PHSBINT 0
#define UAC2_DEF_CCHMASK 0x3
#define UAC2_DEF_CSRATE 64000
#define UAC2_DEF_CSSIZE 2
#define UAC2_DEF_CHSBINT 4
#define UAC2_DEF_CHSBINT 0
#define UAC2_DEF_CSYNC		USB_ENDPOINT_SYNC_ASYNC

#define UAC2_DEF_MUTE_PRESENT	1