| /* |
| * Copyright (c) 2023 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/ztest.h> |
| #include <zephyr/sys/byteorder.h> |
| |
| #include <usbd_uac2_macros.h> |
| |
| static const uint8_t reference_ac_descriptors[] = { |
| /* 4.7.2 Class-Specific AC Interface Descriptor */ |
| 0x09, /* bLength = 9 */ |
| 0x24, /* bDescriptorType = CS_INTERFACE */ |
| 0x01, /* bDescriptorSubtype = HEADER */ |
| 0x00, 0x02, /* bcdADC = 02.00 */ |
| 0x04, /* bCategory = HEADSET */ |
| 0x4b, 0x00, /* wTotalLength = 0x4b = 75 */ |
| 0x00, /* bmControls = Latency Control not present */ |
| /* 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) */ |
| /* 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) */ |
| /* 4.7.2.5 Output Terminal Descriptor */ |
| 0x0c, /* bLength = 12 */ |
| 0x24, /* bDescriptorType = CS_INTERFACE */ |
| 0x03, /* bDescriptorSubtype = OUTPUT_TERMINAL */ |
| 0x03, /* bTerminalID = 3 */ |
| 0x02, 0x04, /* wTerminalType = 0x0402 (Headset) */ |
| 0x04, /* bAssocTerminal = 4 (headset input) */ |
| 0x02, /* bSourceID = 2 (streaming input) */ |
| 0x01, /* bCSourceID = 1 (main clock) */ |
| 0x00, 0x00, /* bmControls = none present */ |
| 0x00, /* iTerminal = 0 (no string descriptor) */ |
| /* 4.7.2.4 Input Terminal Descriptor */ |
| 0x11, /* bLength = 17 */ |
| 0x24, /* bDescriptorType = CS_INTERFACE */ |
| 0x02, /* bDescriptorSubtype = INPUT_TERMINAL */ |
| 0x04, /* bTerminalID = 4 */ |
| 0x02, 0x04, /* wTerminalType = 0x0402 (Headset) */ |
| 0x03, /* bAssocTerminal = 3 (headset output) */ |
| 0x01, /* bCSourceID = 1 (main clock) */ |
| 0x01, /* bNrChannels = 1 */ |
| 0x01, 0x00, 0x00, 0x00, /* bmChannelConfig = Front Left */ |
| 0x00, /* iChannelNames = 0 (all pre-defined) */ |
| 0x00, 0x00, /* bmControls = none present */ |
| 0x00, /* iTerminal = 0 (no string descriptor) */ |
| /* 4.7.2.5 Output Terminal Descriptor */ |
| 0x0c, /* bLength = 12 */ |
| 0x24, /* bDescriptorType = CS_INTERFACE */ |
| 0x03, /* bDescriptorSubtype = OUTPUT_TERMINAL */ |
| 0x05, /* bTerminalID = 5 */ |
| 0x01, 0x01, /* wTerminalType = 0x0101 (USB streaming) */ |
| 0x00, /* bAssocTerminal = 0 (not associated) */ |
| 0x04, /* bSourceID = 4 (headset input) */ |
| 0x01, /* bCSourceID = 1 (main clock) */ |
| 0x00, 0x00, /* bmControls = none present */ |
| 0x00, /* iTerminal = 0 (no string descriptor) */ |
| }; |
| |
| /* USB IN = Audio device streaming output */ |
| static const uint8_t reference_as_in_descriptors[] = { |
| /* 4.9.2 Class-Specific AS Interface Descriptor */ |
| 0x10, /* bLength = 16 */ |
| 0x24, /* bDescriptorType = CS_INTERFACE */ |
| 0x01, /* bDescriptorSubtype = AS_GENERAL */ |
| 0x05, /* bTerminalLink = 5 (USB streaming output) */ |
| 0x00, /* bmControls = non present */ |
| 0x01, /* bFormatType = 1 */ |
| 0x01, 0x00, 0x00, 0x00, /* bmFormats = PCM */ |
| 0x01, /* bNrChannels = 1 */ |
| 0x01, 0x00, 0x00, 0x00, /* bmChannelConfig = Front Left */ |
| 0x00, /* iChannelNames = 0 (all pre-defined) */ |
| /* 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 */ |
| 0x02, /* bSubslotSize = 2 */ |
| 0x10, /* bBitResolution = 16 */ |
| }; |
| |
| /* USB OUT = Audio device streaming input */ |
| static const uint8_t reference_as_out_descriptors[] = { |
| /* 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) */ |
| /* 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 */ |
| 0x02, /* bSubslotSize = 2 */ |
| 0x10, /* bBitResolution = 16 */ |
| }; |
| |
| static const uint8_t reference_as_ep_descriptor[] = { |
| /* 4.10.1.2 Class-Specific AS Isochronous Audio Data Endpoint Descriptor |
| */ |
| 0x08, /* bLength = 8 */ |
| 0x25, /* bDescriptorType = CS_ENDPOINT */ |
| 0x01, /* bDescriptorSubtype = EP_GENERAL */ |
| 0x00, /* bmAttributes = MaxPacketsOnly not set */ |
| 0x00, /* bmControls = non present */ |
| 0x00, /* bLockDelayUnits = 0 (Undefined) */ |
| 0x00, 0x00, /* wLockDelay = 0 */ |
| }; |
| |
| DT_FOREACH_CHILD(DT_NODELABEL(uac2_headset), VALIDATE_NODE) |
| |
| static const uint8_t generated_ac_descriptors[] = { |
| AC_INTERFACE_HEADER_DESCRIPTOR(DT_NODELABEL(uac2_headset)) |
| ENTITY_HEADERS(DT_NODELABEL(uac2_headset)) |
| }; |
| |
| static const uint8_t generated_as_in_descriptors[] = { |
| AUDIO_STREAMING_INTERFACE_DESCRIPTORS(DT_NODELABEL(as_iso_in)) |
| }; |
| |
| static const uint8_t generated_as_out_descriptors[] = { |
| AUDIO_STREAMING_INTERFACE_DESCRIPTORS(DT_NODELABEL(as_iso_out)) |
| }; |
| |
| static const uint8_t generated_uac2_descriptors[] = { |
| UAC2_DESCRIPTORS(DT_NODELABEL(uac2_headset)) |
| }; |
| |
| ZTEST(uac2_desc, test_audiocontrol_descriptors) |
| { |
| zassert_mem_equal(reference_ac_descriptors, generated_ac_descriptors, |
| ARRAY_SIZE(reference_ac_descriptors)); |
| } |
| |
| ZTEST(uac2_desc, test_audiostreaming_descriptors) |
| { |
| zassert_mem_equal(reference_as_in_descriptors, |
| generated_as_in_descriptors, |
| ARRAY_SIZE(reference_as_in_descriptors)); |
| |
| zassert_mem_equal(reference_as_out_descriptors, |
| generated_as_out_descriptors, |
| ARRAY_SIZE(reference_as_out_descriptors)); |
| } |
| |
| ZTEST(uac2_desc, test_uac2_descriptors) |
| { |
| const uint8_t *ptr = generated_uac2_descriptors; |
| const struct usb_association_descriptor *iad; |
| const struct usb_if_descriptor *iface; |
| const struct usb_ep_descriptor *ep; |
| |
| /* Headset has 3 interfaces: 1 AudioControl and 2 AudioStreaming */ |
| iad = (const struct usb_association_descriptor *)ptr; |
| 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, 3); |
| 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 += sizeof(struct usb_association_descriptor); |
| |
| /* AudioControl interface goes first */ |
| iface = (const struct usb_if_descriptor *)ptr; |
| 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 += sizeof(struct usb_if_descriptor); |
| |
| /* AudioControl class-specific descriptors */ |
| zassert_mem_equal(reference_ac_descriptors, ptr, |
| ARRAY_SIZE(reference_ac_descriptors)); |
| ptr += ARRAY_SIZE(reference_ac_descriptors); |
| |
| /* AudioStreaming OUT interface Alt 0 without endpoints */ |
| iface = (const struct usb_if_descriptor *)ptr; |
| 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 += sizeof(struct usb_if_descriptor); |
| |
| /* AudioStreaming OUT interface Alt 1 with endpoint */ |
| iface = (const struct usb_if_descriptor *)ptr; |
| 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, 1); |
| zassert_equal(iface->bInterfaceClass, AUDIO); |
| zassert_equal(iface->bInterfaceSubClass, AUDIOSTREAMING); |
| zassert_equal(iface->bInterfaceProtocol, IP_VERSION_02_00); |
| zassert_equal(iface->iInterface, 0); |
| ptr += sizeof(struct usb_if_descriptor); |
| |
| /* AudioStreaming OUT class-specific descriptors */ |
| zassert_mem_equal(reference_as_out_descriptors, ptr, |
| ARRAY_SIZE(reference_as_out_descriptors)); |
| ptr += ARRAY_SIZE(reference_as_out_descriptors); |
| |
| /* Isochronous OUT endpoint descriptor */ |
| ep = (const struct usb_ep_descriptor *)ptr; |
| 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(&ep->bEndpointAddress - generated_uac2_descriptors, |
| UAC2_DESCRIPTOR_AS_DATA_EP_OFFSET(DT_NODELABEL(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), 196); |
| zassert_equal(ep->bInterval, 1); |
| ptr += sizeof(struct usb_ep_descriptor); |
| |
| /* AudioStreaming OUT endpoint descriptor */ |
| zassert_mem_equal(reference_as_ep_descriptor, ptr, |
| ARRAY_SIZE(reference_as_ep_descriptor)); |
| ptr += ARRAY_SIZE(reference_as_ep_descriptor); |
| |
| /* AudioStreaming IN interface Alt 0 without endpoints */ |
| iface = (const struct usb_if_descriptor *)ptr; |
| zassert_equal(iface->bLength, sizeof(struct usb_if_descriptor)); |
| zassert_equal(iface->bDescriptorType, USB_DESC_INTERFACE); |
| zassert_equal(iface->bInterfaceNumber, FIRST_INTERFACE_NUMBER + 2); |
| 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 += sizeof(struct usb_if_descriptor); |
| |
| /* AudioStreaming IN interface Alt 1 with endpoint */ |
| iface = (const struct usb_if_descriptor *)ptr; |
| zassert_equal(iface->bLength, sizeof(struct usb_if_descriptor)); |
| zassert_equal(iface->bDescriptorType, USB_DESC_INTERFACE); |
| zassert_equal(iface->bInterfaceNumber, FIRST_INTERFACE_NUMBER + 2); |
| zassert_equal(iface->bAlternateSetting, 1); |
| zassert_equal(iface->bNumEndpoints, 1); |
| zassert_equal(iface->bInterfaceClass, AUDIO); |
| zassert_equal(iface->bInterfaceSubClass, AUDIOSTREAMING); |
| zassert_equal(iface->bInterfaceProtocol, IP_VERSION_02_00); |
| zassert_equal(iface->iInterface, 0); |
| ptr += sizeof(struct usb_if_descriptor); |
| |
| /* AudioStreaming IN class-specific descriptors */ |
| zassert_mem_equal(reference_as_in_descriptors, ptr, |
| ARRAY_SIZE(reference_as_in_descriptors)); |
| ptr += ARRAY_SIZE(reference_as_in_descriptors); |
| |
| /* Isochronous IN endpoint descriptor */ |
| ep = (const struct usb_ep_descriptor *)ptr; |
| 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(&ep->bEndpointAddress - generated_uac2_descriptors, |
| UAC2_DESCRIPTOR_AS_DATA_EP_OFFSET(DT_NODELABEL(as_iso_in))); |
| zassert_equal(ep->Attributes.transfer, USB_EP_TYPE_ISO); |
| zassert_equal(ep->Attributes.synch, 1 /* Asynchronous */); |
| zassert_equal(ep->Attributes.usage, 2 /* Implicit Feedback Data */); |
| zassert_equal(sys_le16_to_cpu(ep->wMaxPacketSize), 98); |
| zassert_equal(ep->bInterval, 1); |
| ptr += sizeof(struct usb_ep_descriptor); |
| |
| /* AudioStreaming IN endpoint descriptor */ |
| zassert_mem_equal(reference_as_ep_descriptor, ptr, |
| ARRAY_SIZE(reference_as_ep_descriptor)); |
| ptr += ARRAY_SIZE(reference_as_ep_descriptor); |
| |
| /* Confirm there are is no trailing data */ |
| zassert_equal(ptr - generated_uac2_descriptors, |
| ARRAY_SIZE(generated_uac2_descriptors)); |
| } |
| |
| ZTEST_SUITE(uac2_desc, NULL, NULL, NULL, NULL, NULL); |