From d91843bcd21262658b43480240e5dfc2b7d7bd20 Mon Sep 17 00:00:00 2001 From: Reinhard Panhuber Date: Sat, 25 Jul 2020 11:18:50 +0200 Subject: [PATCH] Get and set requests work --- src/class/audio/audio.h | 311 +++++++++++++++++---------------- src/class/audio/audio_device.c | 201 ++++++++++++++------- src/class/audio/audio_device.h | 14 +- 3 files changed, 309 insertions(+), 217 deletions(-) diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h index 011f5381..36f9f211 100644 --- a/src/class/audio/audio.h +++ b/src/class/audio/audio.h @@ -652,165 +652,172 @@ typedef enum AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000, } audio_channel_config_t; +/// AUDIO Channel Cluster Descriptor (4.1) +typedef struct TU_ATTR_PACKED { + uint8_t bNrChannels; ///< Number of channels currently connected. + audio_channel_config_t bmChannelConfig; ///< Bitmap according to 'audio_channel_config_t' with a 1 set if channel is connected and 0 else. In case channels are non-predefined ignore them here (see UAC2 specification 4.1 Audio Channel Cluster Descriptor. + uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first inserted channel with a non-predefined spatial location. +} audio_desc_channel_cluster_t; + /// AUDIO Class-Specific AC Interface Header Descriptor (4.7.2) typedef struct TU_ATTR_PACKED { - uint8_t bLength ; ///< Size of this descriptor in bytes: 9. - uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. - uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_HEADER. - uint16_t bcdADC ; ///< Audio Device Class Specification Release Number in Binary-Coded Decimal. Value: U16_TO_U8S_LE(0x0200). - uint8_t bCategory ; ///< Constant, indicating the primary use of this audio function, as intended by the manufacturer. See: audio_function_t. - uint16_t wTotalLength ; ///< Total number of bytes returned for the class-specific AudioControl interface descriptor. Includes the combined length of this descriptor header and all Clock Source, Unit and Terminal descriptors. - uint8_t bmControls ; ///< See: audio_cs_ac_interface_control_pos_t. + uint8_t bLength ; ///< Size of this descriptor in bytes: 9. + uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. + uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_HEADER. + uint16_t bcdADC ; ///< Audio Device Class Specification Release Number in Binary-Coded Decimal. Value: U16_TO_U8S_LE(0x0200). + uint8_t bCategory ; ///< Constant, indicating the primary use of this audio function, as intended by the manufacturer. See: audio_function_t. + uint16_t wTotalLength ; ///< Total number of bytes returned for the class-specific AudioControl interface descriptor. Includes the combined length of this descriptor header and all Clock Source, Unit and Terminal descriptors. + uint8_t bmControls ; ///< See: audio_cs_ac_interface_control_pos_t. } audio_desc_cs_ac_interface_t; /// AUDIO Clock Source Descriptor (4.7.2.1) typedef struct TU_ATTR_PACKED { - uint8_t bLength ; ///< Size of this descriptor in bytes: 8. - uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. - uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE. - uint8_t bClockID ; ///< Constant uniquely identifying the Clock Source Entity within the audio function. This value is used in all requests to address this Entity. - uint8_t bmAttributes ; ///< See: audio_clock_source_attribute_t. - uint8_t bmControls ; ///< See: audio_clock_source_control_pos_t. - uint8_t bAssocTerminal ; ///< Terminal ID of the Terminal that is associated with this Clock Source. - uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Source Entity. + uint8_t bLength ; ///< Size of this descriptor in bytes: 8. + uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. + uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE. + uint8_t bClockID ; ///< Constant uniquely identifying the Clock Source Entity within the audio function. This value is used in all requests to address this Entity. + uint8_t bmAttributes ; ///< See: audio_clock_source_attribute_t. + uint8_t bmControls ; ///< See: audio_clock_source_control_pos_t. + uint8_t bAssocTerminal ; ///< Terminal ID of the Terminal that is associated with this Clock Source. + uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Source Entity. } audio_desc_clock_source_t; /// AUDIO Clock Selector Descriptor (4.7.2.2) for ONE pin typedef struct TU_ATTR_PACKED { - uint8_t bLength ; ///< Size of this descriptor, in bytes: 7+p. - uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. - uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR. - uint8_t bClockID ; ///< Constant uniquely identifying the Clock Selector Entity within the audio function. This value is used in all requests to address this Entity. - uint8_t bNrInPins ; ///< Number of Input Pins of this Unit: p = 1 thus bNrInPins = 1. - uint8_t baCSourceID ; ///< ID of the Clock Entity to which the first Clock Input Pin of this Clock Selector Entity is connected.. - uint8_t bmControls ; ///< See: audio_clock_selector_control_pos_t. - uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Selector Entity. + uint8_t bLength ; ///< Size of this descriptor, in bytes: 7+p. + uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. + uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR. + uint8_t bClockID ; ///< Constant uniquely identifying the Clock Selector Entity within the audio function. This value is used in all requests to address this Entity. + uint8_t bNrInPins ; ///< Number of Input Pins of this Unit: p = 1 thus bNrInPins = 1. + uint8_t baCSourceID ; ///< ID of the Clock Entity to which the first Clock Input Pin of this Clock Selector Entity is connected.. + uint8_t bmControls ; ///< See: audio_clock_selector_control_pos_t. + uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Selector Entity. } audio_desc_clock_selector_t; /// AUDIO Clock Selector Descriptor (4.7.2.2) for multiple pins #define audio_desc_clock_selector_n_t(source_num) \ - struct TU_ATTR_PACKED { \ + struct TU_ATTR_PACKED { \ uint8_t bLength ; \ uint8_t bDescriptorType ; \ uint8_t bDescriptorSubType ; \ uint8_t bClockID ; \ uint8_t bNrInPins ; \ - struct TU_ATTR_PACKED { \ - uint8_t baSourceID ; \ - } sourceID[source_num] ; \ - uint8_t bmControls ; \ - uint8_t iClockSource ; \ - } + struct TU_ATTR_PACKED { \ + uint8_t baSourceID ; \ + } sourceID[source_num] ; \ + uint8_t bmControls ; \ + uint8_t iClockSource ; \ +} /// AUDIO Clock Multiplier Descriptor (4.7.2.3) typedef struct TU_ATTR_PACKED { - uint8_t bLength ; ///< Size of this descriptor, in bytes: 7. - uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. - uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER. - uint8_t bClockID ; ///< Constant uniquely identifying the Clock Multiplier Entity within the audio function. This value is used in all requests to address this Entity. - uint8_t bCSourceID ; ///< ID of the Clock Entity to which the last Clock Input Pin of this Clock Selector Entity is connected. - uint8_t bmControls ; ///< See: audio_clock_multiplier_control_pos_t. - uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Multiplier Entity. + uint8_t bLength ; ///< Size of this descriptor, in bytes: 7. + uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. + uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER. + uint8_t bClockID ; ///< Constant uniquely identifying the Clock Multiplier Entity within the audio function. This value is used in all requests to address this Entity. + uint8_t bCSourceID ; ///< ID of the Clock Entity to which the last Clock Input Pin of this Clock Selector Entity is connected. + uint8_t bmControls ; ///< See: audio_clock_multiplier_control_pos_t. + uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Multiplier Entity. } audio_desc_clock_multiplier_t; /// AUDIO Input Terminal Descriptor(4.7.2.4) typedef struct TU_ATTR_PACKED { - uint8_t bLength ; ///< Size of this descriptor, in bytes: 17. - uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. - uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL. - uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_input_type_t for other input types. - uint8_t bAssocTerminal ; ///< ID of the Output Terminal to which this Input Terminal is associated. - uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Input Terminal is connected. - uint8_t bNrChannels ; ///< Number of logical output channels in the Terminal’s output audio channel cluster. - uint32_t bmChannelConfig ; ///< Describes the spatial location of the logical channels. See:audio_channel_config_t. - uint16_t bmControls ; ///< See: audio_terminal_input_control_pos_t. - uint8_t iTerminal ; ///< Index of a string descriptor, describing the Input Terminal. + uint8_t bLength ; ///< Size of this descriptor, in bytes: 17. + uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. + uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL. + uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_input_type_t for other input types. + uint8_t bAssocTerminal ; ///< ID of the Output Terminal to which this Input Terminal is associated. + uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Input Terminal is connected. + uint8_t bNrChannels ; ///< Number of logical output channels in the Terminal’s output audio channel cluster. + uint32_t bmChannelConfig ; ///< Describes the spatial location of the logical channels. See:audio_channel_config_t. + uint16_t bmControls ; ///< See: audio_terminal_input_control_pos_t. + uint8_t iTerminal ; ///< Index of a string descriptor, describing the Input Terminal. } audio_desc_input_terminal_t; /// AUDIO Output Terminal Descriptor(4.7.2.5) typedef struct TU_ATTR_PACKED { - uint8_t bLength ; ///< Size of this descriptor, in bytes: 12. - uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. - uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL. - uint8_t bTerminalID ; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this Terminal. - uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_output_type_t for other output types. - uint8_t bAssocTerminal ; ///< Constant, identifying the Input Terminal to which this Output Terminal is associated. - uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Terminal is connected. - uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Output Terminal is connected. - uint16_t bmControls ; ///< See: audio_terminal_output_type_t. - uint8_t iTerminal ; ///< Index of a string descriptor, describing the Output Terminal. + uint8_t bLength ; ///< Size of this descriptor, in bytes: 12. + uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. + uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL. + uint8_t bTerminalID ; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this Terminal. + uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_output_type_t for other output types. + uint8_t bAssocTerminal ; ///< Constant, identifying the Input Terminal to which this Output Terminal is associated. + uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Terminal is connected. + uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Output Terminal is connected. + uint16_t bmControls ; ///< See: audio_terminal_output_type_t. + uint8_t iTerminal ; ///< Index of a string descriptor, describing the Output Terminal. } audio_desc_output_terminal_t; /// AUDIO Feature Unit Descriptor(4.7.2.8) for ONE channel typedef struct TU_ATTR_PACKED { - uint8_t bLength ; ///< Size of this descriptor, in bytes: 14. - uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. - uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_FEATURE_UNIT. - uint8_t bUnitID ; ///< Constant uniquely identifying the Unit within the audio function. This value is used in all requests to address this Unit. - uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Feature Unit is connected. - struct TU_ATTR_PACKED { - uint32_t bmaControls ; ///< See: audio_feature_unit_control_pos_t. Controls0 is master channel 0 (always present) and Controls1 is logical channel 1. - } controls[2] ; - uint8_t iTerminal ; ///< Index of a string descriptor, describing this Feature Unit. + uint8_t bLength ; ///< Size of this descriptor, in bytes: 14. + uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. + uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_FEATURE_UNIT. + uint8_t bUnitID ; ///< Constant uniquely identifying the Unit within the audio function. This value is used in all requests to address this Unit. + uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Feature Unit is connected. + struct TU_ATTR_PACKED { + uint32_t bmaControls ; ///< See: audio_feature_unit_control_pos_t. Controls0 is master channel 0 (always present) and Controls1 is logical channel 1. + } controls[2] ; + uint8_t iTerminal ; ///< Index of a string descriptor, describing this Feature Unit. } audio_desc_feature_unit_t; /// AUDIO Feature Unit Descriptor(4.7.2.8) for multiple channels #define audio_desc_feature_unit_n_t(ch_num) \ - struct TU_ATTR_PACKED { \ + struct TU_ATTR_PACKED { \ uint8_t bLength ; /* 6+(ch_num+1)*4 */\ - uint8_t bDescriptorType ; \ - uint8_t bDescriptorSubType ; \ - uint8_t bUnitID ; \ - uint8_t bSourceID ; \ - struct TU_ATTR_PACKED { \ - uint32_t bmaControls ; \ - } controls[ch_num+1] ; \ - uint8_t iTerminal ; \ - } + uint8_t bDescriptorType ; \ + uint8_t bDescriptorSubType ; \ + uint8_t bUnitID ; \ + uint8_t bSourceID ; \ + struct TU_ATTR_PACKED { \ + uint32_t bmaControls ; \ + } controls[ch_num+1] ; \ + uint8_t iTerminal ; \ +} /// AUDIO Class-Specific AS Interface Descriptor(4.9.2) typedef struct TU_ATTR_PACKED { - uint8_t bLength ; ///< Size of this descriptor, in bytes: 16. - uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. - uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_AS_GENERAL. - uint8_t bTerminalLink ; ///< The Terminal ID of the Terminal to which this interface is connected. - uint8_t bmControls ; ///< See: audio_cs_as_interface_control_pos_t. - uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. See: audio_format_type_t. - uint32_t bmFormats ; ///< The Audio Data Format(s) that can be used to communicate with this interface.See: audio_data_format_type_I_t. - uint8_t bNrChannels ; ///< Number of physical channels in the AS Interface audio channel cluster. - uint32_t bmChannelConfig ; ///< Describes the spatial location of the physical channels. See: audio_channel_config_t. - uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first physical channel. + uint8_t bLength ; ///< Size of this descriptor, in bytes: 16. + uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. + uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_AS_GENERAL. + uint8_t bTerminalLink ; ///< The Terminal ID of the Terminal to which this interface is connected. + uint8_t bmControls ; ///< See: audio_cs_as_interface_control_pos_t. + uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. See: audio_format_type_t. + uint32_t bmFormats ; ///< The Audio Data Format(s) that can be used to communicate with this interface.See: audio_data_format_type_I_t. + uint8_t bNrChannels ; ///< Number of physical channels in the AS Interface audio channel cluster. + uint32_t bmChannelConfig ; ///< Describes the spatial location of the physical channels. See: audio_channel_config_t. + uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first physical channel. } audio_desc_cs_as_interface_t; /// AUDIO Type I Format Type Descriptor(2.3.1.6 - Audio Formats) typedef struct TU_ATTR_PACKED { - uint8_t bLength ; ///< Size of this descriptor, in bytes: 6. - uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. - uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_FORMAT_TYPE. - uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. Value: AUDIO_FORMAT_TYPE_I. - uint8_t bSubslotSize ; ///< The number of bytes occupied by one audio subslot. Can be 1, 2, 3 or 4. - uint8_t bBitResolution ; ///< The number of effectively used bits from the available bits in an audio subslot. + uint8_t bLength ; ///< Size of this descriptor, in bytes: 6. + uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. + uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_FORMAT_TYPE. + uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. Value: AUDIO_FORMAT_TYPE_I. + uint8_t bSubslotSize ; ///< The number of bytes occupied by one audio subslot. Can be 1, 2, 3 or 4. + uint8_t bBitResolution ; ///< The number of effectively used bits from the available bits in an audio subslot. } audio_desc_type_I_format_t; /// AUDIO Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) typedef struct TU_ATTR_PACKED { - uint8_t bLength ; ///< Size of this descriptor, in bytes: 8. - uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_ENDPOINT. - uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_EP_SUBTYPE_GENERAL. - uint8_t bmAttributes ; ///< See: audio_cs_as_iso_data_ep_attribute_t. - uint8_t bmControls ; ///< See: audio_cs_as_iso_data_ep_control_pos_t. - uint8_t bLockDelayUnits ; ///< Indicates the units used for the wLockDelay field. See: audio_cs_as_iso_data_ep_lock_delay_unit_t. - uint16_t wLockDelay ; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field. + uint8_t bLength ; ///< Size of this descriptor, in bytes: 8. + uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_ENDPOINT. + uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_EP_SUBTYPE_GENERAL. + uint8_t bmAttributes ; ///< See: audio_cs_as_iso_data_ep_attribute_t. + uint8_t bmControls ; ///< See: audio_cs_as_iso_data_ep_control_pos_t. + uint8_t bLockDelayUnits ; ///< Indicates the units used for the wLockDelay field. See: audio_cs_as_iso_data_ep_lock_delay_unit_t. + uint16_t wLockDelay ; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field. } audio_desc_cs_as_iso_data_ep_t; //// 5.2.3 Control Request Parameter Block Layout @@ -818,87 +825,91 @@ typedef struct TU_ATTR_PACKED // 5.2.3.1 1-byte Control CUR Parameter Block typedef struct TU_ATTR_PACKED { - int8_t bCur ; ///< The setting for the CUR attribute of the addressed Control + int8_t bCur ; ///< The setting for the CUR attribute of the addressed Control } audio_control_cur_1_t; // 5.2.3.2 2-byte Control CUR Parameter Block typedef struct TU_ATTR_PACKED { - int16_t bCur ; ///< The setting for the CUR attribute of the addressed Control + int16_t bCur ; ///< The setting for the CUR attribute of the addressed Control } audio_control_cur_2_t; // 5.2.3.3 4-byte Control CUR Parameter Block typedef struct TU_ATTR_PACKED { - int32_t bCur ; ///< The setting for the CUR attribute of the addressed Control + int32_t bCur ; ///< The setting for the CUR attribute of the addressed Control } audio_control_cur_4_t; +// Use the following ONLY for RECEIVED data - compiler does not know how many subranges are defined! Use the one below for predefined lengths - or if you know what you are doing do what you like // 5.2.3.1 1-byte Control RANGE Parameter Block -//#define audio_control_range_1_n_t(numSubRanges) \ -// struct TU_ATTR_PACKED { \ -// uint16_t wNumSubRanges = numSubRanges; \ -// struct TU_ATTR_PACKED { \ -// int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\ -// int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\ -// uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\ -// } setting[numSubRanges] ; \ -// } - typedef struct TU_ATTR_PACKED { uint16_t wNumSubRanges; - struct TU_ATTR_PACKED { - int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/ - int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/ - uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/ - } setting[] ; + struct TU_ATTR_PACKED { + int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/ + int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/ + uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/ + } subrange[] ; } audio_control_range_1_t; // 5.2.3.2 2-byte Control RANGE Parameter Block -//#define audio_control_range_2_n_t(numSubRanges) \ -// struct TU_ATTR_PACKED { \ -// uint16_t wNumSubRanges = numSubRanges; \ -// struct TU_ATTR_PACKED { \ -// int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\ -// int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\ -// uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\ -// } setting[numSubRanges] ; \ -// } - typedef struct TU_ATTR_PACKED { uint16_t wNumSubRanges; - struct TU_ATTR_PACKED { - int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/ - int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/ - uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/ - } setting[] ; + struct TU_ATTR_PACKED { + int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/ + int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/ + uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/ + } subrange[] ; } audio_control_range_2_t; // 5.2.3.3 4-byte Control RANGE Parameter Block -//#define audio_control_range_4_n_t(numSubRanges) \ -// struct TU_ATTR_PACKED { \ -// uint16_t wNumSubRanges = numSubRanges; \ -// struct TU_ATTR_PACKED { \ -// int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\ -// int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\ -// uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\ -// } setting[numSubRanges] ; \ -// } - typedef struct TU_ATTR_PACKED { uint16_t wNumSubRanges; - struct TU_ATTR_PACKED { - int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/ - int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/ - uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/ - } setting[] ; + struct TU_ATTR_PACKED { + int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/ + int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/ + uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/ + } subrange[] ; } audio_control_range_4_t; -/** @} */ +// 5.2.3.1 1-byte Control RANGE Parameter Block +#define audio_control_range_1_n_t(numSubRanges) \ + struct TU_ATTR_PACKED { \ + uint16_t wNumSubRanges = numSubRanges; \ + struct TU_ATTR_PACKED { \ + int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\ + int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\ + uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\ + } subrange[numSubRanges] ; \ +} + + // 5.2.3.2 2-byte Control RANGE Parameter Block +#define audio_control_range_2_n_t(numSubRanges) \ + struct TU_ATTR_PACKED { \ + uint16_t wNumSubRanges = numSubRanges; \ + struct TU_ATTR_PACKED { \ + int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\ + int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\ + uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\ + } subrange[numSubRanges] ; \ + } + + // 5.2.3.3 4-byte Control RANGE Parameter Block +#define audio_control_range_4_n_t(numSubRanges) \ + struct TU_ATTR_PACKED { \ + uint16_t wNumSubRanges = numSubRanges; \ + struct TU_ATTR_PACKED { \ + int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\ + int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\ + uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\ + } subrange[numSubRanges] ; \ + } + + /** @} */ #ifdef __cplusplus -} + } #endif #endif -/** @} */ + /** @} */ diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 7e7fef19..5f57c5e4 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -157,10 +157,10 @@ static bool audio_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* a static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * p_request); static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * p_request); -static bool audiod_get_interface_index(uint8_t itf, uint8_t *idxDriver, uint8_t *idxItf, uint8_t const **pp_desc_int); -static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID); -static bool audiod_verify_itf_exists(uint8_t itf); -static bool audiod_verify_ep_exists(uint8_t ep); +static bool audiod_get_AS_interface_index(uint8_t itf, uint8_t *idxDriver, uint8_t *idxItf, uint8_t const **pp_desc_int); +static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *idxDriver); +static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *idxDriver); +static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *idxDriver); bool tud_audio_n_mounted(uint8_t itf) { @@ -691,7 +691,7 @@ static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * uint8_t idxDriver, idxItf; uint8_t const *dummy; - TU_VERIFY(audiod_get_interface_index(itf, &idxDriver, &idxItf, &dummy)); + TU_VERIFY(audiod_get_AS_interface_index(itf, &idxDriver, &idxItf, &dummy)); TU_VERIFY(tud_control_xfer(rhport, p_request, &_audiod_itf[idxDriver].altSetting[idxItf], 1)); return true; @@ -724,7 +724,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * // Find index of audio streaming interface and index of interface uint8_t idxDriver, idxItf; uint8_t const *p_desc; - TU_VERIFY(audiod_get_interface_index(itf, &idxDriver, &idxItf, &p_desc)); + TU_VERIFY(audiod_get_AS_interface_index(itf, &idxDriver, &idxItf, &p_desc)); // Look if there is an EP to be closed - for this driver, there are only 3 possible EPs which may be closed (only AS related EPs can be closed, AC EP (if present) is always open) #if CFG_TUD_AUDIO_EPSIZE_IN > 0 @@ -837,6 +837,8 @@ bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const * p_re // Handle audio class specific set requests if(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) { + uint8_t idxDriver; + switch (p_request->bmRequestType_bit.recipient) { case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label @@ -844,28 +846,35 @@ bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const * p_re uint8_t itf = TU_U16_LOW(p_request->wIndex); uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - // Verify if entity is present - This check may be omitted if we trust the host not to send rubbish if (entityID != 0) { - // Invoke callback - if (tud_audio_set_req_entity_cb && tud_audio_set_req_entity_cb(rhport, p_request)) + if (tud_audio_set_req_entity_cb) { - tud_control_status(rhport, p_request); + // Check if entity is present and get corresponding driver index + TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &idxDriver)); + + // Invoke callback + return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf); } else { + TU_LOG2(" No entity set request callback available!\r\n"); return false; // In case no callback function is present or request can not be conducted we stall it } } else { - // Invoke callback - if (tud_audio_set_req_itf_cb && tud_audio_set_req_itf_cb(rhport, p_request)) + if (tud_audio_set_req_itf_cb) { - tud_control_status(rhport, p_request); + // Find index of audio driver structure and verify interface really exists + TU_VERIFY(audiod_verify_itf_exists(itf, &idxDriver)); + + // Invoke callback + return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf); } else { + TU_LOG2(" No interface set request callback available!\r\n"); return false; // In case no callback function is present or request can not be conducted we stall it } } @@ -876,18 +885,20 @@ bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const * p_re uint8_t ep = TU_U16_LOW(p_request->wIndex); - // Invoke callback - if (tud_audio_set_req_ep_cb && tud_audio_set_req_ep_cb(rhport, p_request)) + if (tud_audio_set_req_ep_cb) { - tud_control_status(rhport, p_request); + // Check if entity is present and get corresponding driver index + TU_VERIFY(audiod_verify_ep_exists(ep, &idxDriver)); + + // Invoke callback + return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf); } else { + TU_LOG2(" No EP set request callback available!\r\n"); return false; // In case no callback function is present or request can not be conducted we stall it } - break; - // Unknown/Unsupported recipient default: TU_BREAKPOINT(); return false; } @@ -920,82 +931,89 @@ bool audiod_control_request(uint8_t rhport, tusb_control_request_t const * p_req // Handle class requests if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { + uint8_t itf = TU_U16_LOW(p_request->wIndex); + uint8_t idxDriver; + // Conduct checks which depend on the recipient switch (p_request->bmRequestType_bit.recipient) { case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label - uint8_t itf = TU_U16_LOW(p_request->wIndex); uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - // Verify if entity is present - This check may be omitted if we trust the host not to send rubbish + // Verify if entity is present if (entityID != 0) { - TU_VERIFY(audiod_verify_entity_exists(itf, entityID)); + // Find index of audio driver structure and verify entity really exists + TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &idxDriver)); - // If request is a set request we return true here and handle the rest later in audiod_control_complete() once the data stage was finished - if (p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) return true; - - // Invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests - if (tud_audio_get_req_entity_cb) + // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests + if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) { - return tud_audio_get_req_entity_cb(rhport, p_request); - } - else - { - TU_LOG2(" No entity get request callback available!\r\n"); + if (tud_audio_get_req_entity_cb) + { + return tud_audio_get_req_entity_cb(rhport, p_request); + } + else + { + TU_LOG2(" No entity get request callback available!\r\n"); + return false; // Stall + } } } else { - TU_VERIFY(audiod_verify_itf_exists(itf)); + // Find index of audio driver structure and verify interface really exists + TU_VERIFY(audiod_verify_itf_exists(itf, &idxDriver)); - // If request is a set request we return true here and handle the rest later in audiod_control_complete() once the data stage was finished - if (p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) return true; - - // Invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests - if (tud_audio_get_req_itf_cb) + // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests + if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) { - return tud_audio_get_req_itf_cb(rhport, p_request); - } - else - { - TU_LOG2(" No interface get request callback available!\r\n"); + if (tud_audio_get_req_itf_cb) + { + return tud_audio_get_req_itf_cb(rhport, p_request); + } + else + { + TU_LOG2(" No interface get request callback available!\r\n"); + return false; // Stall + } } } - break; case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label uint8_t ep = TU_U16_LOW(p_request->wIndex); - // Verify if EP is present - This check may be omitted if we trust the host not to send rubbish - TU_VERIFY(audiod_verify_ep_exists(ep)); + // Find index of audio driver structure and verify EP really exists + TU_VERIFY(audiod_verify_ep_exists(ep, &idxDriver)); - // If request is a set request we return true here and handle the rest later in audiod_control_complete() once the data stage was finished - if (p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) return true; - - if (tud_audio_get_req_ep_cb) + // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests + if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) { - return tud_audio_get_req_ep_cb(rhport, p_request); + if (tud_audio_get_req_ep_cb) + { + return tud_audio_get_req_ep_cb(rhport, p_request); + } + else + { + TU_LOG2(" No EP get request callback available!\r\n"); + return false; // Stall + } } - else - { - TU_LOG2(" No EP get request callback available!\r\n"); - } - break; // Unknown/Unsupported recipient default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false; } - // Host expects an answer - in case no callback function is present we stall the request - return false; + // If we end here, the received request is a set request - we schedule a receive for the data stage and return true here. We handle the rest later in audiod_control_complete() once the data stage was finished + TU_VERIFY(tud_control_xfer(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf, CFG_TUD_AUDIO_CTRL_BUF_SIZE)); + return true; } - // There went something wrong + // There went something wrong - unsupported control request type TU_BREAKPOINT(); return false; } @@ -1094,10 +1112,61 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 } -// This helper function finds for a given interface number the index of the attached driver interface, the index of the interface in the audio function +bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len) +{ + // Handles only sending of data not receiving + if (p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) return false; + + // Get corresponding driver index + uint8_t idxDriver; + uint8_t itf = TU_U16_LOW(p_request->wIndex); + + // Conduct checks which depend on the recipient + switch (p_request->bmRequestType_bit.recipient) + { + case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label + + uint8_t entityID = TU_U16_HIGH(p_request->wIndex); + + // Verify if entity is present + if (entityID != 0) + { + // Find index of audio driver structure and verify entity really exists + TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &idxDriver)); + } + else + { + // Find index of audio driver structure and verify interface really exists + TU_VERIFY(audiod_verify_itf_exists(itf, &idxDriver)); + } + break; + + case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label + + uint8_t ep = TU_U16_LOW(p_request->wIndex); + + // Find index of audio driver structure and verify EP really exists + TU_VERIFY(audiod_verify_ep_exists(ep, &idxDriver)); + break; + + // Unknown/Unsupported recipient + default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false; + } + + // Crop length + if (len > CFG_TUD_AUDIO_CTRL_BUF_SIZE) len = CFG_TUD_AUDIO_CTRL_BUF_SIZE; + + // Copy into buffer + memcpy((void *)_audiod_itf[idxDriver].ctrl_buf, data, (size_t)len); + + // Schedule transmit + return tud_control_xfer(rhport, p_request, (void*)_audiod_itf[idxDriver].ctrl_buf, len); +} + +// This helper function finds for a given AS interface number the index of the attached driver structure, the index of the interface in the audio function // (e.g. the std. AS interface with interface number 15 is the first AS interface for the given audio function and thus gets index zero), and -// finally a pointer to the std. AS interface, where the pointer always points to the start i.e. alternate interface zero. -static bool audiod_get_interface_index(uint8_t itf, uint8_t *idxDriver, uint8_t *idxItf, uint8_t const **pp_desc_int) +// finally a pointer to the std. AS interface, where the pointer always points to the first alternate setting i.e. alternate interface zero. +static bool audiod_get_AS_interface_index(uint8_t itf, uint8_t *idxDriver, uint8_t *idxItf, uint8_t const **pp_desc_int) { // Loop over audio driver interfaces uint8_t i; @@ -1134,7 +1203,8 @@ static bool audiod_get_interface_index(uint8_t itf, uint8_t *idxDriver, uint8_t return false; } -static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID) +// Verify an entity with the given ID exists and returns also the corresponding driver index +static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *idxDriver) { uint8_t i; for (i = 0; i < CFG_TUD_AUDIO; i++) @@ -1151,6 +1221,7 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID) { if (p_desc[3] == entityID) // Entity IDs are always at offset 3 { + *idxDriver = i; return true; } p_desc = tu_desc_next(p_desc); @@ -1160,7 +1231,7 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID) return false; } -static bool audiod_verify_itf_exists(uint8_t itf) +static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *idxDriver) { uint8_t i; for (i = 0; i < CFG_TUD_AUDIO; i++) @@ -1175,6 +1246,7 @@ static bool audiod_verify_itf_exists(uint8_t itf) { if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)_audiod_itf[i].p_desc)->bInterfaceNumber == itf) { + *idxDriver = i; return true; } p_desc = tu_desc_next(p_desc); @@ -1184,7 +1256,7 @@ static bool audiod_verify_itf_exists(uint8_t itf) return false; } -static bool audiod_verify_ep_exists(uint8_t ep) +static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *idxDriver) { uint8_t i; for (i = 0; i < CFG_TUD_AUDIO; i++) @@ -1202,6 +1274,7 @@ static bool audiod_verify_ep_exists(uint8_t ep) { if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT && ((tusb_desc_endpoint_t const * )p_desc)->bEndpointAddress == ep) { + *idxDriver = i; return true; } p_desc = tu_desc_next(p_desc); diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index 8494281c..9766eb5f 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -204,6 +204,14 @@ inline void tud_audio_int_ctr_read_flush (void); inline uint32_t tud_audio_int_ctr_write (uint8_t const* buffer, uint32_t bufsize); #endif +// Buffer control EP data and schedule a transmit +// This function is intended to be used if you do not have a persistent buffer or memory location available (e.g. non-local variables) and need to answer onto a +// get request. This function buffers your answer request frame into the control buffer of the corresponding audio driver and schedules a transmit for sending it. +// Since transmission is triggered via interrupts, a persistent memory location is required onto which the buffer pointer in pointing. If you already have such +// available you may directly use 'tud_control_xfer(...)'. In this case data does not need to be copied into an additional buffer and you save some time. +// If the request's wLength is zero, a status packet is sent instead. +bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len); + //--------------------------------------------------------------------+ // Application Callback API (weak is optional) //--------------------------------------------------------------------+ @@ -228,13 +236,13 @@ TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t * n_bytes_c TU_ATTR_WEAK bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request); // Invoked when audio class specific set request received for an EP -TU_ATTR_WEAK bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request); +TU_ATTR_WEAK bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff); // Invoked when audio class specific set request received for an interface -TU_ATTR_WEAK bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request); +TU_ATTR_WEAK bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff); // Invoked when audio class specific set request received for an entity -TU_ATTR_WEAK bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request); +TU_ATTR_WEAK bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff); // Invoked when audio class specific get request received for an EP TU_ATTR_WEAK bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request);