diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h index 70d431282..d6f3e22e2 100644 --- a/src/class/audio/audio.h +++ b/src/class/audio/audio.h @@ -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 diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index f4e7de0c8..93e995c56 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -301,8 +301,8 @@ typedef struct #endif -#if CFG_TUD_AUDIO_INT_EPSIZE_IN - uint8_t ep_int; // Audio control interrupt EP. +#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP + uint8_t ep_int; // Audio control interrupt EP. #endif /*------------- From this point, data is not cleared by bus reset -------------*/ @@ -358,8 +358,8 @@ typedef struct #endif // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74) -#if CFG_TUD_AUDIO_INT_EPSIZE_IN - CFG_TUSB_MEM_ALIGN uint8_t ep_int_buf[CFG_TUD_AUDIO_INT_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 @@ -826,26 +826,29 @@ tu_fifo_t* tud_audio_n_get_tx_support_ff(uint8_t func_id, uint8_t ff_idx) #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 -uint16_t tud_audio_int_n_write(uint8_t func_id, uint8_t const* buffer, uint16_t len) +bool tud_audio_int_n_write(uint8_t func_id, const audio_interrupt_data_t * data) { - TU_VERIFY(_audiod_fct[func_id].ep_int != 0); - 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)); + TU_VERIFY(usbd_edpt_claim(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int)); // Check length - TU_VERIFY(tu_memcpy_s(_audiod_fct[func_id].ep_int_buf, CFG_TUD_AUDIO_INT_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, _audiod_fct[func_id].ep_int_buf, len)); + 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. @@ -1091,48 +1094,6 @@ static inline bool audiod_fb_send(uint8_t rhport, audiod_function_t *audio) } #endif -#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP -static bool set_int_number(audiod_function_t *audio) -{ - - uint8_t const *p_desc = audio->p_desc; - // Get pointer at end - uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN; - - - bool found = false; - while (!found && p_desc < p_desc_end) - { - // For each interface/alternate - if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) - { - uint8_t foundEPs = 0, nEps = ((tusb_desc_interface_t const * )p_desc)->bNumEndpoints; - while (!found && foundEPs < nEps && p_desc < p_desc_end) - { - // 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 == 0x03) // Check if usage is interrupt EP - { - // Store endpoint number and open endpoint - audio->ep_int = ep_addr; - TU_ASSERT(usbd_edpt_open(audio->rhport, desc_ep)); - found = true; - } - foundEPs += 1; - } - p_desc = tu_desc_next(p_desc); - } - } - p_desc = tu_desc_next(p_desc); - } - return found; -} -#endif - //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ @@ -1492,10 +1453,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_EPSIZE_IN > 0); + TU_ASSERT(CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP); } // Alternate setting MUST be zero - this check can be omitted @@ -1639,6 +1601,31 @@ 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 + break; } } @@ -1649,10 +1636,6 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin // This is all we need so far - the EPs are setup by a later set_interface request (as per UAC2 specification) uint16_t drv_len = _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN; // - TUD_AUDIO_DESC_IAD_LEN since tinyUSB already handles the IAD descriptor -#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP - TU_ASSERT(set_int_number(&_audiod_fct[i])); -#endif - return drv_len; } @@ -2169,7 +2152,7 @@ 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_EPSIZE_IN +#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP // Data transmission of control interrupt finished if (audio->ep_int == ep_addr) @@ -2181,7 +2164,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_done_cb) TU_VERIFY(tud_audio_int_done_cb(rhport, (uint16_t) xferred_bytes)); + if (tud_audio_int_done_cb) tud_audio_int_done_cb(rhport); + return true; } #endif diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index 70bbd7ff6..040a760d6 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -198,21 +198,7 @@ // 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 - -// Audio interrupt control EP size - disabled if 0 -#ifndef CFG_TUD_AUDIO_INT_EPSIZE_IN -// Audio interrupt control - if required - 6 Bytes according to UAC 2 specification (p. 74) -#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP -#define CFG_TUD_AUDIO_INT_EPSIZE_IN 6 -#else -#define CFG_TUD_AUDIO_INT_EPSIZE_IN 0 -#endif -#endif - -#ifndef CFG_TUD_AUDIO_INT_EP_IN_SW_BUFFER_SIZE -#define CFG_TUD_AUDIO_INT_EP_IN_SW_BUFFER_SIZE 6 // Buffer size of audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74) +#define CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP 0 // Feedback - 0 or 1 #endif // Use software encoding/decoding @@ -404,7 +390,7 @@ tu_fifo_t* tud_audio_n_get_tx_support_ff (uint8_t func_id, uint8_t ff_i #endif #if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP -uint16_t tud_audio_int_n_write (uint8_t func_id, uint8_t const* buffer, uint16_t len); +bool tud_audio_int_n_write (uint8_t func_id, const audio_interrupt_data_t * data); #endif @@ -448,7 +434,7 @@ static inline tu_fifo_t* tud_audio_get_tx_support_ff (uint8_t ff_idx); // INT CTR API #if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP -static inline uint16_t tud_audio_int_write (uint8_t const* buffer, uint16_t len); +static inline bool tud_audio_int_write (const audio_interrupt_data_t * data); #endif // Buffer control EP data and schedule a transmit @@ -548,32 +534,7 @@ 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_ENABLE_INTERRUPT_EP -// UAC2 ยง6.1 "interrupt data message" -typedef struct TU_ATTR_PACKED -{ - uint8_t bInfo; - uint8_t bAttribute; - union TU_ATTR_PACKED - { - uint16_t wValue; - struct TU_ATTR_PACKED - { - uint8_t wValue_cn_or_mcn; - uint8_t wValue_cs; - }; - }; - union TU_ATTR_PACKED - { - uint16_t wIndex; - struct TU_ATTR_PACKED - { - uint8_t wIndex_ep_or_int; - uint8_t wIndex_entity_id; - }; - }; -} audio_status_update_t; - -TU_ATTR_WEAK bool tud_audio_int_done_cb(uint8_t rhport, uint16_t n_bytes_copied); +TU_ATTR_WEAK void tud_audio_int_done_cb(uint8_t rhport); #endif // Invoked when audio set interface request received @@ -705,9 +666,9 @@ static inline tu_fifo_t* tud_audio_get_tx_support_ff(uint8_t ff_idx) #endif #if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP -static inline uint16_t tud_audio_int_write(uint8_t const* buffer, uint16_t len) +static inline bool tud_audio_int_write(const audio_interrupt_data_t * data) { - return tud_audio_int_n_write(0, buffer, len); + return tud_audio_int_n_write(0, data); } #endif diff --git a/src/device/usbd.h b/src/device/usbd.h index cf500143a..2e3987b99 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -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) */\