usb: gadget: f_uac2: Optionally determine bInterval for HS and SS
authorPavel Hofman <pavel.hofman@ivitera.com>
Thu, 27 Jan 2022 11:43:30 +0000 (12:43 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 31 Jan 2022 13:26:18 +0000 (14:26 +0100)
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>
Documentation/ABI/testing/configfs-usb-gadget-uac2
Documentation/usb/gadget-testing.rst
drivers/usb/gadget/function/f_uac2.c
drivers/usb/gadget/function/u_uac2.h

index 33fb237..3371c39 100644 (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
index bd91191..c6d034a 100644 (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
index 34bb6c9..48d6fb2 100644 (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;
index 733a0e0..0510c9b 100644 (file)
 #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