diff options
Diffstat (limited to 'src/class/audio/audio_device.c')
| -rw-r--r-- | src/class/audio/audio_device.c | 268 |
1 files changed, 250 insertions, 18 deletions
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 1c9e0494e..67c94837a 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -152,6 +152,9 @@ static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * 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); bool tud_audio_n_mounted(uint8_t itf) { @@ -638,7 +641,7 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass); // Verify version is correct - this check can be omitted - TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_PROTOCOL_V2); + 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 @@ -666,7 +669,7 @@ 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) // Notify caller we read complete descriptor -// (*p_length) += tud_audio_desc_lengths[i]; + // (*p_length) += tud_audio_desc_lengths[i]; // TODO: Find a way to find end of current audio function and avoid necessity of tud_audio_desc_lengths - since now max_length is available we could do this surely somehow uint16_t drv_len = tud_audio_desc_lengths[i] - TUD_AUDIO_DESC_IAD_LEN; // - TUD_AUDIO_DESC_IAD_LEN since tinyUSB already handles the IAD descriptor @@ -675,8 +678,6 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * p_request) { - (void) rhport; - #if CFG_TUD_AUDIO_N_AS_INT > 0 uint8_t const itf = tu_u16_low(p_request->wIndex); @@ -690,6 +691,7 @@ static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * return true; #else + (void) rhport; (void) p_request; return false; #endif @@ -805,21 +807,85 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * // Check for nothing found - we can rely on this since EP descriptors are never the last descriptors, there are always also class specific EP descriptors following! TU_VERIFY(p_desc < p_desc_end); - // Conduct audio driver function specific stuff - - // HERE DO WHAT YOU HAVE TO DO - E.G. START ADC OR SO - //#error Implementation specific setInterface code required here! + // Invoke callback + if (tud_audio_set_itf_cb) + { + if (!tud_audio_set_itf_cb(rhport, p_request)) + { + return false; + } + } // Save current alternative interface setting _audiod_itf[idxDriver].altSetting[idxItf] = alt; + tud_control_status(rhport, p_request); + return true; } +// Invoked when class request DATA stage is finished. +// return false to stall control EP (e.g Host send non-sense DATA) bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const * p_request) { - (void) rhport; - (void) p_request; + // Handle audio class specific set requests + if(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) + { + 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 + if (entityID != 0) + { + // Invoke callback + if (tud_audio_set_req_entity_cb && tud_audio_set_req_entity_cb(rhport, p_request)) + { + tud_control_status(rhport, p_request); + } + else + { + 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)) + { + tud_control_status(rhport, p_request); + } + else + { + return false; // In case no callback function is present or request can not be conducted we stall it + } + } + + 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); + + // Invoke callback + if (tud_audio_set_req_ep_cb && tud_audio_set_req_ep_cb(rhport, p_request)) + { + tud_control_status(rhport, p_request); + } + else + { + 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; + } + } return true; } @@ -829,17 +895,103 @@ bool audiod_control_request(uint8_t rhport, tusb_control_request_t const * p_req { (void) rhport; - switch (p_request->bRequest) + // Handle standard requests - standard set requests usually have no data stage so we also handle set requests here + if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) + { + switch (p_request->bRequest) + { + case TUSB_REQ_GET_INTERFACE: + return audiod_get_interface(rhport, p_request); + + case TUSB_REQ_SET_INTERFACE: + return audiod_set_interface(rhport, p_request); + + // Unknown/Unsupported request + default: TU_BREAKPOINT(); return false; + } + } + + // Handle class requests + if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { - case TUSB_REQ_GET_INTERFACE: - return audiod_get_interface(rhport, p_request); + // 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 + if (entityID != 0) + { + TU_VERIFY(audiod_verify_entity_exists(itf, entityID)); + + // 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) + { + return tud_audio_get_req_entity_cb(rhport, p_request); + } + else + { + TU_LOG2(" No entity get request callback available!\r\n"); + } + } + else + { + TU_VERIFY(audiod_verify_itf_exists(itf)); + + // 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) + { + return tud_audio_get_req_itf_cb(rhport, p_request); + } + else + { + TU_LOG2(" No interface get request callback available!\r\n"); + } + } + + 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); - case TUSB_REQ_SET_INTERFACE: - return audiod_set_interface(rhport, p_request); + // 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)); - default: + // 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) + { + return tud_audio_get_req_ep_cb(rhport, p_request); + } + 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; } + + // There went something wrong + TU_BREAKPOINT(); + return false; } bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) @@ -936,19 +1088,22 @@ 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 +// (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) { // Loop over audio driver interfaces uint8_t i; for (i = 0; i < CFG_TUD_AUDIO; i++) { - if (!_audiod_itf[i].p_desc) + if (_audiod_itf[i].p_desc) { // Get pointer at end uint8_t const *p_desc_end = _audiod_itf[i].p_desc + tud_audio_desc_lengths[i]; // Advance past AC descriptors - uint8_t const * p_desc = tu_desc_next(_audiod_itf[i].p_desc); + uint8_t const *p_desc = tu_desc_next(_audiod_itf[i].p_desc); p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength; uint8_t tmp = 0; @@ -973,6 +1128,83 @@ 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) +{ + uint8_t i; + for (i = 0; i < CFG_TUD_AUDIO; i++) + { + // Look for the correct driver by checking if the unique standard AC interface number fits + if (_audiod_itf[i].p_desc && ((tusb_desc_interface_t const *)_audiod_itf[i].p_desc)->bInterfaceNumber == itf) + { + // Get pointers after class specific AC descriptors and end of AC descriptors - entities are defined in between + uint8_t const *p_desc = tu_desc_next(_audiod_itf[i].p_desc); // Points to CS AC descriptor + uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength + p_desc; + p_desc = tu_desc_next(p_desc); // Get past CS AC descriptor + + while (p_desc < p_desc_end) + { + if (p_desc[3] == entityID) // Entity IDs are always at offset 3 + { + return true; + } + p_desc = tu_desc_next(p_desc); + } + } + } + return false; +} + +static bool audiod_verify_itf_exists(uint8_t itf) +{ + uint8_t i; + for (i = 0; i < CFG_TUD_AUDIO; i++) + { + if (_audiod_itf[i].p_desc) + { + // Get pointer at beginning and end + uint8_t const *p_desc = _audiod_itf[i].p_desc; + uint8_t const *p_desc_end = _audiod_itf[i].p_desc + tud_audio_desc_lengths[i]; + + while (p_desc < p_desc_end) + { + if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)_audiod_itf[i].p_desc)->bInterfaceNumber == itf) + { + return true; + } + p_desc = tu_desc_next(p_desc); + } + } + } + return false; +} + +static bool audiod_verify_ep_exists(uint8_t ep) +{ + uint8_t i; + for (i = 0; i < CFG_TUD_AUDIO; i++) + { + if (_audiod_itf[i].p_desc) + { + // Get pointer at end + uint8_t const *p_desc_end = _audiod_itf[i].p_desc + tud_audio_desc_lengths[i]; + + // Advance past AC descriptors - EP we look for are streaming EPs + uint8_t const *p_desc = tu_desc_next(_audiod_itf[i].p_desc); + p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength; + + while (p_desc < p_desc_end) + { + if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT && ((tusb_desc_endpoint_t const * )p_desc)->bEndpointAddress == ep) + { + return true; + } + p_desc = tu_desc_next(p_desc); + } + } + } + return false; +} + #endif //TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO // OLD |
