usb: gadget: f_uac2: add adaptive sync support for capture
authorRuslan Bilovol <ruslan.bilovol@gmail.com>
Thu, 3 Jun 2021 22:01:03 +0000 (00:01 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 9 Jun 2021 09:26:49 +0000 (11:26 +0200)
Current f_uac2 USB OUT (aka 'capture') synchronization
implements 'ASYNC' scenario which means USB Gadget has
it's own freerunning clock and can update Host about
real clock frequency through feedback endpoint so Host
can align number of samples sent to the USB gadget to
prevent overruns/underruns

In case if Gadget can has no it's internal clock and
can consume audio samples at any rate (for example,
on the Gadget side someone records audio directly to
a file, or audio samples are played through an
external DAC as soon as they arrive), UAC2 spec
suggests 'ADAPTIVE' synchronization type.

Change UAC2 driver to make it configurable through
additional 'c_sync' configfs file.

Default remains 'asynchronous' with possibility to
switch it to 'adaptive'

Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Link: https://lore.kernel.org/r/20210603220104.1216001-3-jbrunet@baylibre.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 d4356c8b8cd6541516582de48c9d5479effa0b52..e7e59d7fb12f01d64bbc3f16a473c0db4fd133d8 100644 (file)
@@ -8,6 +8,7 @@ Description:
                c_chmask   capture channel mask
                c_srate    capture sampling rate
                c_ssize    capture sample size (bytes)
+               c_sync     capture synchronization type (async/adaptive)
                p_chmask   playback channel mask
                p_srate    playback sampling rate
                p_ssize    playback sample size (bytes)
index 2085e7b24eeb9b14b431e9b3b67dd7e9d74ca4d4..f5a12667bd414f539665758ebfd51215d19bfa3f 100644 (file)
@@ -728,6 +728,7 @@ The uac2 function provides these attributes in its function directory:
        c_chmask        capture channel mask
        c_srate         capture sampling rate
        c_ssize         capture sample size (bytes)
+       c_sync          capture synchronization type (async/adaptive)
        p_chmask        playback channel mask
        p_srate         playback sampling rate
        p_ssize         playback sample size (bytes)
index 5d63244ba319cb555e36d762e42419a31692d1e9..321e6c05ba93eeae74df6460546ea6fec4e26071 100644 (file)
@@ -44,6 +44,7 @@
 
 #define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
 #define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
+#define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC)
 
 struct f_uac2 {
        struct g_audio g_audio;
@@ -240,7 +241,7 @@ static struct usb_interface_descriptor std_as_out_if1_desc = {
        .bDescriptorType = USB_DT_INTERFACE,
 
        .bAlternateSetting = 1,
-       .bNumEndpoints = 2,
+       .bNumEndpoints = 1,
        .bInterfaceClass = USB_CLASS_AUDIO,
        .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
        .bInterfaceProtocol = UAC_VERSION_2,
@@ -273,7 +274,7 @@ static struct usb_endpoint_descriptor fs_epout_desc = {
        .bDescriptorType = USB_DT_ENDPOINT,
 
        .bEndpointAddress = USB_DIR_OUT,
-       .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+       /* .bmAttributes = DYNAMIC */
        /* .wMaxPacketSize = DYNAMIC */
        .bInterval = 1,
 };
@@ -282,7 +283,7 @@ static struct usb_endpoint_descriptor hs_epout_desc = {
        .bLength = USB_DT_ENDPOINT_SIZE,
        .bDescriptorType = USB_DT_ENDPOINT,
 
-       .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+       /* .bmAttributes = DYNAMIC */
        /* .wMaxPacketSize = DYNAMIC */
        .bInterval = 4,
 };
@@ -292,7 +293,7 @@ static struct usb_endpoint_descriptor ss_epout_desc = {
        .bDescriptorType = USB_DT_ENDPOINT,
 
        .bEndpointAddress = USB_DIR_OUT,
-       .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+       /* .bmAttributes = DYNAMIC */
        /* .wMaxPacketSize = DYNAMIC */
        .bInterval = 4,
 };
@@ -649,7 +650,9 @@ static void setup_headers(struct f_uac2_opts *opts,
                        headers[i++] = USBDHDR(epout_desc_comp);
 
                headers[i++] = USBDHDR(&as_iso_out_desc);
-               headers[i++] = USBDHDR(epin_fback_desc);
+
+               if (EPOUT_FBACK_IN_EN(opts))
+                       headers[i++] = USBDHDR(epin_fback_desc);
        }
        if (EPIN_EN(opts)) {
                headers[i++] = USBDHDR(&std_as_in_if0_desc);
@@ -820,6 +823,23 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
                std_as_out_if1_desc.bInterfaceNumber = ret;
                uac2->as_out_intf = ret;
                uac2->as_out_alt = 0;
+
+               if (EPOUT_FBACK_IN_EN(uac2_opts)) {
+                       fs_epout_desc.bmAttributes =
+                         USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
+                       hs_epout_desc.bmAttributes =
+                         USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
+                       ss_epout_desc.bmAttributes =
+                         USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
+                       std_as_out_if1_desc.bNumEndpoints++;
+               } else {
+                       fs_epout_desc.bmAttributes =
+                         USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
+                       hs_epout_desc.bmAttributes =
+                         USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
+                       ss_epout_desc.bmAttributes =
+                         USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
+               }
        }
 
        if (EPIN_EN(uac2_opts)) {
@@ -883,11 +903,14 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
                        dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
                        return -ENODEV;
                }
-               agdev->in_ep_fback = usb_ep_autoconfig(gadget,
+               if (EPOUT_FBACK_IN_EN(uac2_opts)) {
+                       agdev->in_ep_fback = usb_ep_autoconfig(gadget,
                                                       &fs_epin_fback_desc);
-               if (!agdev->in_ep_fback) {
-                       dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-                       return -ENODEV;
+                       if (!agdev->in_ep_fback) {
+                               dev_err(dev, "%s:%d Error!\n",
+                                       __func__, __LINE__);
+                               return -ENODEV;
+                       }
                }
        }
 
@@ -1242,11 +1265,68 @@ end:                                                                    \
                                                                        \
 CONFIGFS_ATTR(f_uac2_opts_, name)
 
+#define UAC2_ATTRIBUTE_SYNC(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;                                                     \
+       char *str;                                                      \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       switch (opts->name) {                                           \
+       case USB_ENDPOINT_SYNC_ASYNC:                                   \
+               str = "async";                                          \
+               break;                                                  \
+       case USB_ENDPOINT_SYNC_ADAPTIVE:                                \
+               str = "adaptive";                                       \
+               break;                                                  \
+       default:                                                        \
+               str = "unknown";                                        \
+               break;                                                  \
+       }                                                               \
+       result = sprintf(page, "%s\n", str);                            \
+       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);                \
+       int ret = 0;                                                    \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       if (opts->refcnt) {                                             \
+               ret = -EBUSY;                                           \
+               goto end;                                               \
+       }                                                               \
+                                                                       \
+       if (!strncmp(page, "async", 5))                                 \
+               opts->name = USB_ENDPOINT_SYNC_ASYNC;                   \
+       else if (!strncmp(page, "adaptive", 8))                         \
+               opts->name = USB_ENDPOINT_SYNC_ADAPTIVE;                \
+       else {                                                          \
+               ret = -EINVAL;                                          \
+               goto end;                                               \
+       }                                                               \
+                                                                       \
+       ret = len;                                                      \
+                                                                       \
+end:                                                                   \
+       mutex_unlock(&opts->lock);                                      \
+       return ret;                                                     \
+}                                                                      \
+                                                                       \
+CONFIGFS_ATTR(f_uac2_opts_, name)
+
 UAC2_ATTRIBUTE(p_chmask);
 UAC2_ATTRIBUTE(p_srate);
 UAC2_ATTRIBUTE(p_ssize);
 UAC2_ATTRIBUTE(c_chmask);
 UAC2_ATTRIBUTE(c_srate);
+UAC2_ATTRIBUTE_SYNC(c_sync);
 UAC2_ATTRIBUTE(c_ssize);
 UAC2_ATTRIBUTE(req_number);
 
@@ -1257,6 +1337,7 @@ static struct configfs_attribute *f_uac2_attrs[] = {
        &f_uac2_opts_attr_c_chmask,
        &f_uac2_opts_attr_c_srate,
        &f_uac2_opts_attr_c_ssize,
+       &f_uac2_opts_attr_c_sync,
        &f_uac2_opts_attr_req_number,
        NULL,
 };
@@ -1295,6 +1376,7 @@ static struct usb_function_instance *afunc_alloc_inst(void)
        opts->c_chmask = UAC2_DEF_CCHMASK;
        opts->c_srate = UAC2_DEF_CSRATE;
        opts->c_ssize = UAC2_DEF_CSSIZE;
+       opts->c_sync = UAC2_DEF_CSYNC;
        opts->req_number = UAC2_DEF_REQ_NUM;
        return &opts->func_inst;
 }
index b5035711172d9013b5fa5cf4df71b17e393adf06..13589c3c805c4e0ceb08a4b248f86fc621229bff 100644 (file)
@@ -21,6 +21,7 @@
 #define UAC2_DEF_CCHMASK 0x3
 #define UAC2_DEF_CSRATE 64000
 #define UAC2_DEF_CSSIZE 2
+#define UAC2_DEF_CSYNC         USB_ENDPOINT_SYNC_ASYNC
 #define UAC2_DEF_REQ_NUM 2
 
 struct f_uac2_opts {
@@ -31,6 +32,7 @@ struct f_uac2_opts {
        int                             c_chmask;
        int                             c_srate;
        int                             c_ssize;
+       int                             c_sync;
        int                             req_number;
        bool                            bound;