diff options
Diffstat (limited to 'drivers/usb/gadget/function/f_uac2.c')
-rw-r--r-- | drivers/usb/gadget/function/f_uac2.c | 100 |
1 files changed, 91 insertions, 9 deletions
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 5d63244ba319..321e6c05ba93 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -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; } |