diff mbox series

[RFC,03/14] usb: gadget: f_uac1: Add adaptive sync support for capture

Message ID 20240928150905.2616313-4-crwulff@gmail.com (mailing list archive)
State RFC
Headers show
Series usb: gadget: f_uac: Add support for alt mode settings | expand

Commit Message

Chris Wulff Sept. 28, 2024, 3:08 p.m. UTC
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 mbox series

Patch

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];