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: Pavel Hofman <pavel.hofman@ivitera.com>
Link: https://lore.kernel.org/r/20220127114331.41367-4-pavel.hofman@ivitera.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Pavel Hofman 2022-01-27 12:43:30 +01:00 committed by Greg Kroah-Hartman
parent a857c42fca
commit 355a05dc83
4 changed files with 91 additions and 47 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
}
ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw,
max_size_ep));
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(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;

View File

@ -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