| /* |
| * Copyright (c) 2023 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /* The macros in this file are not public, applications should not use them. |
| * The macros are used to translate devicetree zephyr,uac2 compatible nodes |
| * into uint8_t array initializer. The output should be treated as a binary blob |
| * for the USB host to use (and parse). |
| */ |
| |
| #include <zephyr/sys/util.h> |
| #include <zephyr/usb/usb_ch9.h> |
| |
| #ifndef ZEPHYR_INCLUDE_USBD_UAC2_MACROS_H_ |
| #define ZEPHYR_INCLUDE_USBD_UAC2_MACROS_H_ |
| |
| #define ARRAY_BIT(idx, value) (value##ull << idx) |
| |
| #define U16_LE(value) ((value) & 0xFF), (((value) & 0xFF00) >> 8) |
| #define U32_LE(value) \ |
| ((value) & 0xFF), \ |
| (((value) & 0xFF00) >> 8), \ |
| (((value) & 0xFF0000) >> 16), \ |
| (((value) & 0xFF000000) >> 24) |
| |
| #define ARRAY_ELEMENT_LESS_THAN_NEXT(node, prop, idx) \ |
| COND_CODE_1(IS_EQ(idx, UTIL_DEC(DT_PROP_LEN(node, prop))), \ |
| (1 /* nothing to compare the last element against */), \ |
| ((DT_PROP_BY_IDX(node, prop, idx) < \ |
| DT_PROP_BY_IDX(node, prop, UTIL_INC(idx))))) |
| #define IS_ARRAY_SORTED(node, prop) \ |
| DT_FOREACH_PROP_ELEM_SEP(node, prop, ARRAY_ELEMENT_LESS_THAN_NEXT, (&&)) |
| |
| #define FIRST_INTERFACE_NUMBER 0x00 |
| #define FIRST_IN_EP_ADDR 0x81 |
| #define FIRST_OUT_EP_ADDR 0x01 |
| |
| /* A.1 Audio Function Class Code */ |
| #define AUDIO_FUNCTION AUDIO |
| |
| /* A.2 Audio Function Subclass Codes */ |
| #define FUNCTION_SUBCLASS_UNDEFINED 0x00 |
| |
| /* A.3 Audio Function Protocol Codes */ |
| #define FUNCTION_PROTOCOL_UNDEFINED 0x00 |
| #define AF_VERSION_02_00 IP_VERSION_02_00 |
| |
| /* A.4 Audio Interface Class Code */ |
| #define AUDIO 0x01 |
| |
| /* A.5 Audio Interface Subclass Codes */ |
| #define INTERFACE_SUBCLASS_UNDEFINED 0x00 |
| #define AUDIOCONTROL 0x01 |
| #define AUDIOSTREAMING 0x02 |
| #define MIDISTREAMING 0x03 |
| |
| /* A.6 Audio Interface Protocol Codes */ |
| #define INTERFACE_PROTOCOL_UNDEFINED 0x00 |
| #define IP_VERSION_02_00 0x20 |
| |
| /* A.8 Audio Class-Specific Descriptor Types */ |
| #define CS_UNDEFINED 0x20 |
| #define CS_DEVICE 0x21 |
| #define CS_CONFIGURATION 0x22 |
| #define CS_STRING 0x23 |
| #define CS_INTERFACE 0x24 |
| #define CS_ENDPOINT 0x25 |
| |
| /* A.9 Audio Class-Specific AC Interface Descriptor Subtypes */ |
| #define AC_DESCRIPTOR_UNDEFINED 0x00 |
| #define AC_DESCRIPTOR_HEADER 0x01 |
| #define AC_DESCRIPTOR_INPUT_TERMINAL 0x02 |
| #define AC_DESCRIPTOR_OUTPUT_TERMINAL 0x03 |
| #define AC_DESCRIPTOR_MIXER_UNIT 0x04 |
| #define AC_DESCRIPTOR_SELECTOR_UNIT 0x05 |
| #define AC_DESCRIPTOR_FEATURE_UNIT 0x06 |
| #define AC_DESCRIPTOR_EFFECT_UNIT 0x07 |
| #define AC_DESCRIPTOR_PROCESSING_UNIT 0x08 |
| #define AC_DESCRIPTOR_EXTENSION_UNIT 0x09 |
| #define AC_DESCRIPTOR_CLOCK_SOURCE 0x0A |
| #define AC_DESCRIPTOR_CLOCK_SELECTOR 0x0B |
| #define AC_DESCRIPTOR_CLOCK_MULTIPLIER 0x0C |
| #define AC_DESCRIPTOR_SAMPLE_RATE_CONVERTER 0x0D |
| |
| /* A.10 Audio Class-Specific AS Interface Descriptor Subtypes */ |
| #define AS_DESCRIPTOR_UNDEFINED 0x00 |
| #define AS_DESCRIPTOR_GENERAL 0x01 |
| #define AS_DESCRIPTOR_FORMAT_TYPE 0x02 |
| #define AS_DESCRIPTOR_ENCODER 0x03 |
| #define AS_DESCRIPTOR_DECODER 0x04 |
| |
| /* A.13 Audio Class-Specific Endpoint Descriptor Subtypes */ |
| #define DESCRIPTOR_UNDEFINED 0x00 |
| #define EP_GENERAL 0x01 |
| |
| /* Universal Serial Bus Device Class Definition for Audio Data Formats |
| * Release 2.0, May 31, 2006. A.1 Format Type Codes |
| * Values are in decimal to facilitate use with IS_EQ() macro. |
| */ |
| #define FORMAT_TYPE_UNDEFINED 0 |
| #define FORMAT_TYPE_I 1 |
| #define FORMAT_TYPE_II 2 |
| #define FORMAT_TYPE_III 3 |
| #define FORMAT_TYPE_IV 4 |
| #define EXT_FORMAT_TYPE_I 129 |
| #define EXT_FORMAT_TYPE_II 130 |
| #define EXT_FORMAT_TYPE_III 131 |
| |
| /* Automatically assign Entity IDs based on entities order in devicetree */ |
| #define ENTITY_ID(e) UTIL_INC(DT_NODE_CHILD_IDX(e)) |
| |
| /* Connected Entity ID or 0 if property is not defined. Rely on devicetree |
| * "required: true" to fail compilation if mandatory handle (e.g. clock source) |
| * is absent. |
| */ |
| #define CONNECTED_ENTITY_ID(entity, phandle) \ |
| COND_CODE_1(DT_NODE_HAS_PROP(entity, phandle), \ |
| (ENTITY_ID(DT_PHANDLE_BY_IDX(entity, phandle, 0))), (0)) |
| |
| #define ID_IF_TERMINAL_ASSOCIATES_WITH_TARGET(entity, target_id) \ |
| IF_ENABLED(UTIL_AND(IS_EQ( \ |
| CONNECTED_ENTITY_ID(entity, assoc_terminal), target_id), \ |
| UTIL_OR( \ |
| DT_NODE_HAS_COMPAT(entity, zephyr_uac2_input_terminal), \ |
| DT_NODE_HAS_COMPAT(entity, zephyr_uac2_output_terminal) \ |
| )), (ENTITY_ID(entity))) |
| |
| /* Find ID of terminal entity associated with given terminal entity. This macro |
| * evaluates to "+ 0" if there isn't any terminal entity associated. If there |
| * are terminal entities associated with given terminal, then the macro evalues |
| * to "IDs + 0" where IDs are the terminal entities ID separated by spaces. |
| * |
| * If there is exactly one ID then compiler will compute correct value. |
| * If there is more than one associated entity, then it will fail at build time |
| * (as it should) because the caller expects single integer. |
| */ |
| #define FIND_ASSOCIATED_TERMINAL(entity) \ |
| DT_FOREACH_CHILD_VARGS(DT_PARENT(entity), \ |
| ID_IF_TERMINAL_ASSOCIATES_WITH_TARGET, ENTITY_ID(entity)) + 0 |
| |
| /* If entity has assoc_terminal property, return the entity ID of associated |
| * terminal. Otherwise, search if any other terminal entity points to us and |
| * use its ID. If search yields no results then this evaluates to "+ 0" which |
| * matches the value USB Audio Class expects in bAssocTerminal if no association |
| * exists. |
| * |
| * This is a workaround for lack of cyclic dependencies support in devicetree. |
| */ |
| #define ASSOCIATED_TERMINAL_ID(entity) \ |
| COND_CODE_1(DT_NODE_HAS_PROP(entity, assoc_terminal), \ |
| (CONNECTED_ENTITY_ID(entity, assoc_terminal)), \ |
| (FIND_ASSOCIATED_TERMINAL(entity))) |
| |
| |
| #define CLOCK_SOURCE_ATTRIBUTES(entity) \ |
| (DT_ENUM_IDX(entity, clock_type)) | \ |
| (DT_PROP(entity, sof_synchronized) << 2) |
| |
| /* Control properties are optional enums in devicetree that can either be |
| * "read-only" or "host-programmable". If the property is missing, then it means |
| * that control is not present. Convert the control property into actual values |
| * used by USB Audio Class, i.e. 0b00 when control is not present, 0b01 when |
| * 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) \ |
| 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 */) |
| |
| #define CLOCK_SOURCE_CONTROLS(entity) \ |
| CONTROL_BITS(entity, frequency_control, 0) | \ |
| CONTROL_BITS(entity, validity_control, 2) |
| |
| #define INPUT_TERMINAL_CONTROLS(entity) \ |
| CONTROL_BITS(entity, copy_protect_control, 0) | \ |
| CONTROL_BITS(entity, connector_control, 2) | \ |
| CONTROL_BITS(entity, overload_control, 4) | \ |
| CONTROL_BITS(entity, cluster_control, 6) | \ |
| CONTROL_BITS(entity, underflow_control, 8) | \ |
| CONTROL_BITS(entity, overflow_control, 10) |
| |
| #define OUTPUT_TERMINAL_CONTROLS(entity) \ |
| CONTROL_BITS(entity, copy_protect_control, 0) | \ |
| CONTROL_BITS(entity, connector_control, 2) | \ |
| CONTROL_BITS(entity, overload_control, 4) | \ |
| CONTROL_BITS(entity, underflow_control, 6) | \ |
| CONTROL_BITS(entity, overflow_control, 8) |
| |
| #define AUDIO_STREAMING_DATA_ENDPOINT_CONTROLS(node) \ |
| CONTROL_BITS(node, pitch_control, 0) | \ |
| CONTROL_BITS(node, data_overrun_control, 2) | \ |
| CONTROL_BITS(node, data_underrun_control, 4) |
| |
| /* 4.1 Audio Channel Cluster Descriptor */ |
| #define SPATIAL_LOCATIONS_ARRAY(cluster) \ |
| DT_PROP(cluster, front_left), \ |
| DT_PROP(cluster, front_right), \ |
| DT_PROP(cluster, front_center), \ |
| DT_PROP(cluster, low_frequency_effects), \ |
| DT_PROP(cluster, back_left), \ |
| DT_PROP(cluster, back_right), \ |
| DT_PROP(cluster, front_left_of_center), \ |
| DT_PROP(cluster, front_right_of_center), \ |
| DT_PROP(cluster, back_center), \ |
| DT_PROP(cluster, side_left), \ |
| DT_PROP(cluster, side_right), \ |
| DT_PROP(cluster, top_center), \ |
| DT_PROP(cluster, top_front_left), \ |
| DT_PROP(cluster, top_front_center), \ |
| DT_PROP(cluster, top_front_right), \ |
| DT_PROP(cluster, top_back_left), \ |
| DT_PROP(cluster, top_back_center), \ |
| DT_PROP(cluster, top_back_right), \ |
| DT_PROP(cluster, top_front_left_of_center), \ |
| DT_PROP(cluster, top_front_right_of_center), \ |
| DT_PROP(cluster, left_low_frequency_effects), \ |
| DT_PROP(cluster, right_low_frequency_effects), \ |
| DT_PROP(cluster, top_side_left), \ |
| DT_PROP(cluster, top_side_right), \ |
| DT_PROP(cluster, bottom_center), \ |
| DT_PROP(cluster, back_left_of_center), \ |
| DT_PROP(cluster, back_right_of_center), \ |
| 0, 0, 0, 0, /* D27..D30: Reserved */ \ |
| DT_PROP(cluster, raw_data) |
| |
| #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 SPATIAL_LOCATIONS(entity) U32_LE(SPATIAL_LOCATIONS_U32(entity)) |
| |
| |
| /* 4.7.2.1 Clock Source Descriptor */ |
| #define CLOCK_SOURCE_DESCRIPTOR(entity) \ |
| 0x08, /* bLength */ \ |
| CS_INTERFACE, /* bDescriptorType */ \ |
| AC_DESCRIPTOR_CLOCK_SOURCE, /* bDescriptorSubtype */\ |
| ENTITY_ID(entity), /* bClockID */ \ |
| CLOCK_SOURCE_ATTRIBUTES(entity), /* bmAttributes */ \ |
| CLOCK_SOURCE_CONTROLS(entity), /* bmControls */ \ |
| CONNECTED_ENTITY_ID(entity, assoc_terminal), /* bAssocTerminal */ \ |
| 0x00, /* iClockSource */ |
| |
| /* 4.7.2.4 Input Terminal Descriptor */ |
| #define INPUT_TERMINAL_DESCRIPTOR(entity) \ |
| 0x11, /* bLength */ \ |
| CS_INTERFACE, /* bDescriptorType */ \ |
| AC_DESCRIPTOR_INPUT_TERMINAL, /* bDescriptorSubtype */\ |
| ENTITY_ID(entity), /* bTerminalID */ \ |
| U16_LE(DT_PROP(entity, terminal_type)), /* wTerminalType */ \ |
| ASSOCIATED_TERMINAL_ID(entity), /* bAssocTerminal */ \ |
| CONNECTED_ENTITY_ID(entity, clock_source), /* bCSourceID */ \ |
| NUM_SPATIAL_LOCATIONS(entity), /* bNrChannels */ \ |
| SPATIAL_LOCATIONS(entity), /* bmChannelConfig */ \ |
| 0x00, /* iChannelNames */ \ |
| U16_LE(INPUT_TERMINAL_CONTROLS(entity)), /* bmControls */ \ |
| 0x00, /* iTerminal */ |
| |
| /* 4.7.2.5 Output Terminal Descriptor */ |
| #define OUTPUT_TERMINAL_DESCRIPTOR(entity) \ |
| 0x0C, /* bLength */ \ |
| CS_INTERFACE, /* bDescriptorType */ \ |
| AC_DESCRIPTOR_OUTPUT_TERMINAL, /* bDescriptorSubtype */\ |
| ENTITY_ID(entity), /* bTerminalID */ \ |
| U16_LE(DT_PROP(entity, terminal_type)), /* wTerminalType */ \ |
| ASSOCIATED_TERMINAL_ID(entity), /* bAssocTerminal */ \ |
| CONNECTED_ENTITY_ID(entity, data_source), /* bSourceID */ \ |
| CONNECTED_ENTITY_ID(entity, clock_source), /* bCSourceID */ \ |
| U16_LE(OUTPUT_TERMINAL_CONTROLS(entity)), /* bmControls */ \ |
| 0x00, /* iTerminal */ |
| |
| #define ENTITY_HEADER(entity) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(entity, zephyr_uac2_clock_source), ( \ |
| CLOCK_SOURCE_DESCRIPTOR(entity) \ |
| )) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(entity, zephyr_uac2_input_terminal), ( \ |
| INPUT_TERMINAL_DESCRIPTOR(entity) \ |
| )) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(entity, zephyr_uac2_output_terminal), ( \ |
| OUTPUT_TERMINAL_DESCRIPTOR(entity) \ |
| )) |
| |
| #define ENTITY_HEADERS(node) DT_FOREACH_CHILD(node, ENTITY_HEADER) |
| #define ENTITY_HEADERS_LENGTH(node) sizeof((uint8_t []){ENTITY_HEADERS(node)}) |
| |
| #define AUDIO_STREAMING_CONTROLS(node) \ |
| CONTROL_BITS(entity, active_alternate_setting_control, 0) | \ |
| CONTROL_BITS(entity, valid_alternate_settings_control, 2) |
| |
| /* TODO: Support other format types. Currently the PCM format (0x00000001) is |
| * hardcoded and format type is either I or IV depending on whether the |
| * interface has isochronous endpoint or not. |
| */ |
| #define AUDIO_STREAMING_FORMAT_TYPE(node) \ |
| COND_CODE_0(DT_PROP(node, external_interface), \ |
| (FORMAT_TYPE_I), (FORMAT_TYPE_IV)) |
| #define AUDIO_STREAMING_FORMATS(node) U32_LE(0x00000001) |
| |
| /* 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 |
| * entity. |
| */ |
| #define AUDIO_STREAMING_CHANNEL_CLUSTER(node) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(DT_PROP(node, linked_terminal), \ |
| zephyr_uac2_input_terminal), ( \ |
| 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) \ |
| )) |
| |
| #define AUDIO_STREAMING_NUM_SPATIAL_LOCATIONS(node) \ |
| NUM_SPATIAL_LOCATIONS(AUDIO_STREAMING_CHANNEL_CLUSTER(node)) |
| #define AUDIO_STREAMING_SPATIAL_LOCATIONS(node) \ |
| SPATIAL_LOCATIONS(AUDIO_STREAMING_CHANNEL_CLUSTER(node)) |
| |
| /* 4.9.2 Class-Specific AS Interface Descriptor */ |
| #define AUDIO_STREAMING_GENERAL_DESCRIPTOR(node) \ |
| 0x10, /* bLength */ \ |
| CS_INTERFACE, /* bDescriptorType */ \ |
| AS_DESCRIPTOR_GENERAL, /* bDescriptorSubtype */\ |
| CONNECTED_ENTITY_ID(node, linked_terminal), /* bTerminalLink */ \ |
| AUDIO_STREAMING_CONTROLS(node), /* bmControls*/ \ |
| AUDIO_STREAMING_FORMAT_TYPE(node), /* bFormatType */ \ |
| AUDIO_STREAMING_FORMATS(node), /* bmFormats */ \ |
| AUDIO_STREAMING_NUM_SPATIAL_LOCATIONS(node), /* bNrChannels */ \ |
| AUDIO_STREAMING_SPATIAL_LOCATIONS(node), /* bmChannelConfig */ \ |
| 0x00, /* iChannelNames */ |
| |
| /* 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 |
| */ |
| #define AUDIO_STREAMING_FORMAT_I_TYPE_DESCRIPTOR(node) \ |
| 0x06, /* bLength */ \ |
| CS_INTERFACE, /* bDescriptorType */ \ |
| AS_DESCRIPTOR_FORMAT_TYPE, /* bDescriptorSubtype */\ |
| FORMAT_TYPE_I, /* bFormatType */ \ |
| DT_PROP(node, subslot_size), /* bSubslotSize */ \ |
| DT_PROP(node, bit_resolution), /* bBitResolution */ |
| |
| /* Universal Serial Bus Device Class Definition for Audio Data Formats |
| * Release 2.0, May 31, 2006. 2.3.4.1 Type IV Format Type Descriptor |
| */ |
| #define AUDIO_STREAMING_FORMAT_IV_TYPE_DESCRIPTOR(node) \ |
| 0x04, /* bLength */ \ |
| CS_INTERFACE, /* bDescriptorType */ \ |
| AS_DESCRIPTOR_FORMAT_TYPE, /* bDescriptorSubtype */\ |
| FORMAT_TYPE_IV, /* bFormatType */ |
| |
| /* 4.9.3 Class-Specific AS Format Type Descriptor */ |
| #define AUDIO_STREAMING_FORMAT_TYPE_DESCRIPTOR(node) \ |
| IF_ENABLED(IS_EQ(AUDIO_STREAMING_FORMAT_TYPE(node), FORMAT_TYPE_I), ( \ |
| AUDIO_STREAMING_FORMAT_I_TYPE_DESCRIPTOR(node))) \ |
| IF_ENABLED(IS_EQ(AUDIO_STREAMING_FORMAT_TYPE(node), FORMAT_TYPE_IV), ( \ |
| AUDIO_STREAMING_FORMAT_IV_TYPE_DESCRIPTOR(node))) |
| |
| #define AUDIO_STREAMING_INTERFACE_DESCRIPTORS(node) \ |
| AUDIO_STREAMING_GENERAL_DESCRIPTOR(node) \ |
| AUDIO_STREAMING_FORMAT_TYPE_DESCRIPTOR(node) |
| |
| /* 4.7.2 Class-Specific AC Interface Descriptor */ |
| #define AC_INTERFACE_HEADER_DESCRIPTOR(node) \ |
| 0x09, /* bLength */ \ |
| CS_INTERFACE, /* bDescriptorType */ \ |
| AC_DESCRIPTOR_HEADER, /* bDescriptorSubtype */\ |
| U16_LE(0x0200), /* bcdADC */ \ |
| DT_PROP(node, audio_function), /* bCategory */ \ |
| U16_LE(9 + ENTITY_HEADERS_LENGTH(node)), /* wTotalLength */ \ |
| 0x00, /* bmControls */ \ |
| |
| #define IS_AUDIOSTREAMING_INTERFACE(node) \ |
| DT_NODE_HAS_COMPAT(node, zephyr_uac2_audio_streaming) |
| |
| #define UAC2_NUM_INTERFACES(node) \ |
| 1 /* AudioControl interface */ + \ |
| DT_FOREACH_CHILD_SEP(node, IS_AUDIOSTREAMING_INTERFACE, (+)) |
| |
| /* 4.6 Interface Association Descriptor */ |
| #define UAC2_INTERFACE_ASSOCIATION_DESCRIPTOR(node) \ |
| 0x08, /* bLength */ \ |
| USB_DESC_INTERFACE_ASSOC, /* bDescriptorType */ \ |
| FIRST_INTERFACE_NUMBER, /* bFirstInterface */ \ |
| UAC2_NUM_INTERFACES(node), /* bInterfaceCount */ \ |
| AUDIO_FUNCTION, /* bFunctionClass */ \ |
| FUNCTION_SUBCLASS_UNDEFINED, /* bFunctionSubclass */ \ |
| AF_VERSION_02_00, /* bFunctionProtocol */ \ |
| 0x00, /* iFunction */ |
| |
| /* 4.7.1 Standard AC Interface Descriptor */ |
| #define AC_INTERFACE_DESCRIPTOR(node) \ |
| 0x09, /* bLength */ \ |
| USB_DESC_INTERFACE, /* bDescriptorType */ \ |
| FIRST_INTERFACE_NUMBER, /* bInterfaceNumber */ \ |
| 0x00, /* bAlternateSetting */ \ |
| DT_PROP(node, interrupt_endpoint), /* bNumEndpoints */ \ |
| AUDIO, /* bInterfaceClass */ \ |
| AUDIOCONTROL, /* bInterfaceSubClass */\ |
| IP_VERSION_02_00, /* bInterfaceProtocol */\ |
| 0x00, /* iInterface */ |
| |
| /* 4.8.2.1 Standard AC Interrupt Endpoint Descriptor */ |
| #define AC_ENDPOINT_DESCRIPTOR(node) \ |
| 0x07, /* bLength */ \ |
| USB_DESC_ENDPOINT, /* bDescriptorType */ \ |
| FIRST_IN_EP_ADDR, /* bEndpointAddress */ \ |
| USB_EP_TYPE_INTERRUPT, /* bmAttributes */ \ |
| 0x06, /* wMaxPacketSize */ \ |
| 0x01, /* bInterval */ \ |
| |
| #define FIND_AUDIOSTREAMING(node, fn, ...) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_audio_streaming), ( \ |
| fn(node, __VA_ARGS__))) |
| |
| #define FOR_EACH_AUDIOSTREAMING_INTERFACE(node, fn, ...) \ |
| DT_FOREACH_CHILD_VARGS(node, FIND_AUDIOSTREAMING, fn, __VA_ARGS__) |
| |
| #define COUNT_AS_INTERFACES_BEFORE_IDX(node, idx) \ |
| + 1 * (DT_NODE_CHILD_IDX(node) < idx) |
| |
| #define AS_INTERFACE_NUMBER(node) \ |
| FIRST_INTERFACE_NUMBER + 1 /* AudioControl interface */ + \ |
| FOR_EACH_AUDIOSTREAMING_INTERFACE(DT_PARENT(node), \ |
| COUNT_AS_INTERFACES_BEFORE_IDX, DT_NODE_CHILD_IDX(node)) |
| |
| #define AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node) \ |
| UTIL_NOT(DT_PROP(node, external_interface)) |
| |
| #define AS_IS_USB_ISO_IN(node) \ |
| UTIL_AND(AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node), \ |
| DT_NODE_HAS_COMPAT(DT_PROP(node, linked_terminal), \ |
| zephyr_uac2_output_terminal)) |
| |
| #define AS_IS_USB_ISO_OUT(node) \ |
| UTIL_AND(AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node), \ |
| DT_NODE_HAS_COMPAT(DT_PROP(node, linked_terminal), \ |
| zephyr_uac2_input_terminal)) |
| |
| #define CLK_IS_SOF_SYNCHRONIZED(entity) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(entity, zephyr_uac2_clock_source), ( \ |
| DT_PROP(entity, sof_synchronized) \ |
| )) |
| |
| /* Sampling frequencies are sorted (asserted at compile time), so just grab |
| * last sampling frequency. |
| */ |
| #define CLK_MAX_FREQUENCY(entity) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(entity, zephyr_uac2_clock_source), ( \ |
| DT_PROP_BY_IDX(entity, sampling_frequencies, \ |
| UTIL_DEC(DT_PROP_LEN(entity, sampling_frequencies))) \ |
| )) |
| |
| #define AS_CLK_SOURCE(node) \ |
| DT_PROP(DT_PROP(node, linked_terminal), clock_source) |
| |
| #define AS_CLK_MAX_FREQUENCY(node) \ |
| CLK_MAX_FREQUENCY(AS_CLK_SOURCE(node)) |
| |
| #define AS_IS_SOF_SYNCHRONIZED(node) \ |
| CLK_IS_SOF_SYNCHRONIZED(AS_CLK_SOURCE(node)) |
| |
| #define AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node) \ |
| UTIL_AND(UTIL_AND(AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node), \ |
| UTIL_NOT(DT_PROP(node, implicit_feedback))), \ |
| UTIL_AND(UTIL_NOT(AS_IS_SOF_SYNCHRONIZED(node)), \ |
| AS_IS_USB_ISO_OUT(node))) |
| |
| #define AS_INTERFACE_NUM_ENDPOINTS(node) \ |
| (AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node) + \ |
| AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node)) |
| |
| /* 4.9.1 Standard AS Interface Descriptor */ |
| #define AS_INTERFACE_DESCRIPTOR(node, alternate, numendpoints) \ |
| 0x09, /* bLength */ \ |
| USB_DESC_INTERFACE, /* bDescriptorType */ \ |
| AS_INTERFACE_NUMBER(node), /* bInterfaceNumber */ \ |
| alternate, /* bAlternateSetting */ \ |
| numendpoints, /* bNumEndpoints */ \ |
| AUDIO, /* bInterfaceClass */ \ |
| AUDIOSTREAMING, /* bInterfaceSubClass */\ |
| IP_VERSION_02_00, /* bInterfaceProtocol */\ |
| 0x00, /* iInterface */ |
| |
| #define COUNT_AS_OUT_ENDPOINTS_BEFORE_IDX(node, idx) \ |
| + AS_IS_USB_ISO_OUT(node) * (DT_NODE_CHILD_IDX(node) < idx) |
| |
| #define COUNT_AS_IN_ENDPOINTS_BEFORE_IDX(node, idx) \ |
| + (AS_IS_USB_ISO_IN(node) + AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node)) \ |
| * (DT_NODE_CHILD_IDX(node) < idx) |
| |
| /* FIXME: Ensure that the explicit feedback endpoints assignments match |
| * numbering requirements from Universal Serial Bus Specification Revision 2.0 |
| * 9.6.6 Endpoint. This FIXME is not limited to the macros here but also to |
| * actual USB stack endpoint fixup (so the fixup does not break the numbering). |
| * |
| * This FIXME does not affect nRF52 and nRF53 when implicit feedback is used |
| * because the endpoints after fixup will be 0x08 and 0x88 because there are |
| * only two isochronous endpoints on these devices (one IN, one OUT). |
| */ |
| #define AS_NEXT_OUT_EP_ADDR(node) \ |
| FIRST_OUT_EP_ADDR + \ |
| FOR_EACH_AUDIOSTREAMING_INTERFACE(DT_PARENT(node), \ |
| COUNT_AS_OUT_ENDPOINTS_BEFORE_IDX, DT_NODE_CHILD_IDX(node)) |
| |
| #define AS_NEXT_IN_EP_ADDR(node) \ |
| FIRST_IN_EP_ADDR + DT_PROP(DT_PARENT(node), interrupt_endpoint) + \ |
| FOR_EACH_AUDIOSTREAMING_INTERFACE(DT_PARENT(node), \ |
| COUNT_AS_IN_ENDPOINTS_BEFORE_IDX, DT_NODE_CHILD_IDX(node)) |
| |
| #define AS_DATA_EP_ADDR(node) \ |
| COND_CODE_1(AS_IS_USB_ISO_OUT(node), \ |
| (AS_NEXT_OUT_EP_ADDR(node)), \ |
| (AS_NEXT_IN_EP_ADDR(node))) |
| |
| #define AS_BYTES_PER_SAMPLE(node) \ |
| DT_PROP(node, subslot_size) |
| |
| /* Asynchronous endpoints needs space for 1 extra sample */ |
| #define AS_SAMPLES_PER_PACKET(node) \ |
| ((ROUND_UP(AS_CLK_MAX_FREQUENCY(node), 1000) / 1000) + \ |
| UTIL_NOT(AS_IS_SOF_SYNCHRONIZED(node))) |
| |
| #define AS_DATA_EP_SYNC_TYPE(node) \ |
| COND_CODE_1(AS_IS_SOF_SYNCHRONIZED(node), (0x3 << 2), (0x1 << 2)) |
| |
| #define AS_DATA_EP_USAGE_TYPE(node) \ |
| COND_CODE_1(UTIL_AND(DT_PROP(node, implicit_feedback), \ |
| UTIL_NOT(AS_IS_USB_ISO_OUT(node))), (0x2 << 4), (0x0 << 4)) |
| |
| #define AS_DATA_EP_ATTR(node) \ |
| USB_EP_TYPE_ISO | AS_DATA_EP_SYNC_TYPE(node) | \ |
| AS_DATA_EP_USAGE_TYPE(node) |
| |
| #define AS_DATA_EP_MAX_PACKET_SIZE(node) \ |
| AUDIO_STREAMING_NUM_SPATIAL_LOCATIONS(node) * \ |
| AS_BYTES_PER_SAMPLE(node) * AS_SAMPLES_PER_PACKET(node) |
| |
| /* 4.10.1.1 Standard AS Isochronous Audio Data Endpoint Descriptor */ |
| #define STANDARD_AS_ISOCHRONOUS_DATA_ENDPOINT_DESCRIPTOR(node) \ |
| 0x07, /* bLength */ \ |
| USB_DESC_ENDPOINT, /* bDescriptorType */ \ |
| AS_DATA_EP_ADDR(node), /* bEndpointAddress */ \ |
| AS_DATA_EP_ATTR(node), /* bmAttributes */ \ |
| U16_LE(AS_DATA_EP_MAX_PACKET_SIZE(node)), /* wMaxPacketSize */ \ |
| 0x01, /* bInterval */ |
| |
| #define LOCK_DELAY_UNITS(node) \ |
| COND_CODE_1(DT_NODE_HAS_PROP(node, lock_delay_units), \ |
| (1 + DT_ENUM_IDX(node, lock_delay_units)), \ |
| (0 /* Undefined */)) |
| |
| /* 4.10.1.2 Class-Specific AS Isochronous Audio Data Endpoint Descriptor */ |
| #define CLASS_SPECIFIC_AS_ISOCHRONOUS_DATA_ENDPOINT_DESCRIPTOR(node) \ |
| 0x08, /* bLength */ \ |
| CS_ENDPOINT, /* bDescriptorType */ \ |
| EP_GENERAL, /* bDescriptorSubtype */\ |
| 0x00, /* bmAttributes */ \ |
| AUDIO_STREAMING_DATA_ENDPOINT_CONTROLS(node), /* bmControls */ \ |
| LOCK_DELAY_UNITS(node), /* bLockDelayUnits */ \ |
| U16_LE(DT_PROP_OR(node, lock_delay, 0)), /* wLockDelay */ |
| |
| #define AS_ISOCHRONOUS_DATA_ENDPOINT_DESCRIPTORS(node) \ |
| STANDARD_AS_ISOCHRONOUS_DATA_ENDPOINT_DESCRIPTOR(node) \ |
| CLASS_SPECIFIC_AS_ISOCHRONOUS_DATA_ENDPOINT_DESCRIPTOR(node) |
| |
| #define AS_EXPLICIT_FEEDBACK_ENDPOINT_DESCRIPTOR(node) \ |
| 0x07, /* bLength */ \ |
| USB_DESC_ENDPOINT, /* bDescriptorType */ \ |
| AS_NEXT_IN_EP_ADDR(node), /* bEndpointAddress */ \ |
| 0x11, /* bmAttributes */ \ |
| U16_LE(0x03), /* FIXME: 4 on High-Speed*/ /* wMaxPacketSize */ \ |
| 0x01, /* TODO: adjust to P 5.12.4.2 Feedback */ /* bInterval */ |
| |
| #define AS_DESCRIPTORS(node) \ |
| AS_INTERFACE_DESCRIPTOR(node, 0, 0) \ |
| IF_ENABLED(AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node), ( \ |
| AS_INTERFACE_DESCRIPTOR(node, 1, \ |
| AS_INTERFACE_NUM_ENDPOINTS(node)))) \ |
| AUDIO_STREAMING_INTERFACE_DESCRIPTORS(node) \ |
| IF_ENABLED(AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node), ( \ |
| AS_ISOCHRONOUS_DATA_ENDPOINT_DESCRIPTORS(node) \ |
| IF_ENABLED(AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node), ( \ |
| AS_EXPLICIT_FEEDBACK_ENDPOINT_DESCRIPTOR(node))) \ |
| )) |
| |
| #define AS_DESCRIPTORS_IF_AUDIOSTREAMING(node) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_audio_streaming), ( \ |
| AS_DESCRIPTORS(node))) |
| |
| #define UAC2_AUDIO_CONTROL_DESCRIPTORS(node) \ |
| AC_INTERFACE_DESCRIPTOR(node) \ |
| AC_INTERFACE_HEADER_DESCRIPTOR(node) \ |
| ENTITY_HEADERS(node) \ |
| IF_ENABLED(DT_PROP(node, interrupt_endpoint), ( \ |
| AC_ENDPOINT_DESCRIPTOR(node))) |
| |
| #define UAC2_DESCRIPTORS(node) \ |
| UAC2_INTERFACE_ASSOCIATION_DESCRIPTOR(node) \ |
| UAC2_AUDIO_CONTROL_DESCRIPTORS(node) \ |
| DT_FOREACH_CHILD(node, AS_DESCRIPTORS_IF_AUDIOSTREAMING) |
| |
| |
| /* Helper macros to determine endpoint offset within complete UAC2 blob */ |
| #define COUNT_AS_DESCRIPTORS_BYTES_UP_TO_IDX(node, idx) \ |
| (sizeof((uint8_t []){AS_DESCRIPTORS_IF_AUDIOSTREAMING(node)}) * \ |
| (DT_NODE_CHILD_IDX(node) <= idx)) |
| |
| #define UAC2_DESCRIPTOR_AS_DESC_END_OFFSET(node) \ |
| (sizeof((uint8_t []){ \ |
| UAC2_INTERFACE_ASSOCIATION_DESCRIPTOR(DT_PARENT(node)) \ |
| UAC2_AUDIO_CONTROL_DESCRIPTORS(DT_PARENT(node)) \ |
| })) + DT_FOREACH_CHILD_SEP_VARGS(DT_PARENT(node), \ |
| COUNT_AS_DESCRIPTORS_BYTES_UP_TO_IDX, (+), \ |
| DT_NODE_CHILD_IDX(node)) |
| |
| #define AS_ISOCHRONOUS_DATA_ENDPOINT_DESCRIPTORS_SIZE(node) \ |
| (sizeof((uint8_t []) {AS_ISOCHRONOUS_DATA_ENDPOINT_DESCRIPTORS(node)})) |
| |
| #define AS_EXPLICIT_FEEDBACK_ENDPOINT_DESCRIPTOR_SIZE(node) \ |
| COND_CODE_1(AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node), \ |
| (sizeof((uint8_t []) { \ |
| AS_EXPLICIT_FEEDBACK_ENDPOINT_DESCRIPTOR(node) \ |
| })), (0)) |
| |
| /* Return offset inside UAC2_DESCRIPTORS(DT_PARENT(node)) poiting to data |
| * endpoint address belonging to given AudioStreaming interface node. |
| * |
| * It is programmer error to call this macro with node other than AudioStreaming |
| * or when AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node) is 0. |
| */ |
| #define UAC2_DESCRIPTOR_AS_DATA_EP_OFFSET(node) \ |
| UAC2_DESCRIPTOR_AS_DESC_END_OFFSET(node) \ |
| - AS_EXPLICIT_FEEDBACK_ENDPOINT_DESCRIPTOR_SIZE(node) \ |
| - AS_ISOCHRONOUS_DATA_ENDPOINT_DESCRIPTORS_SIZE(node) \ |
| + offsetof(struct usb_ep_descriptor, bEndpointAddress) |
| |
| /* Return offset inside UAC2_DESCRIPTORS(DT_PARENT(node)) poiting to feedback |
| * endpoint address belonging to given AudioStreaming interface node. |
| * |
| * It is programmer error to call this macro with node other than AudioStreaming |
| * or when AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node) is 0. |
| */ |
| #define UAC2_DESCRIPTOR_AS_FEEDBACK_EP_OFFSET(node) \ |
| UAC2_DESCRIPTOR_AS_DESC_END_OFFSET(node) \ |
| - AS_EXPLICIT_FEEDBACK_ENDPOINT_DESCRIPTOR_SIZE(node) \ |
| + offsetof(struct usb_ep_descriptor, bEndpointAddress) |
| |
| /* Helper macros to validate USB Audio Class 2 devicetree entries. |
| * Macros above do rely on the assumptions checked below. |
| */ |
| #define VALIDATE_INPUT_TERMINAL_ASSOCIATION(entity) \ |
| UTIL_OR(UTIL_NOT(DT_NODE_HAS_PROP(entity, assoc_terminal)), \ |
| DT_NODE_HAS_COMPAT(DT_PROP(entity, assoc_terminal), \ |
| zephyr_uac2_output_terminal)) |
| |
| #define VALIDATE_OUTPUT_TERMINAL_ASSOCIATION(entity) \ |
| UTIL_OR(UTIL_NOT(DT_NODE_HAS_PROP(entity, assoc_terminal)), \ |
| DT_NODE_HAS_COMPAT(DT_PROP(entity, assoc_terminal), \ |
| zephyr_uac2_input_terminal)) |
| |
| #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)), \ |
| UTIL_OR(IS_EQ(AUDIO_STREAMING_FORMAT_TYPE(node), EXT_FORMAT_TYPE_I), \ |
| IS_EQ(AUDIO_STREAMING_FORMAT_TYPE(node), EXT_FORMAT_TYPE_III))) |
| |
| #define VALIDATE_SUBSLOT_SIZE(node) \ |
| (DT_PROP(node, subslot_size) >= 1 && DT_PROP(node, subslot_size) <= 4) |
| |
| #define VALIDATE_BIT_RESOLUTION(node) \ |
| (DT_PROP(node, bit_resolution) <= (DT_PROP(node, subslot_size) * 8)) |
| |
| #define VALIDATE_LINKED_TERMINAL(node) \ |
| UTIL_OR(DT_NODE_HAS_COMPAT(DT_PROP(node, linked_terminal), \ |
| zephyr_uac2_input_terminal), \ |
| DT_NODE_HAS_COMPAT(DT_PROP(node, linked_terminal), \ |
| zephyr_uac2_output_terminal)) |
| |
| #define VALIDATE_NODE(node) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_clock_source), ( \ |
| BUILD_ASSERT(DT_PROP_LEN(node, sampling_frequencies), \ |
| "Sampling frequencies array must not be empty"); \ |
| BUILD_ASSERT(IS_ARRAY_SORTED(node, sampling_frequencies), \ |
| "Sampling frequencies array must be sorted ascending"); \ |
| )) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_input_terminal), ( \ |
| BUILD_ASSERT(!((SPATIAL_LOCATIONS_U32(node) & BIT(31))) || \ |
| SPATIAL_LOCATIONS_U32(node) == BIT(31), \ |
| "Raw Data set alongside other spatial locations"); \ |
| BUILD_ASSERT(VALIDATE_INPUT_TERMINAL_ASSOCIATION(node), \ |
| "Terminals associations must be Input<->Output"); \ |
| )) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_output_terminal), ( \ |
| BUILD_ASSERT(VALIDATE_OUTPUT_TERMINAL_ASSOCIATION(node), \ |
| "Terminals associations must be Input<->Output"); \ |
| )) \ |
| IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_audio_streaming), ( \ |
| BUILD_ASSERT(VALIDATE_LINKED_TERMINAL(node), \ |
| "Linked Terminal must be Input or Output Terminal"); \ |
| BUILD_ASSERT(!NEEDS_SUBSLOT_SIZE_AND_BIT_RESOLUTION(node) || \ |
| VALIDATE_SUBSLOT_SIZE(node), \ |
| "Subslot Size can only be 1, 2, 3 or 4"); \ |
| BUILD_ASSERT(!NEEDS_SUBSLOT_SIZE_AND_BIT_RESOLUTION(node) || \ |
| VALIDATE_BIT_RESOLUTION(node), \ |
| "Bit Resolution must fit inside Subslot Size"); \ |
| BUILD_ASSERT(!DT_PROP(node, implicit_feedback) || \ |
| !AS_IS_SOF_SYNCHRONIZED(node), \ |
| "Implicit feedback on SOF synchronized clock"); \ |
| )) |
| |
| #endif /* ZEPHYR_INCLUDE_USBD_UAC2_MACROS_H_ */ |