tests: usb: uac2: test High-Speed only instance

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ń <tomasz.mon@nordicsemi.no>
This commit is contained in:
Tomasz Moń 2024-09-04 13:38:09 +02:00 committed by Fabio Baltieri
commit 7282e4ee3d
2 changed files with 258 additions and 0 deletions

View file

@ -74,3 +74,41 @@
};
};
};
/ {
hs_uac2_headphones: hs_usb_audio2 {
compatible = "zephyr,uac2";
status = "okay";
high-speed;
audio-function = <AUDIO_FUNCTION_OTHER>;
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 = <USB_TERMINAL_STREAMING>;
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 = <OUTPUT_TERMINAL_HEADPHONES>;
};
hs_as_iso_out: hs_out_interface {
compatible = "zephyr,uac2-audio-streaming";
linked-terminal = <&hs_out_terminal>;
subslot-size = <3>;
bit-resolution = <24>;
};
};
};

View file

@ -10,6 +10,7 @@
#include <zephyr/usb/usbd.h>
#include <usbd_uac2_macros.h>
/* 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);