Merge pull request #1702 from battlesnake/uac2-interrupt-endpoint

UAC2 supports interrupt-endpoint for providing control-change notifications to the host
This commit is contained in:
HiFiPhile
2024-04-02 20:19:58 +02:00
committed by GitHub
8 changed files with 161 additions and 63 deletions

View File

@@ -924,6 +924,31 @@ typedef struct TU_ATTR_PACKED {
} subrange[numSubRanges]; \
}
// 6.1 Interrupt Data Message Format
typedef struct TU_ATTR_PACKED
{
uint8_t bInfo;
uint8_t bAttribute;
union
{
uint16_t wValue;
struct
{
uint8_t wValue_cn_or_mcn;
uint8_t wValue_cs;
};
};
union
{
uint16_t wIndex;
struct
{
uint8_t wIndex_ep_or_int;
uint8_t wIndex_entity_id;
};
};
} audio_interrupt_data_t;
/** @} */
#ifdef __cplusplus

View File

@@ -301,10 +301,12 @@ typedef struct
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
uint8_t ep_int_ctr; // Audio control interrupt EP.
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
uint8_t ep_int; // Audio control interrupt EP.
#endif
bool mounted; // Device opened
/*------------- From this point, data is not cleared by bus reset -------------*/
uint16_t desc_length; // Length of audio function descriptor
@@ -358,8 +360,8 @@ typedef struct
#endif
// Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74)
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE];
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
CFG_TUSB_MEM_ALIGN uint8_t ep_int_buf[6];
#endif
// Decoding parameters - parameters are set when alternate AS interface is set by host
@@ -486,23 +488,7 @@ bool tud_audio_n_mounted(uint8_t func_id)
TU_VERIFY(func_id < CFG_TUD_AUDIO);
audiod_function_t* audio = &_audiod_fct[func_id];
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
if (audio->ep_out == 0) return false;
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN
if (audio->ep_in == 0) return false;
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
if (audio->ep_int_ctr == 0) return false;
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
if (audio->ep_fb == 0) return false;
#endif
return true;
return audio->mounted;
}
//--------------------------------------------------------------------+
@@ -825,24 +811,30 @@ tu_fifo_t* tud_audio_n_get_tx_support_ff(uint8_t func_id, uint8_t ff_idx)
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
// If no interrupt transmit is pending bytes get written into buffer and a transmit is scheduled - once transmit completed tud_audio_int_ctr_done_cb() is called in inform user
uint16_t tud_audio_int_ctr_n_write(uint8_t func_id, uint8_t const* buffer, uint16_t len)
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
// If no interrupt transmit is pending bytes get written into buffer and a transmit is scheduled - once transmit completed tud_audio_int_done_cb() is called in inform user
bool tud_audio_int_n_write(uint8_t func_id, const audio_interrupt_data_t * data)
{
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
TU_VERIFY(_audiod_fct[func_id].ep_int != 0);
// We write directly into the EP's buffer - abort if previous transfer not complete
TU_VERIFY(!usbd_edpt_busy(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int_ctr));
TU_VERIFY(usbd_edpt_claim(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int));
TU_VERIFY(tu_memcpy_s(_audiod_fct[func_id].ep_int_ctr_buf, CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE, buffer, len)==0);
// Schedule transmit
TU_VERIFY(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int_ctr, _audiod_fct[func_id].ep_int_ctr_buf, len));
// Check length
if (tu_memcpy_s(_audiod_fct[func_id].ep_int_buf, sizeof(_audiod_fct[func_id].ep_int_buf), data, sizeof(audio_interrupt_data_t)) == 0)
{
// Schedule transmit
TU_ASSERT(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int, _audiod_fct[func_id].ep_int_buf, sizeof(_audiod_fct[func_id].ep_int_buf)), 0);
} else
{
// Release endpoint since we don't make any transfer
usbd_edpt_release(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int);
}
return true;
}
#endif
// This function is called once a transmit of an audio packet was successfully completed. Here, we encode samples and place it in IN EP's buffer for next transmission.
@@ -1447,10 +1439,11 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
// Verify version is correct - this check can be omitted
TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
// Verify interrupt control EP is enabled if demanded by descriptor - this should be best some static check however - this check can be omitted
if (itf_desc->bNumEndpoints == 1) // 0 or 1 EPs are allowed
// Verify interrupt control EP is enabled if demanded by descriptor
TU_ASSERT(itf_desc->bNumEndpoints <= 1); // 0 or 1 EPs are allowed
if (itf_desc->bNumEndpoints == 1)
{
TU_VERIFY(CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0);
TU_ASSERT(CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP);
}
// Alternate setting MUST be zero - this check can be omitted
@@ -1594,6 +1587,32 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
}
#endif // CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
{
uint8_t const *p_desc = _audiod_fct[i].p_desc;
uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0)
{
// For each endpoint
if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
{
tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const *) p_desc;
uint8_t const ep_addr = desc_ep->bEndpointAddress;
// If endpoint is input-direction and interrupt-type
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT)
{
// Store endpoint number and open endpoint
_audiod_fct[i].ep_int = ep_addr;
TU_ASSERT(usbd_edpt_open(_audiod_fct[i].rhport, desc_ep));
}
}
p_desc = tu_desc_next(p_desc);
}
}
#endif
_audiod_fct[i].mounted = true;
break;
}
}
@@ -2120,10 +2139,10 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
{
audiod_function_t* audio = &_audiod_fct[func_id];
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
// Data transmission of control interrupt finished
if (audio->ep_int_ctr == ep_addr)
if (audio->ep_int == ep_addr)
{
// According to USB2 specification, maximum payload of interrupt EP is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes on high speed (but only if an alternate interface other than 0 is used - see specification p. 49)
// In case there is nothing to send we have to return a NAK - this is taken care of by PHY ???
@@ -2132,7 +2151,8 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
// I assume here, that things above are handled by PHY
// All transmission is done - what remains to do is to inform job was completed
if (tud_audio_int_ctr_done_cb) TU_VERIFY(tud_audio_int_ctr_done_cb(rhport, (uint16_t) xferred_bytes));
if (tud_audio_int_done_cb) tud_audio_int_done_cb(rhport);
return true;
}
#endif

View File

@@ -196,13 +196,9 @@
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0 // 0 or 1
#endif
// Audio interrupt control EP size - disabled if 0
#ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control - if required - 6 Bytes according to UAC 2 specification (p. 74)
#endif
#ifndef CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE
#define CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE 6 // Buffer size of audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
// Enable/disable interrupt EP (required for notifying host of control changes)
#ifndef CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
#define CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP 0 // Feedback - 0 or 1
#endif
// Use software encoding/decoding
@@ -393,8 +389,8 @@ uint16_t tud_audio_n_write_support_ff (uint8_t func_id, uint8_t ff_i
tu_fifo_t* tud_audio_n_get_tx_support_ff (uint8_t func_id, uint8_t ff_idx);
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
uint16_t tud_audio_int_ctr_n_write (uint8_t func_id, uint8_t const* buffer, uint16_t len);
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
bool tud_audio_int_n_write (uint8_t func_id, const audio_interrupt_data_t * data);
#endif
@@ -437,8 +433,8 @@ static inline tu_fifo_t* tud_audio_get_tx_support_ff (uint8_t ff_idx);
// INT CTR API
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
static inline uint16_t tud_audio_int_ctr_write (uint8_t const* buffer, uint16_t len);
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
static inline bool tud_audio_int_write (const audio_interrupt_data_t * data);
#endif
// Buffer control EP data and schedule a transmit
@@ -537,8 +533,8 @@ TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t n_bytes_copied);
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
TU_ATTR_WEAK void tud_audio_int_done_cb(uint8_t rhport);
#endif
// Invoked when audio set interface request received
@@ -669,10 +665,10 @@ static inline tu_fifo_t* tud_audio_get_tx_support_ff(uint8_t ff_idx)
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t len)
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
static inline bool tud_audio_int_write(const audio_interrupt_data_t * data)
{
return tud_audio_int_ctr_n_write(0, buffer, len);
return tud_audio_int_n_write(0, data);
}
#endif

View File

@@ -393,6 +393,11 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
// For more channels, add definitions here
/* Standard AC Interrupt Endpoint Descriptor(4.8.2.1) */
#define TUD_AUDIO_DESC_STD_AC_INT_EP_LEN 7
#define TUD_AUDIO_DESC_STD_AC_INT_EP(_ep, _interval) \
TUD_AUDIO_DESC_STD_AC_INT_EP_LEN, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(6), _interval
/* Standard AS Interface Descriptor(4.9.1) */
#define TUD_AUDIO_DESC_STD_AS_INT_LEN 9
#define TUD_AUDIO_DESC_STD_AS_INT(_itfnum, _altset, _nEPs, _stridx) \
@@ -468,7 +473,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
@@ -517,7 +522,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
@@ -565,7 +570,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\