diff --git a/dts/bindings/usb/uac2/zephyr,uac2-feature-unit.yaml b/dts/bindings/usb/uac2/zephyr,uac2-feature-unit.yaml new file mode 100644 index 000000000000..9a79a65630f6 --- /dev/null +++ b/dts/bindings/usb/uac2/zephyr,uac2-feature-unit.yaml @@ -0,0 +1,134 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: USB Audio Class 2 Feature Unit entity + +compatible: "zephyr,uac2-feature-unit" + +# string-array properties start with Primary channel 0 and follow with Logical +# channel(s). The "not-present" value is allowed to facilitate having controls +# that are not present on the Primary channel, but only present at the Logical +# channel(s). + +properties: + data-source: + type: phandle + description: Unit or Terminal to which this Feature Unit is connected + + mute-control: + type: string-array + description: Mute Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + volume-control: + type: string-array + description: Volume Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + bass-control: + type: string-array + description: Bass Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + mid-control: + type: string-array + description: Mid Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + treble-control: + type: string-array + description: Treble Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + graphic-equalizer-control: + type: string-array + description: Graphic Equalizer capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + automatic-gain-control: + type: string-array + description: Automatic Gain Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + delay-control: + type: string-array + description: Delay Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + bass-boost-control: + type: string-array + description: Bass Boost Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + loundness-control: + type: string-array + description: Loundness Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + input-gain-control: + type: string-array + description: Input Gain Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + input-gain-pad-control: + type: string-array + description: Input Gain Pad Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + phase-inverter-control: + type: string-array + description: Phase Inverter Control capabilities + enum: + - "read-only" + - "host-programmable" + - "not-present" + + underflow-control: + type: string-array + description: Underflow Control capabilities + enum: + - "read-only" + - "not-present" + + overflow-control: + type: string-array + description: Overflow Control capabilities + enum: + - "read-only" + - "not-present" diff --git a/subsys/usb/device_next/class/usbd_uac2_macros.h b/subsys/usb/device_next/class/usbd_uac2_macros.h index 2fbfea86d9d2..a31278d9cf8a 100644 --- a/subsys/usb/device_next/class/usbd_uac2_macros.h +++ b/subsys/usb/device_next/class/usbd_uac2_macros.h @@ -108,6 +108,9 @@ #define EXT_FORMAT_TYPE_II 130 #define EXT_FORMAT_TYPE_III 131 +/* Convert 0 to empty and everything else to itself */ +#define EMPTY_ON_ZERO(value) COND_CODE_0(value, (), (value)) + /* Automatically assign Entity IDs based on entities order in devicetree */ #define ENTITY_ID(e) UTIL_INC(DT_NODE_CHILD_IDX(e)) @@ -168,12 +171,26 @@ * control is present but read-only and 0b11 when control can be programmed by * host. Value 0b10 is not allowed by the specification. */ -#define CONTROL_BITS(entity, control_name, bitshift) \ +#define CONTROL_NOT_PRESENT 0x0 +#define CONTROL_READ_ONLY 0x1 +#define CONTROL_HOST_PROGRAMMABLE 0x3 + +#define CONTROL_TOKEN(entity, control_name) \ COND_CODE_1(DT_NODE_HAS_PROP(entity, control_name), \ - (COND_CODE_0(DT_ENUM_IDX(entity, control_name), \ - ((0x1 << bitshift)) /* read-only */, \ - ((0x3 << bitshift)) /* host-programmable */)), \ - ((0x0 << bitshift)) /* control not present */) + (DT_STRING_UPPER_TOKEN(entity, control_name)), \ + (NOT_PRESENT)) + +#define CONTROL_BITS(entity, control_name, bitshift) \ + (UTIL_CAT(CONTROL_, CONTROL_TOKEN(entity, control_name)) << bitshift) + +#define CONTROL_TOKEN_BY_IDX(entity, control_name, idx) \ + COND_CODE_1(DT_PROP_HAS_IDX(entity, control_name, idx), \ + (DT_STRING_UPPER_TOKEN_BY_IDX(entity, control_name, idx)), \ + (NOT_PRESENT)) + +#define CONTROL_BITS_BY_IDX(entity, control_name, idx, bitshift) \ + (UTIL_CAT(CONTROL_, CONTROL_TOKEN_BY_IDX(entity, control_name, idx)) \ + << bitshift) #define CLOCK_SOURCE_CONTROLS(entity) \ CONTROL_BITS(entity, frequency_control, 0) | \ @@ -194,6 +211,23 @@ CONTROL_BITS(entity, underflow_control, 6) | \ CONTROL_BITS(entity, overflow_control, 8) +#define FEATURE_UNIT_CHANNEL_CONTROLS(entity, ch) \ + CONTROL_BITS_BY_IDX(entity, mute_control, ch, 0) | \ + CONTROL_BITS_BY_IDX(entity, volume_control, ch, 2) | \ + CONTROL_BITS_BY_IDX(entity, bass_control, ch, 4) | \ + CONTROL_BITS_BY_IDX(entity, mid_control, ch, 6) | \ + CONTROL_BITS_BY_IDX(entity, treble_control, ch, 8) | \ + CONTROL_BITS_BY_IDX(entity, graphic_equalizer_control, ch, 10) | \ + CONTROL_BITS_BY_IDX(entity, automatic_gain_control, ch, 12) | \ + CONTROL_BITS_BY_IDX(entity, delay_control, ch, 14) | \ + CONTROL_BITS_BY_IDX(entity, bass_boost_control, ch, 16) | \ + CONTROL_BITS_BY_IDX(entity, loudness_control, ch, 18) | \ + CONTROL_BITS_BY_IDX(entity, input_gain_control, ch, 20) | \ + CONTROL_BITS_BY_IDX(entity, input_gain_pad_control, ch, 22) | \ + CONTROL_BITS_BY_IDX(entity, phase_inverter_control, ch, 24) | \ + CONTROL_BITS_BY_IDX(entity, underflow_control, ch, 26) | \ + CONTROL_BITS_BY_IDX(entity, overflow_control, ch, 28) + #define AUDIO_STREAMING_DATA_ENDPOINT_CONTROLS(node) \ CONTROL_BITS(node, pitch_control, 0) | \ CONTROL_BITS(node, data_overrun_control, 2) | \ @@ -233,10 +267,24 @@ #define SPATIAL_LOCATIONS_U32(entity) \ (FOR_EACH_IDX(ARRAY_BIT, (|), SPATIAL_LOCATIONS_ARRAY(entity))) -#define NUM_SPATIAL_LOCATIONS(entity) \ - (FOR_EACH(IDENTITY, (+), SPATIAL_LOCATIONS_ARRAY(entity))) +#define NUM_SPATIAL_LOCATIONS(entity) \ + NUM_VA_ARGS(LIST_DROP_EMPTY( \ + FOR_EACH(EMPTY_ON_ZERO, (,), SPATIAL_LOCATIONS_ARRAY(entity)) \ + )) #define SPATIAL_LOCATIONS(entity) U32_LE(SPATIAL_LOCATIONS_U32(entity)) +#define FEATURE_UNIT_NUM_CHANNELS(entity) \ + NUM_SPATIAL_LOCATIONS(DT_PHANDLE_BY_IDX(entity, data_source, 0)) + +#define FEATURE_UNIT_CONTROLS_BY_IDX(i, entity) \ + U32_LE(FEATURE_UNIT_CHANNEL_CONTROLS(entity, i)) + +#define FEATURE_UNIT_CONTROLS_ARRAYS(entity) \ + LISTIFY(UTIL_INC(FEATURE_UNIT_NUM_CHANNELS(entity)), \ + FEATURE_UNIT_CONTROLS_BY_IDX, (,), entity) + +#define FEATURE_UNIT_DESCRIPTOR_LENGTH(entity) \ + (6 + (FEATURE_UNIT_NUM_CHANNELS(entity) + 1) * 4) /* 4.7.2.1 Clock Source Descriptor */ #define CLOCK_SOURCE_DESCRIPTOR(entity) \ @@ -277,6 +325,16 @@ U16_LE(OUTPUT_TERMINAL_CONTROLS(entity)), /* bmControls */ \ 0x00, /* iTerminal */ +/* 4.7.2.8 Feature Unit Descriptor */ +#define FEATURE_UNIT_DESCRIPTOR(entity) \ + FEATURE_UNIT_DESCRIPTOR_LENGTH(entity), /* bLength */ \ + CS_INTERFACE, /* bDescriptorType */ \ + AC_DESCRIPTOR_FEATURE_UNIT, /* bDescriptorSubtype */\ + ENTITY_ID(entity), /* bUnitID */ \ + CONNECTED_ENTITY_ID(entity, data_source), /* bSourceID */ \ + FEATURE_UNIT_CONTROLS_ARRAYS(entity), /* bmaControls 0..ch */ \ + 0x00, /* iFeature */ + #define ENTITY_HEADER(entity) \ IF_ENABLED(DT_NODE_HAS_COMPAT(entity, zephyr_uac2_clock_source), ( \ CLOCK_SOURCE_DESCRIPTOR(entity) \ @@ -286,6 +344,9 @@ )) \ IF_ENABLED(DT_NODE_HAS_COMPAT(entity, zephyr_uac2_output_terminal), ( \ OUTPUT_TERMINAL_DESCRIPTOR(entity) \ + )) \ + IF_ENABLED(DT_NODE_HAS_COMPAT(entity, zephyr_uac2_feature_unit), ( \ + FEATURE_UNIT_DESCRIPTOR(entity) \ )) #define ENTITY_HEADER_ARRAYS(entity) \ @@ -318,6 +379,23 @@ (FORMAT_TYPE_I), (FORMAT_TYPE_IV)) #define AUDIO_STREAMING_FORMATS(node) U32_LE(0x00000001) +#define FEATURE_UNIT_CHANNEL_CLUSTER(node) \ + IF_ENABLED(DT_NODE_HAS_COMPAT(DT_PROP(node, data_source), \ + zephyr_uac2_input_terminal), ( \ + DT_PROP(node, data_source) \ + )) + +/* Track back Output Terminal data source to entity that has channel cluster */ +#define OUTPUT_TERMINAL_CHANNEL_CLUSTER(node) \ + IF_ENABLED(DT_NODE_HAS_COMPAT(DT_PROP(node, data_source), \ + zephyr_uac2_input_terminal), ( \ + DT_PROP(node, data_source) \ + )) \ + IF_ENABLED(DT_NODE_HAS_COMPAT(DT_PROP(node, data_source), \ + zephyr_uac2_feature_unit), ( \ + FEATURE_UNIT_CHANNEL_CLUSTER(DT_PROP(node, data_source))\ + )) + /* If AudioStreaming is linked to input terminal, obtain the channel cluster * configuration from the linked terminal. Otherwise (it has to be connected * to output terminal) obtain the channel cluster configuration from data source @@ -329,8 +407,8 @@ DT_PROP(node, linked_terminal) \ )) \ IF_ENABLED(DT_NODE_HAS_COMPAT(DT_PROP(node, linked_terminal), \ - zephyr_uac2_output_terminal), ( \ - DT_PROP(DT_PROP(node, linked_terminal), data_source) \ + zephyr_uac2_output_terminal), (OUTPUT_TERMINAL_CHANNEL_CLUSTER( \ + DT_PROP(node, linked_terminal)) \ )) #define AUDIO_STREAMING_NUM_SPATIAL_LOCATIONS(node) \ @@ -928,6 +1006,42 @@ DT_NODE_HAS_COMPAT(DT_PROP(entity, assoc_terminal), \ zephyr_uac2_input_terminal)) +#define VALIDATE_OUTPUT_TERMINAL_DATA_SOURCE(entity) \ + UTIL_OR(DT_NODE_HAS_COMPAT(DT_PROP(entity, data_source), \ + zephyr_uac2_input_terminal), \ + DT_NODE_HAS_COMPAT(DT_PROP(entity, data_source), \ + zephyr_uac2_feature_unit)) + +#define VALIDATE_FEATURE_UNIT_DATA_SOURCE(entity) \ + DT_NODE_HAS_COMPAT(DT_PROP(entity, data_source), \ + zephyr_uac2_input_terminal) + +#define BUILD_ASSERT_FEATURE_UNIT_CONTROL(fu, control) \ + BUILD_ASSERT(UTIL_OR(UTIL_NOT(DT_NODE_HAS_PROP(fu, control)), \ + DT_PROP_LEN(fu, control) <= 1 + FEATURE_UNIT_NUM_CHANNELS(fu)), \ + "Feature Unit " DT_NODE_PATH(fu) " has " \ + STRINGIFY(FEATURE_UNIT_NUM_CHANNELS(fu)) " logical channel(s) " \ + "but its property " #control " has " \ + STRINGIFY(DT_PROP_LEN(fu, control)) " values" \ + ); + +#define BUILD_ASSERT_FEATURE_UNIT_CONTROLS_LENGTH(entity) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, mute_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, volume_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, bass_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, mid_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, treble_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, graphic_equalizer_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, automatic_gain_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, delay_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, bass_boost_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, loudness_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, input_gain_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, input_gain_pad_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, phase_inverter_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, underflow_control) \ + BUILD_ASSERT_FEATURE_UNIT_CONTROL(entity, overflow_control) + #define NEEDS_SUBSLOT_SIZE_AND_BIT_RESOLUTION(node) UTIL_OR( \ UTIL_OR(IS_EQ(AUDIO_STREAMING_FORMAT_TYPE(node), FORMAT_TYPE_I), \ IS_EQ(AUDIO_STREAMING_FORMAT_TYPE(node), FORMAT_TYPE_III)), \ @@ -973,6 +1087,13 @@ IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_output_terminal), ( \ BUILD_ASSERT(VALIDATE_OUTPUT_TERMINAL_ASSOCIATION(node), \ "Terminals associations must be Input<->Output"); \ + BUILD_ASSERT(VALIDATE_OUTPUT_TERMINAL_DATA_SOURCE(node), \ + "Unsupported Output Terminal data source"); \ + )) \ + IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_feature_unit), ( \ + BUILD_ASSERT(VALIDATE_FEATURE_UNIT_DATA_SOURCE(node), \ + "Unsupported Feature Unit data source"); \ + BUILD_ASSERT_FEATURE_UNIT_CONTROLS_LENGTH(node); \ )) \ IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_audio_streaming), ( \ BUILD_ASSERT(VALIDATE_LINKED_TERMINAL(node), \ diff --git a/tests/subsys/usb/uac2/app.overlay b/tests/subsys/usb/uac2/app.overlay index 7b7585209a90..52db720a29ca 100644 --- a/tests/subsys/usb/uac2/app.overlay +++ b/tests/subsys/usb/uac2/app.overlay @@ -29,9 +29,19 @@ front-right; }; + out_feature_unit: out_feature_unit { + compatible = "zephyr,uac2-feature-unit"; + data-source = <&out_terminal>; + mute-control = "host-programmable"; + automatic-gain-control = + "host-programmable" /* Primary */, + "host-programmable" /* Channel 1 */, + "host-programmable" /* Channel 2 */; + }; + headphones_output: headphones { compatible = "zephyr,uac2-output-terminal"; - data-source = <&out_terminal>; + data-source = <&out_feature_unit>; clock-source = <&uac_aclk>; terminal-type = ; assoc-terminal = <&mic_input>; @@ -50,9 +60,18 @@ front-left; }; + in_feature_unit: in_feature_unit { + compatible = "zephyr,uac2-feature-unit"; + data-source = <&mic_input>; + mute-control = "host-programmable"; + automatic-gain-control = + "not-present" /* Primary */, + "host-programmable" /* Channel 1 */; + }; + in_terminal: in_terminal { compatible = "zephyr,uac2-output-terminal"; - data-source = <&mic_input>; + data-source = <&in_feature_unit>; clock-source = <&uac_aclk>; terminal-type = ; }; diff --git a/tests/subsys/usb/uac2/src/uac2_desc.c b/tests/subsys/usb/uac2/src/uac2_desc.c index 77474649c4cf..c0c76efabbaa 100644 --- a/tests/subsys/usb/uac2/src/uac2_desc.c +++ b/tests/subsys/usb/uac2/src/uac2_desc.c @@ -18,7 +18,7 @@ static const uint8_t reference_ac_interface_descriptor[] = { 0x01, /* bDescriptorSubtype = HEADER */ 0x00, 0x02, /* bcdADC = 02.00 */ 0x04, /* bCategory = HEADSET */ - 0x4b, 0x00, /* wTotalLength = 0x4b = 75 */ + 0x6b, 0x00, /* wTotalLength = 0x6b = 107 */ 0x00, /* bmControls = Latency Control not present */ }; @@ -50,15 +50,28 @@ static const uint8_t reference_ac_hp_input_terminal_descriptor[] = { 0x00, /* iTerminal = 0 (no string descriptor) */ }; +static const uint8_t reference_ac_hp_feature_unit_descriptor[] = { + /* 4.7.2.8 Feature Unit Descriptor */ + 0x12, /* bLength = 18 */ + 0x24, /* bDescriptorType = CS_INTERFACE */ + 0x06, /* bDescriptorSubtype = FEATURE_UNIT */ + 0x03, /* bUnitID = 3 */ + 0x02, /* bSourceID = 2 (streaming input) */ + 0x03, 0x30, 0x00, 0x00, /* bmaControls(0): Mute and Auto Gain */ + 0x00, 0x30, 0x00, 0x00, /* bmaControls(1): Auto Gain */ + 0x00, 0x30, 0x00, 0x00, /* bmaControls(2): Auto Gain */ + 0x00, /* iFeature = 0 (no string descriptor)*/ +}; + static const uint8_t reference_ac_hp_output_terminal_descriptor[] = { /* 4.7.2.5 Output Terminal Descriptor */ 0x0c, /* bLength = 12 */ 0x24, /* bDescriptorType = CS_INTERFACE */ 0x03, /* bDescriptorSubtype = OUTPUT_TERMINAL */ - 0x03, /* bTerminalID = 3 */ + 0x04, /* bTerminalID = 4 */ 0x02, 0x04, /* wTerminalType = 0x0402 (Headset) */ - 0x04, /* bAssocTerminal = 4 (headset input) */ - 0x02, /* bSourceID = 2 (streaming input) */ + 0x05, /* bAssocTerminal = 5 (headset input) */ + 0x03, /* bSourceID = 3 (headphones feature unit) */ 0x01, /* bCSourceID = 1 (main clock) */ 0x00, 0x00, /* bmControls = none present */ 0x00, /* iTerminal = 0 (no string descriptor) */ @@ -69,9 +82,9 @@ static const uint8_t reference_ac_mic_input_terminal_descriptor[] = { 0x11, /* bLength = 17 */ 0x24, /* bDescriptorType = CS_INTERFACE */ 0x02, /* bDescriptorSubtype = INPUT_TERMINAL */ - 0x04, /* bTerminalID = 4 */ + 0x05, /* bTerminalID = 5 */ 0x02, 0x04, /* wTerminalType = 0x0402 (Headset) */ - 0x03, /* bAssocTerminal = 3 (headset output) */ + 0x04, /* bAssocTerminal = 4 (headset output) */ 0x01, /* bCSourceID = 1 (main clock) */ 0x01, /* bNrChannels = 1 */ 0x01, 0x00, 0x00, 0x00, /* bmChannelConfig = Front Left */ @@ -80,15 +93,27 @@ static const uint8_t reference_ac_mic_input_terminal_descriptor[] = { 0x00, /* iTerminal = 0 (no string descriptor) */ }; +static const uint8_t reference_ac_mic_feature_unit_descriptor[] = { + /* 4.7.2.8 Feature Unit Descriptor */ + 0x0e, /* bLength = 14 */ + 0x24, /* bDescriptorType = CS_INTERFACE */ + 0x06, /* bDescriptorSubtype = FEATURE_UNIT */ + 0x06, /* bUnitID = 6 */ + 0x05, /* bSourceID = 5 (headset input) */ + 0x03, 0x00, 0x00, 0x00, /* bmaControls(0): Mute */ + 0x00, 0x30, 0x00, 0x00, /* bmaControls(1): Auto Gain */ + 0x00, /* iFeature = 0 (no string descriptor)*/ +}; + static const uint8_t reference_ac_mic_output_terminal_descriptor[] = { /* 4.7.2.5 Output Terminal Descriptor */ 0x0c, /* bLength = 12 */ 0x24, /* bDescriptorType = CS_INTERFACE */ 0x03, /* bDescriptorSubtype = OUTPUT_TERMINAL */ - 0x05, /* bTerminalID = 5 */ + 0x07, /* bTerminalID = 7 */ 0x01, 0x01, /* wTerminalType = 0x0101 (USB streaming) */ 0x00, /* bAssocTerminal = 0 (not associated) */ - 0x04, /* bSourceID = 4 (headset input) */ + 0x06, /* bSourceID = 6 (mic feature unit) */ 0x01, /* bCSourceID = 1 (main clock) */ 0x00, 0x00, /* bmControls = none present */ 0x00, /* iTerminal = 0 (no string descriptor) */ @@ -100,7 +125,7 @@ static const uint8_t reference_as_in_cs_general_descriptor[] = { 0x10, /* bLength = 16 */ 0x24, /* bDescriptorType = CS_INTERFACE */ 0x01, /* bDescriptorSubtype = AS_GENERAL */ - 0x05, /* bTerminalLink = 5 (USB streaming output) */ + 0x07, /* bTerminalLink = 7 (USB streaming output) */ 0x00, /* bmControls = non present */ 0x01, /* bFormatType = 1 */ 0x01, 0x00, 0x00, 0x00, /* bmFormats = PCM */ @@ -308,12 +333,18 @@ static void test_uac2_descriptors(const struct usb_desc_header **descriptors, zassert_mem_equal(reference_ac_hp_input_terminal_descriptor, *ptr, ARRAY_SIZE(reference_ac_hp_input_terminal_descriptor)); ptr++; + zassert_mem_equal(reference_ac_hp_feature_unit_descriptor, *ptr, + ARRAY_SIZE(reference_ac_hp_feature_unit_descriptor)); + ptr++; zassert_mem_equal(reference_ac_hp_output_terminal_descriptor, *ptr, ARRAY_SIZE(reference_ac_hp_output_terminal_descriptor)); ptr++; zassert_mem_equal(reference_ac_mic_input_terminal_descriptor, *ptr, ARRAY_SIZE(reference_ac_mic_input_terminal_descriptor)); ptr++; + zassert_mem_equal(reference_ac_mic_feature_unit_descriptor, *ptr, + ARRAY_SIZE(reference_ac_mic_feature_unit_descriptor)); + ptr++; zassert_mem_equal(reference_ac_mic_output_terminal_descriptor, *ptr, ARRAY_SIZE(reference_ac_mic_output_terminal_descriptor)); ptr++;