From 7282e4ee3d8c43153641eccc5a9d985a9bf12334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Wed, 4 Sep 2024 13:38:09 +0200 Subject: [PATCH] tests: usb: uac2: test High-Speed only instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add and verify against reference descriptors a High-Speed only 192 kHz 24-bit stereo headphones with explicit feedback UAC2 instance. Signed-off-by: Tomasz Moń --- tests/subsys/usb/uac2/app.overlay | 38 +++++ tests/subsys/usb/uac2/src/uac2_desc.c | 220 ++++++++++++++++++++++++++ 2 files changed, 258 insertions(+) diff --git a/tests/subsys/usb/uac2/app.overlay b/tests/subsys/usb/uac2/app.overlay index 583b1f8ef7b..7b7585209a9 100644 --- a/tests/subsys/usb/uac2/app.overlay +++ b/tests/subsys/usb/uac2/app.overlay @@ -74,3 +74,41 @@ }; }; }; + +/ { + hs_uac2_headphones: hs_usb_audio2 { + compatible = "zephyr,uac2"; + status = "okay"; + high-speed; + audio-function = ; + + hs_uac_aclk: hs_aclk { + compatible = "zephyr,uac2-clock-source"; + clock-type = "internal-programmable"; + frequency-control = "host-programmable"; + sampling-frequencies = <192000>; + }; + + hs_out_terminal: hs_out_terminal { + compatible = "zephyr,uac2-input-terminal"; + clock-source = <&hs_uac_aclk>; + terminal-type = ; + front-left; + front-right; + }; + + hs_headphones_output: hs_headphones { + compatible = "zephyr,uac2-output-terminal"; + data-source = <&hs_out_terminal>; + clock-source = <&hs_uac_aclk>; + terminal-type = ; + }; + + hs_as_iso_out: hs_out_interface { + compatible = "zephyr,uac2-audio-streaming"; + linked-terminal = <&hs_out_terminal>; + subslot-size = <3>; + bit-resolution = <24>; + }; + }; +}; diff --git a/tests/subsys/usb/uac2/src/uac2_desc.c b/tests/subsys/usb/uac2/src/uac2_desc.c index 9efcf4947c0..77474649c4c 100644 --- a/tests/subsys/usb/uac2/src/uac2_desc.c +++ b/tests/subsys/usb/uac2/src/uac2_desc.c @@ -10,6 +10,7 @@ #include #include +/* 48 kHz headset with implicit feedback, allowed on both Full and High-Speed */ static const uint8_t reference_ac_interface_descriptor[] = { /* 4.7.2 Class-Specific AC Interface Descriptor */ 0x09, /* bLength = 9 */ @@ -169,6 +170,98 @@ const static struct usb_desc_header *uac2_fs_descriptor_set[] = const static struct usb_desc_header *uac2_hs_descriptor_set[] = UAC2_HS_DESCRIPTOR_PTRS_ARRAY(DT_NODELABEL(uac2_headset)); +VALIDATE_INSTANCE(DT_NODELABEL(uac2_headset)) + +/* 192 kHz 24-bit stereo headphones allowed on High-Speed only */ +static const uint8_t reference_hs_ac_interface_descriptor[] = { + /* 4.7.2 Class-Specific AC Interface Descriptor */ + 0x09, /* bLength = 9 */ + 0x24, /* bDescriptorType = CS_INTERFACE */ + 0x01, /* bDescriptorSubtype = HEADER */ + 0x00, 0x02, /* bcdADC = 02.00 */ + 0xff, /* bCategory = OTHER */ + 0x2e, 0x00, /* wTotalLength = 0x2e = 46 */ + 0x00, /* bmControls = Latency Control not present */ +}; + +static const uint8_t reference_hs_ac_clock_source_descriptor[] = { + /* 4.7.2.1 Clock Source Descriptor */ + 0x08, /* bLength = 8 */ + 0x24, /* bDescriptorType = CS_INTERFACE */ + 0x0a, /* bDescriptorSubtype = CLOCK_SOURCE */ + 0x01, /* bClockID = 1 */ + 0x03, /* bmAttributes = Internal programmable */ + 0x03, /* bmControls = frequency host programmable */ + 0x00, /* bAssocTerminal = 0 (not associated) */ + 0x00, /* iClockSource = 0 (no string descriptor) */ +}; + +static const uint8_t reference_hs_ac_input_terminal_descriptor[] = { + /* 4.7.2.4 Input Terminal Descriptor */ + 0x11, /* bLength = 17 */ + 0x24, /* bDescriptorType = CS_INTERFACE */ + 0x02, /* bDescriptorSubtype = INPUT_TERMINAL */ + 0x02, /* bTerminalID = 2 */ + 0x01, 0x01, /* wTerminalType = 0x0101 (USB streaming) */ + 0x00, /* bAssocTerminal = 0 (not associated) */ + 0x01, /* bCSourceID = 1 (main clock) */ + 0x02, /* bNrChannels = 2 */ + 0x03, 0x00, 0x00, 0x00, /* bmChannelConfig = Front Left, Front Right */ + 0x00, /* iChannelNames = 0 (all pre-defined) */ + 0x00, 0x00, /* bmControls = none present */ + 0x00, /* iTerminal = 0 (no string descriptor) */ +}; + +static const uint8_t reference_hs_ac_output_terminal_descriptor[] = { + /* 4.7.2.5 Output Terminal Descriptor */ + 0x0c, /* bLength = 12 */ + 0x24, /* bDescriptorType = CS_INTERFACE */ + 0x03, /* bDescriptorSubtype = OUTPUT_TERMINAL */ + 0x03, /* bTerminalID = 3 */ + 0x02, 0x03, /* wTerminalType = 0x0302 (Headphones) */ + 0x00, /* bAssocTerminal = 0 (none) */ + 0x02, /* bSourceID = 2 (streaming input) */ + 0x01, /* bCSourceID = 1 (main clock) */ + 0x00, 0x00, /* bmControls = none present */ + 0x00, /* iTerminal = 0 (no string descriptor) */ +}; + +static const uint8_t reference_hs_as_cs_general_descriptor[] = { + /* 4.9.2 Class-Specific AS Interface Descriptor */ + 0x10, /* bLength = 16 */ + 0x24, /* bDescriptorType = CS_INTERFACE */ + 0x01, /* bDescriptorSubtype = AS_GENERAL */ + 0x02, /* bTerminalLink = 2 (USB streaming input) */ + 0x00, /* bmControls = non present */ + 0x01, /* bFormatType = 1 */ + 0x01, 0x00, 0x00, 0x00, /* bmFormats = PCM */ + 0x02, /* bNrChannels = 2 */ + 0x03, 0x00, 0x00, 0x00, /* bmChannelConfig = Front Left, Front Right */ + 0x00, /* iChannelNames = 0 (all pre-defined) */ +}; + +static const uint8_t reference_hs_as_cs_format_descriptor[] = { + /* Universal Serial Bus Device Class Definition for Audio Data Formats + * Release 2.0, May 31, 2006. 2.3.1.6 Type I Format Type Descriptor + */ + 0x06, /* bLength = 6 */ + 0x24, /* bDescriptorType = CS_INTERFACE */ + 0x02, /* bDescriptorSubtype = FORMAT_TYPE */ + 0x01, /* bFormatType = 1 */ + 0x03, /* bSubslotSize = 3 */ + 0x18, /* bBitResolution = 24 */ +}; + +VALIDATE_INSTANCE(DT_NODELABEL(hs_uac2_headphones)) + +UAC2_DESCRIPTOR_ARRAYS(DT_NODELABEL(hs_uac2_headphones)) + +const static struct usb_desc_header *fs_headphones_descriptor_set[] = + UAC2_FS_DESCRIPTOR_PTRS_ARRAY(DT_NODELABEL(hs_uac2_headphones)); + +const static struct usb_desc_header *hs_headphones_descriptor_set[] = + UAC2_HS_DESCRIPTOR_PTRS_ARRAY(DT_NODELABEL(hs_uac2_headphones)); + static void test_uac2_descriptors(const struct usb_desc_header **descriptors, enum usbd_speed speed) { @@ -392,4 +485,131 @@ ZTEST(uac2_desc, test_fs_hs_iface_and_ep_descriptors_not_shared) } } +ZTEST(uac2_desc, test_hs_only_headset) +{ + const struct usb_desc_header **ptr = hs_headphones_descriptor_set; + + const struct usb_association_descriptor *iad; + const struct usb_if_descriptor *iface; + const struct usb_ep_descriptor *ep; + + /* Allowed only at High-Speed so Full-Speed should be NULL */ + zassert_equal(*fs_headphones_descriptor_set, NULL); + + /* Headset has 3 interfaces: 1 AudioControl and 2 AudioStreaming */ + iad = (const struct usb_association_descriptor *)*ptr; + zassert_not_null(iad); + zassert_equal(iad->bLength, sizeof(struct usb_association_descriptor)); + zassert_equal(iad->bDescriptorType, USB_DESC_INTERFACE_ASSOC); + zassert_equal(iad->bFirstInterface, FIRST_INTERFACE_NUMBER); + zassert_equal(iad->bInterfaceCount, 2); + zassert_equal(iad->bFunctionClass, AUDIO_FUNCTION); + zassert_equal(iad->bFunctionSubClass, FUNCTION_SUBCLASS_UNDEFINED); + zassert_equal(iad->bFunctionProtocol, AF_VERSION_02_00); + zassert_equal(iad->iFunction, 0); + ptr++; + + /* AudioControl interface goes first */ + iface = (const struct usb_if_descriptor *)*ptr; + zassert_not_null(iface); + zassert_equal(iface->bLength, sizeof(struct usb_if_descriptor)); + zassert_equal(iface->bDescriptorType, USB_DESC_INTERFACE); + zassert_equal(iface->bInterfaceNumber, FIRST_INTERFACE_NUMBER); + zassert_equal(iface->bAlternateSetting, 0); + zassert_equal(iface->bNumEndpoints, 0); + zassert_equal(iface->bInterfaceClass, AUDIO); + zassert_equal(iface->bInterfaceSubClass, AUDIOCONTROL); + zassert_equal(iface->bInterfaceProtocol, IP_VERSION_02_00); + zassert_equal(iface->iInterface, 0); + ptr++; + + /* AudioControl class-specific descriptors */ + zassert_mem_equal(reference_hs_ac_interface_descriptor, *ptr, + ARRAY_SIZE(reference_hs_ac_interface_descriptor)); + ptr++; + zassert_mem_equal(reference_hs_ac_clock_source_descriptor, *ptr, + ARRAY_SIZE(reference_hs_ac_clock_source_descriptor)); + ptr++; + zassert_mem_equal(reference_hs_ac_input_terminal_descriptor, *ptr, + ARRAY_SIZE(reference_hs_ac_input_terminal_descriptor)); + ptr++; + zassert_mem_equal(reference_hs_ac_output_terminal_descriptor, *ptr, + ARRAY_SIZE(reference_hs_ac_output_terminal_descriptor)); + ptr++; + + /* AudioStreaming OUT interface Alt 0 without endpoints */ + iface = (const struct usb_if_descriptor *)*ptr; + zassert_not_null(iface); + zassert_equal(iface->bLength, sizeof(struct usb_if_descriptor)); + zassert_equal(iface->bDescriptorType, USB_DESC_INTERFACE); + zassert_equal(iface->bInterfaceNumber, FIRST_INTERFACE_NUMBER + 1); + zassert_equal(iface->bAlternateSetting, 0); + zassert_equal(iface->bNumEndpoints, 0); + zassert_equal(iface->bInterfaceClass, AUDIO); + zassert_equal(iface->bInterfaceSubClass, AUDIOSTREAMING); + zassert_equal(iface->bInterfaceProtocol, IP_VERSION_02_00); + zassert_equal(iface->iInterface, 0); + ptr++; + + /* AudioStreaming OUT interface Alt 1 with endpoints */ + iface = (const struct usb_if_descriptor *)*ptr; + zassert_not_null(iface); + zassert_equal(iface->bLength, sizeof(struct usb_if_descriptor)); + zassert_equal(iface->bDescriptorType, USB_DESC_INTERFACE); + zassert_equal(iface->bInterfaceNumber, FIRST_INTERFACE_NUMBER + 1); + zassert_equal(iface->bAlternateSetting, 1); + zassert_equal(iface->bNumEndpoints, 2); + zassert_equal(iface->bInterfaceClass, AUDIO); + zassert_equal(iface->bInterfaceSubClass, AUDIOSTREAMING); + zassert_equal(iface->bInterfaceProtocol, IP_VERSION_02_00); + zassert_equal(iface->iInterface, 0); + ptr++; + + /* AudioStreaming OUT class-specific descriptors */ + zassert_mem_equal(reference_hs_as_cs_general_descriptor, *ptr, + ARRAY_SIZE(reference_hs_as_cs_general_descriptor)); + ptr++; + zassert_mem_equal(reference_hs_as_cs_format_descriptor, *ptr, + ARRAY_SIZE(reference_hs_as_cs_format_descriptor)); + ptr++; + + /* Isochronous OUT endpoint descriptor */ + ep = (const struct usb_ep_descriptor *)*ptr; + zassert_not_null(ep); + zassert_equal(ep->bLength, sizeof(struct usb_ep_descriptor)); + zassert_equal(ep->bDescriptorType, USB_DESC_ENDPOINT); + zassert_equal(ep->bEndpointAddress, FIRST_OUT_EP_ADDR); + zassert_equal(ptr - hs_headphones_descriptor_set, + UAC2_DESCRIPTOR_AS_DATA_EP_INDEX(DT_NODELABEL(hs_as_iso_out))); + zassert_equal(ep->Attributes.transfer, USB_EP_TYPE_ISO); + zassert_equal(ep->Attributes.synch, 1 /* Asynchronous */); + zassert_equal(ep->Attributes.usage, 0 /* Data Endpoint */); + zassert_equal(sys_le16_to_cpu(ep->wMaxPacketSize), 150); + zassert_equal(ep->bInterval, 1); + ptr++; + + /* AudioStreaming OUT endpoint descriptor */ + zassert_mem_equal(reference_as_ep_descriptor, *ptr, + ARRAY_SIZE(reference_as_ep_descriptor)); + ptr++; + + /* Isochronous IN explicit feedback endpoint descriptor */ + ep = (const struct usb_ep_descriptor *)*ptr; + zassert_not_null(ep); + zassert_equal(ep->bLength, sizeof(struct usb_ep_descriptor)); + zassert_equal(ep->bDescriptorType, USB_DESC_ENDPOINT); + zassert_equal(ep->bEndpointAddress, FIRST_IN_EP_ADDR); + zassert_equal(ptr - hs_headphones_descriptor_set, + UAC2_DESCRIPTOR_AS_FEEDBACK_EP_INDEX(DT_NODELABEL(hs_as_iso_out))); + zassert_equal(ep->Attributes.transfer, USB_EP_TYPE_ISO); + zassert_equal(ep->Attributes.synch, 0 /* No Synchronization */); + zassert_equal(ep->Attributes.usage, 1 /* Explicit Feedback-Endpoint */); + zassert_equal(sys_le16_to_cpu(ep->wMaxPacketSize), 4); + zassert_equal(ep->bInterval, 1); + ptr++; + + /* Confirm there is no trailing data */ + zassert_equal(*ptr, NULL); +} + ZTEST_SUITE(uac2_desc, NULL, NULL, NULL, NULL, NULL);