From: Chris Wulff <crwulff@gmail.com>
Allow the user to configure async or adaptive mode for data from
the host. Enabling async mode will include the feedback endpoint.
This functions the same as UAC2.
Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
.../ABI/testing/configfs-usb-gadget-uac1 | 3 +
Documentation/usb/gadget-testing.rst | 2 +
drivers/usb/gadget/function/f_uac1.c | 101 +++++++++++++++++-
drivers/usb/gadget/function/u_uac1.h | 3 +
4 files changed, 108 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1
index 64188a85592b..758b8c9a988a 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uac1
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1
@@ -8,6 +8,8 @@ Description:
c_chmask capture channel mask
c_srate list of capture sampling rates (comma-separated)
c_ssize capture sample size (bytes)
+ c_sync capture synchronization type
+ (async/adaptive)
c_mute_present capture mute control enable
c_volume_present capture volume control enable
c_volume_min capture volume control min value
@@ -16,6 +18,7 @@ Description:
(in 1/256 dB)
c_volume_res capture volume control resolution
(in 1/256 dB)
+ fb_max maximum extra bandwidth in async mode
p_chmask playback channel mask
p_srate list of playback sampling rates (comma-separated)
p_ssize playback sample size (bytes)
diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst
index bf555c2270f5..68fc0011b388 100644
--- a/Documentation/usb/gadget-testing.rst
+++ b/Documentation/usb/gadget-testing.rst
@@ -952,11 +952,13 @@ The uac1 function provides these attributes in its function directory:
c_chmask capture channel mask
c_srate list of capture sampling rates (comma-separated)
c_ssize capture sample size (bytes)
+ c_sync capture synchronization type (async/adaptive)
c_mute_present capture mute control enable
c_volume_present capture volume control enable
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)
+ fb_max maximum extra bandwidth in async mode
p_chmask playback channel mask
p_srate list of playback sampling rates (comma-separated)
p_ssize playback sample size (bytes)
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index f68d444d1961..84423d9a8bd7 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -33,6 +33,7 @@
|| (_opts)->p_volume_present)
#define FUOUT_EN(_opts) ((_opts)->c_mute_present \
|| (_opts)->c_volume_present)
+#define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC)
struct f_uac1 {
struct g_audio g_audio;
@@ -305,6 +306,48 @@ static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
.wLockDelay = 0,
};
+/* STD AS ISO IN Feedback Endpoint */
+static struct usb_endpoint_descriptor fs_as_in_fback_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK,
+ .wMaxPacketSize = cpu_to_le16(3),
+ .bInterval = 1,
+ .bRefresh = 0,
+ .bSynchAddress = 0,
+};
+
+static struct usb_endpoint_descriptor hs_as_in_fback_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK,
+ .wMaxPacketSize = cpu_to_le16(4),
+ .bInterval = 4,
+ .bRefresh = 0,
+ .bSynchAddress = 0,
+};
+
+static struct usb_endpoint_descriptor ss_as_in_fback_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK,
+ .wMaxPacketSize = cpu_to_le16(4),
+ .bInterval = 4,
+ .bRefresh = 0,
+ .bSynchAddress = 0,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_as_in_fback_desc_comp = {
+ .bLength = sizeof(ss_as_in_fback_desc_comp),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = 0,
+ .wBytesPerInterval = cpu_to_le16(4),
+};
+
static struct usb_descriptor_header *f_audio_fs_desc[] = {
(struct usb_descriptor_header *)&ac_interface_desc,
(struct usb_descriptor_header *)&ac_header_desc,
@@ -327,6 +370,7 @@ static struct usb_descriptor_header *f_audio_fs_desc[] = {
(struct usb_descriptor_header *)&fs_as_out_ep_desc,
(struct usb_descriptor_header *)&as_iso_out_desc,
+ (struct usb_descriptor_header *)&fs_as_in_fback_desc,
(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
@@ -361,6 +405,7 @@ static struct usb_descriptor_header *f_audio_hs_desc[] = {
(struct usb_descriptor_header *)&hs_as_out_ep_desc,
(struct usb_descriptor_header *)&as_iso_out_desc,
+ (struct usb_descriptor_header *)&hs_as_in_fback_desc,
(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
@@ -435,6 +480,8 @@ static struct usb_descriptor_header *f_audio_ss_desc[] = {
(struct usb_descriptor_header *)&ss_as_out_ep_desc,
(struct usb_descriptor_header *)&ss_as_out_ep_desc_comp,
(struct usb_descriptor_header *)&as_iso_out_desc,
+ (struct usb_descriptor_header *)&ss_as_in_fback_desc,
+ (struct usb_descriptor_header *)&ss_as_in_fback_desc_comp,
(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
@@ -1236,9 +1283,11 @@ static void setup_headers(struct f_uac1_opts *opts,
{
struct usb_ss_ep_comp_descriptor *epout_desc_comp = NULL;
struct usb_ss_ep_comp_descriptor *epin_desc_comp = NULL;
+ struct usb_ss_ep_comp_descriptor *epin_fback_desc_comp = NULL;
struct usb_ss_ep_comp_descriptor *ep_int_desc_comp = NULL;
struct usb_endpoint_descriptor *epout_desc;
struct usb_endpoint_descriptor *epin_desc;
+ struct usb_endpoint_descriptor *epin_fback_desc;
struct usb_endpoint_descriptor *ep_int_desc;
int i;
@@ -1246,11 +1295,13 @@ static void setup_headers(struct f_uac1_opts *opts,
case USB_SPEED_FULL:
epout_desc = &fs_as_out_ep_desc;
epin_desc = &fs_as_in_ep_desc;
+ epin_fback_desc = &fs_as_in_fback_desc;
ep_int_desc = &fs_ac_int_ep_desc;
break;
case USB_SPEED_HIGH:
epout_desc = &hs_as_out_ep_desc;
epin_desc = &hs_as_in_ep_desc;
+ epin_fback_desc = &hs_as_in_fback_desc;
ep_int_desc = &hs_ac_int_ep_desc;
break;
default:
@@ -1258,6 +1309,8 @@ static void setup_headers(struct f_uac1_opts *opts,
epin_desc = &ss_as_in_ep_desc;
epout_desc_comp = &ss_as_out_ep_desc_comp;
epin_desc_comp = &ss_as_in_ep_desc_comp;
+ epin_fback_desc = &ss_as_in_fback_desc;
+ epin_fback_desc_comp = &ss_as_in_fback_desc_comp;
ep_int_desc = &ss_ac_int_ep_desc;
ep_int_desc_comp = &ss_ac_int_ep_desc_comp;
}
@@ -1296,6 +1349,12 @@ static void setup_headers(struct f_uac1_opts *opts,
headers[i++] = USBDHDR(epout_desc_comp);
headers[i++] = USBDHDR(&as_iso_out_desc);
+
+ if (EPOUT_FBACK_IN_EN(opts)) {
+ headers[i++] = USBDHDR(epin_fback_desc);
+ if (epin_fback_desc_comp)
+ headers[i++] = USBDHDR(epin_fback_desc_comp);
+ }
}
if (EPIN_EN(opts)) {
headers[i++] = USBDHDR(&as_in_interface_alt_0_desc);
@@ -1509,6 +1568,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0)
goto err_free_fu;
ac_interface_desc.bInterfaceNumber = status;
+ ac_interface_desc.bNumEndpoints = 1;
uac1->ac_intf = status;
uac1->ac_alt = 0;
@@ -1523,6 +1583,23 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
uac1->as_out_intf = status;
uac1->as_out_alt = 0;
+
+ if (EPOUT_FBACK_IN_EN(audio_opts)) {
+ fs_as_out_ep_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
+ hs_as_out_ep_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
+ ss_as_out_ep_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
+ ac_interface_desc.bNumEndpoints++;
+ } else {
+ fs_as_out_ep_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
+ hs_as_out_ep_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
+ ss_as_out_ep_desc.bmAttributes =
+ USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
+ }
}
if (EPIN_EN(audio_opts)) {
@@ -1569,6 +1646,17 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->out_ep = ep;
audio->out_ep->desc = &fs_as_out_ep_desc;
+
+ if (EPOUT_FBACK_IN_EN(audio_opts)) {
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_fback_desc);
+ if (!ep)
+ goto err_free_fu;
+
+ hs_as_in_fback_desc.bEndpointAddress = fs_as_in_fback_desc.bEndpointAddress;
+ ss_as_in_fback_desc.bEndpointAddress = fs_as_in_fback_desc.bEndpointAddress;
+
+ audio->in_ep_fback = ep;
+ }
}
if (EPIN_EN(audio_opts)) {
@@ -1631,7 +1719,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->params.c_fu.volume_res = audio_opts->c_volume_res;
}
audio->params.req_number = audio_opts->req_number;
- audio->params.fb_max = FBACK_FAST_MAX;
+ audio->params.fb_max = audio_opts->fb_max;
if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts))
audio->notify = audio_notify;
@@ -1678,6 +1766,10 @@ static struct configfs_item_operations f_uac1_item_ops = {
UAC_ATTRIBUTE(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts, \
opts->lock, opts->refcnt, type, name)
+#define UAC1_ATTRIBUTE_SYNC(name) \
+ UAC_ATTRIBUTE_SYNC(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts, \
+ opts->lock, opts->refcnt, name)
+
#define UAC1_RATE_ATTRIBUTE(name) \
UAC_RATE_ATTRIBUTE(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts, \
opts->lock, opts->refcnt, name)
@@ -1688,6 +1780,7 @@ static struct configfs_item_operations f_uac1_item_ops = {
UAC1_ATTRIBUTE(u32, c_chmask);
UAC1_RATE_ATTRIBUTE(c_srate);
+UAC1_ATTRIBUTE_SYNC(c_sync);
UAC1_ATTRIBUTE(u32, c_ssize);
UAC1_ATTRIBUTE(u32, p_chmask);
UAC1_RATE_ATTRIBUTE(p_srate);
@@ -1706,6 +1799,8 @@ UAC1_ATTRIBUTE(s16, c_volume_min);
UAC1_ATTRIBUTE(s16, c_volume_max);
UAC1_ATTRIBUTE(s16, c_volume_res);
+UAC1_ATTRIBUTE(u32, fb_max);
+
UAC1_ATTRIBUTE_STRING(function_name);
UAC1_ATTRIBUTE_STRING(p_it_name);
@@ -1721,11 +1816,13 @@ UAC1_ATTRIBUTE_STRING(c_fu_vol_name);
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_chmask,
&f_uac1_opts_attr_c_srate,
+ &f_uac1_opts_attr_c_sync,
&f_uac1_opts_attr_c_ssize,
&f_uac1_opts_attr_p_chmask,
&f_uac1_opts_attr_p_srate,
&f_uac1_opts_attr_p_ssize,
&f_uac1_opts_attr_req_number,
+ &f_uac1_opts_attr_fb_max,
&f_uac1_opts_attr_p_mute_present,
&f_uac1_opts_attr_p_volume_present,
@@ -1784,6 +1881,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
opts->c_chmask = UAC1_DEF_CCHMASK;
opts->c_srates[0] = UAC1_DEF_CSRATE;
+ opts->c_sync = UAC1_DEF_CSYNC;
opts->c_ssize = UAC1_DEF_CSSIZE;
opts->p_chmask = UAC1_DEF_PCHMASK;
opts->p_srates[0] = UAC1_DEF_PSRATE;
@@ -1802,6 +1900,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
opts->c_volume_res = UAC1_DEF_RES_DB;
opts->req_number = UAC1_DEF_REQ_NUM;
+ opts->fb_max = FBACK_FAST_MAX;
scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index feb6eb76462f..59eac5ca8114 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -14,6 +14,7 @@
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
#define UAC1_DEF_CCHMASK 0x3
#define UAC1_DEF_CSRATE 48000
+#define UAC1_DEF_CSYNC USB_ENDPOINT_SYNC_ADAPTIVE
#define UAC1_DEF_CSSIZE 2
#define UAC1_DEF_PCHMASK 0x3
#define UAC1_DEF_PSRATE 48000
@@ -32,6 +33,7 @@ struct f_uac1_opts {
struct usb_function_instance func_inst;
int c_chmask;
int c_srates[UAC_MAX_RATES];
+ int c_sync;
int c_ssize;
int p_chmask;
int p_srates[UAC_MAX_RATES];
@@ -50,6 +52,7 @@ struct f_uac1_opts {
s16 c_volume_res;
int req_number;
+ int fb_max;
unsigned bound:1;
char function_name[USB_MAX_STRING_LEN];
--
2.43.0
© 2016 - 2024 Red Hat, Inc.