mirror of
https://github.com/hathach/tinyusb.git
synced 2026-02-05 22:15:42 +00:00
Add UAC1 support to speaker example
Signed-off-by: Mengsk <admin@hifiphile.com>
This commit is contained in:
@ -142,7 +142,7 @@ void tud_resume_cb(void) {
|
||||
}
|
||||
|
||||
// Helper for clock get requests
|
||||
static bool tud_audio_clock_get_request(uint8_t rhport, audio20_control_request_t const *request) {
|
||||
static bool tud_audio20_clock_get_request(uint8_t rhport, audio20_control_request_t const *request) {
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
|
||||
|
||||
if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) {
|
||||
@ -177,7 +177,7 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio20_control_request_
|
||||
}
|
||||
|
||||
// Helper for clock set requests
|
||||
static bool tud_audio_clock_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) {
|
||||
static bool tud_audio20_clock_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) {
|
||||
(void) rhport;
|
||||
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
|
||||
@ -199,7 +199,7 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio20_control_request_
|
||||
}
|
||||
|
||||
// Helper for feature unit get requests
|
||||
static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio20_control_request_t const *request) {
|
||||
static bool tud_audio20_feature_unit_get_request(uint8_t rhport, audio20_control_request_t const *request) {
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT);
|
||||
|
||||
if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE && request->bRequest == AUDIO20_CS_REQ_CUR) {
|
||||
@ -227,7 +227,7 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio20_control_r
|
||||
}
|
||||
|
||||
// Helper for feature unit set requests
|
||||
static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) {
|
||||
static bool tud_audio20_feature_unit_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) {
|
||||
(void) rhport;
|
||||
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT);
|
||||
@ -262,16 +262,21 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio20_control_r
|
||||
|
||||
// Invoked when audio class specific get request received for an entity
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
audio20_control_request_t const *request = (audio20_control_request_t const *) p_request;
|
||||
if (tud_audio_version() == 1) {
|
||||
// No entity in UAC1
|
||||
} else {
|
||||
audio20_control_request_t const *request = (audio20_control_request_t const *) p_request;
|
||||
|
||||
if (request->bEntityID == UAC2_ENTITY_CLOCK)
|
||||
return tud_audio_clock_get_request(rhport, request);
|
||||
if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
|
||||
return tud_audio_feature_unit_get_request(rhport, request);
|
||||
else {
|
||||
TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
if (request->bEntityID == UAC2_ENTITY_CLOCK)
|
||||
return tud_audio20_clock_get_request(rhport, request);
|
||||
if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
|
||||
return tud_audio20_feature_unit_get_request(rhport, request);
|
||||
else {
|
||||
TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -280,9 +285,9 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
|
||||
audio20_control_request_t const *request = (audio20_control_request_t const *) p_request;
|
||||
|
||||
if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
|
||||
return tud_audio_feature_unit_set_request(rhport, request, buf);
|
||||
return tud_audio20_feature_unit_set_request(rhport, request, buf);
|
||||
if (request->bEntityID == UAC2_ENTITY_CLOCK)
|
||||
return tud_audio_clock_set_request(rhport, request, buf);
|
||||
return tud_audio20_clock_set_request(rhport, request, buf);
|
||||
TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
|
||||
|
||||
@ -21,7 +21,6 @@ add_executable(${PROJECT})
|
||||
target_sources(${PROJECT} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/quirk_os_guessing.c
|
||||
)
|
||||
|
||||
# Example include
|
||||
|
||||
@ -31,25 +31,17 @@
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
#ifdef CFG_QUIRK_OS_GUESSING
|
||||
#include "quirk_os_guessing.h"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTOTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// List of supported sample rates
|
||||
#if defined(__RX__)
|
||||
const uint32_t sample_rates[] = {44100, 48000};
|
||||
#else
|
||||
// List of supported sample rates for UAC2
|
||||
const uint32_t sample_rates[] = {44100, 48000, 88200, 96000};
|
||||
#endif
|
||||
|
||||
uint32_t current_sample_rate = 44100;
|
||||
|
||||
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
|
||||
|
||||
uint32_t current_sample_rate = 44100;
|
||||
|
||||
/* Blink pattern
|
||||
* - 25 ms : streaming data
|
||||
* - 250 ms : device not mounted
|
||||
@ -153,8 +145,179 @@ void tud_resume_cb(void) {
|
||||
// Application Callback API Implementations
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Helper for clock get requests
|
||||
static bool tud_audio_clock_get_request(uint8_t rhport, audio20_control_request_t const *request) {
|
||||
//--------------------------------------------------------------------+
|
||||
// UAC1 Helper Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static bool audio10_set_req_ep(tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
|
||||
switch (ctrlSel) {
|
||||
case AUDIO10_EP_CTRL_SAMPLING_FREQ:
|
||||
if (p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
|
||||
// Request uses 3 bytes
|
||||
TU_VERIFY(p_request->wLength == 3);
|
||||
|
||||
current_sample_rate = tu_unaligned_read32(pBuff) & 0x00FFFFFF;
|
||||
|
||||
TU_LOG2("EP set current freq: %" PRIu32 "\r\n", current_sample_rate);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool audio10_get_req_ep(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
|
||||
switch (ctrlSel) {
|
||||
case AUDIO10_EP_CTRL_SAMPLING_FREQ:
|
||||
if (p_request->bRequest == AUDIO10_CS_REQ_GET_CUR) {
|
||||
TU_LOG2("EP get current freq\r\n");
|
||||
|
||||
uint8_t freq[3];
|
||||
freq[0] = (uint8_t) (current_sample_rate & 0xFF);
|
||||
freq[1] = (uint8_t) ((current_sample_rate >> 8) & 0xFF);
|
||||
freq[2] = (uint8_t) ((current_sample_rate >> 16) & 0xFF);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, freq, sizeof(freq));
|
||||
}
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool audio10_set_req_entity(tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
||||
|
||||
// If request is for our feature unit
|
||||
if (entityID == UAC1_ENTITY_FEATURE_UNIT) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO10_FU_CTRL_MUTE:
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO10_CS_REQ_SET_CUR:
|
||||
// Only 1st form is supported
|
||||
TU_VERIFY(p_request->wLength ==1);
|
||||
|
||||
mute[channelNum] = pBuff[0];
|
||||
|
||||
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false; // not supported
|
||||
}
|
||||
|
||||
case AUDIO10_FU_CTRL_VOLUME:
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO10_CS_REQ_SET_CUR:
|
||||
// Only 1st form is supported
|
||||
TU_VERIFY(p_request->wLength == 2);
|
||||
|
||||
volume[channelNum] = (int16_t)tu_unaligned_read16(pBuff) / 256;
|
||||
|
||||
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false; // not supported
|
||||
}
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool audio10_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
||||
|
||||
// If request is for our feature unit
|
||||
if (entityID == UAC1_ENTITY_FEATURE_UNIT) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO10_FU_CTRL_MUTE:
|
||||
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
|
||||
// There does not exist a range parameter block for mute
|
||||
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
|
||||
|
||||
case AUDIO10_FU_CTRL_VOLUME:
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO10_CS_REQ_GET_CUR:
|
||||
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
|
||||
{
|
||||
int16_t vol = (int16_t) volume[channelNum];
|
||||
vol = vol * 256; // convert to 1/256 dB units
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &vol, sizeof(vol));
|
||||
}
|
||||
|
||||
case AUDIO10_CS_REQ_GET_MIN:
|
||||
TU_LOG2(" Get Volume min of channel: %u\r\n", channelNum);
|
||||
{
|
||||
int16_t min = -90; // -90 dB
|
||||
min = min * 256; // convert to 1/256 dB units
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &min, sizeof(min));
|
||||
}
|
||||
|
||||
case AUDIO10_CS_REQ_GET_MAX:
|
||||
TU_LOG2(" Get Volume max of channel: %u\r\n", channelNum);
|
||||
{
|
||||
int16_t max = 30; // +30 dB
|
||||
max = max * 256; // convert to 1/256 dB units
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &max, sizeof(max));
|
||||
}
|
||||
|
||||
case AUDIO10_CS_REQ_GET_RES:
|
||||
TU_LOG2(" Get Volume res of channel: %u\r\n", channelNum);
|
||||
{
|
||||
int16_t res = 128; // 0.5 dB
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &res, sizeof(res));
|
||||
}
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// UAC2 Helper Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
|
||||
static bool audio20_clock_get_request(uint8_t rhport, audio20_control_request_t const *request) {
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
|
||||
|
||||
if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) {
|
||||
@ -188,10 +351,7 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio20_control_request_
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper for clock set requests
|
||||
static bool tud_audio_clock_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) {
|
||||
(void) rhport;
|
||||
|
||||
static bool audio20_clock_set_request(audio20_control_request_t const *request, uint8_t const *buf) {
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
|
||||
TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
|
||||
|
||||
@ -210,8 +370,7 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio20_control_request_
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for feature unit get requests
|
||||
static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio20_control_request_t const *request) {
|
||||
static bool audio20_feature_unit_get_request(uint8_t rhport, audio20_control_request_t const *request) {
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT);
|
||||
|
||||
if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE && request->bRequest == AUDIO20_CS_REQ_CUR) {
|
||||
@ -238,10 +397,7 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio20_control_r
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper for feature unit set requests
|
||||
static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) {
|
||||
(void) rhport;
|
||||
|
||||
static bool audio20_feature_unit_set_request(audio20_control_request_t const *request, uint8_t const *buf) {
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT);
|
||||
TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
|
||||
|
||||
@ -268,14 +424,13 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio20_control_r
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked when audio class specific get request received for an entity
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
static bool audio20_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
audio20_control_request_t const *request = (audio20_control_request_t const *) p_request;
|
||||
|
||||
if (request->bEntityID == UAC2_ENTITY_CLOCK)
|
||||
return tud_audio_clock_get_request(rhport, request);
|
||||
return audio20_clock_get_request(rhport, request);
|
||||
if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT)
|
||||
return tud_audio_feature_unit_get_request(rhport, request);
|
||||
return audio20_feature_unit_get_request(rhport, request);
|
||||
else {
|
||||
TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
@ -283,31 +438,24 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
|
||||
return false;
|
||||
}
|
||||
|
||||
// Invoked when audio class specific set request received for an entity
|
||||
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
|
||||
static bool audio20_set_req_entity(tusb_control_request_t const *p_request, uint8_t *buf) {
|
||||
audio20_control_request_t const *request = (audio20_control_request_t const *) p_request;
|
||||
|
||||
if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT)
|
||||
return tud_audio_feature_unit_set_request(rhport, request, buf);
|
||||
return audio20_feature_unit_set_request(request, buf);
|
||||
if (request->bEntityID == UAC2_ENTITY_CLOCK)
|
||||
return tud_audio_clock_set_request(rhport, request, buf);
|
||||
return audio20_clock_set_request(request, buf);
|
||||
TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
|
||||
uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
|
||||
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
|
||||
|
||||
if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0)
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------------+
|
||||
// Main Callback Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
@ -325,6 +473,75 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_reques
|
||||
return true;
|
||||
}
|
||||
|
||||
// Invoked when audio class specific set request received for an EP
|
||||
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
if (tud_audio_version() == 1) {
|
||||
return audio10_set_req_ep(p_request, pBuff);
|
||||
} else if (tud_audio_version() == 2) {
|
||||
// We do not support any requests here
|
||||
}
|
||||
|
||||
return false;// Yet not implemented
|
||||
}
|
||||
|
||||
// Invoked when audio class specific get request received for an EP
|
||||
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
if (tud_audio_version() == 1) {
|
||||
return audio10_get_req_ep(rhport, p_request);
|
||||
} else if (tud_audio_version() == 2) {
|
||||
// We do not support any requests here
|
||||
}
|
||||
|
||||
return false;// Yet not implemented
|
||||
}
|
||||
|
||||
// Invoked when audio class specific set request received for an entity
|
||||
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
|
||||
(void) rhport;
|
||||
|
||||
if (tud_audio_version() == 1) {
|
||||
return audio10_set_req_entity(p_request, buf);
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
} else if (tud_audio_version() == 2) {
|
||||
return audio20_set_req_entity(p_request, buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Invoked when audio class specific get request received for an entity
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
if (tud_audio_version() == 1) {
|
||||
return audio10_get_req_entity(rhport, p_request);
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
} else if (tud_audio_version() == 2) {
|
||||
return audio20_get_req_entity(rhport, p_request);
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
|
||||
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
|
||||
|
||||
if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0)
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t *feedback_param) {
|
||||
(void) func_id;
|
||||
(void) alt_itf;
|
||||
@ -349,16 +566,6 @@ bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t fu
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_QUIRK_OS_GUESSING
|
||||
bool tud_audio_feedback_format_correction_cb(uint8_t func_id) {
|
||||
(void) func_id;
|
||||
if (tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//--------------------------------------------------------------------+
|
||||
// AUDIO Task
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 HiFiPhile
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "quirk_os_guessing.h"
|
||||
|
||||
static tusb_desc_type_t desc_req_buf[2];
|
||||
static int desc_req_idx = 0;
|
||||
|
||||
// Place at the start of tud_descriptor_device_cb()
|
||||
void quirk_os_guessing_desc_device_cb(void) {
|
||||
desc_req_idx = 0;
|
||||
}
|
||||
|
||||
// Place at the start of tud_descriptor_configuration_cb()
|
||||
void quirk_os_guessing_desc_configuration_cb(void) {
|
||||
// Skip redundant request
|
||||
if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_CONFIGURATION)) {
|
||||
desc_req_buf[desc_req_idx++] = TUSB_DESC_CONFIGURATION;
|
||||
}
|
||||
}
|
||||
|
||||
// Place at the start of tud_descriptor_bos_cb()
|
||||
void quirk_os_guessing_desc_bos_cb(void) {
|
||||
// Skip redundant request
|
||||
if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_BOS)) {
|
||||
desc_req_buf[desc_req_idx++] = TUSB_DESC_BOS;
|
||||
}
|
||||
}
|
||||
|
||||
// Place at the start of tud_descriptor_string_cb()
|
||||
void quirk_os_guessing_desc_string_cb(void) {
|
||||
// Skip redundant request
|
||||
if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_STRING)) {
|
||||
desc_req_buf[desc_req_idx++] = TUSB_DESC_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
// Each OS request descriptors differently:
|
||||
// Windows 10 - 11
|
||||
// Device Desc
|
||||
// Config Desc
|
||||
// BOS Desc
|
||||
// String Desc
|
||||
// Linux 3.16 - 6.8
|
||||
// Device Desc
|
||||
// BOS Desc
|
||||
// Config Desc
|
||||
// String Desc
|
||||
// OS X Ventura - Sonoma
|
||||
// Device Desc
|
||||
// String Desc
|
||||
// Config Desc || BOS Desc
|
||||
// BOS Desc || Config Desc
|
||||
quirk_os_guessing_t quirk_os_guessing_get(void) {
|
||||
if (desc_req_idx < 2) {
|
||||
return QUIRK_OS_GUESSING_UNKNOWN;
|
||||
}
|
||||
|
||||
if (desc_req_buf[0] == TUSB_DESC_BOS && desc_req_buf[1] == TUSB_DESC_CONFIGURATION) {
|
||||
return QUIRK_OS_GUESSING_LINUX;
|
||||
} else if (desc_req_buf[0] == TUSB_DESC_CONFIGURATION && desc_req_buf[1] == TUSB_DESC_BOS) {
|
||||
return QUIRK_OS_GUESSING_WINDOWS;
|
||||
} else if (desc_req_buf[0] == TUSB_DESC_STRING && (desc_req_buf[1] == TUSB_DESC_BOS || desc_req_buf[1] == TUSB_DESC_CONFIGURATION)) {
|
||||
return QUIRK_OS_GUESSING_OSX;
|
||||
}
|
||||
|
||||
return QUIRK_OS_GUESSING_UNKNOWN;
|
||||
}
|
||||
@ -1,75 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 HiFiPhile
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _QUIRK_OS_GUESSING_H_
|
||||
#define _QUIRK_OS_GUESSING_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
//================================== !!! WARNING !!! ====================================
|
||||
// This quirk operate out of USB specification in order to workaround specific issues.
|
||||
// It may not work on your platform.
|
||||
//=======================================================================================
|
||||
//
|
||||
// Prerequisites:
|
||||
// - Set USB version to at least 2.01 in Device Descriptor
|
||||
// - Has a valid BOS Descriptor, refer to webusb_serial example
|
||||
//
|
||||
// Attention:
|
||||
// Windows detection result comes out after Configuration Descriptor request,
|
||||
// meaning it will be too late to do descriptor adjustment. It's advised to make
|
||||
// Windows as default configuration and adjust to other OS accordingly.
|
||||
|
||||
typedef enum {
|
||||
QUIRK_OS_GUESSING_UNKNOWN,
|
||||
QUIRK_OS_GUESSING_LINUX,
|
||||
QUIRK_OS_GUESSING_OSX,
|
||||
QUIRK_OS_GUESSING_WINDOWS,
|
||||
} quirk_os_guessing_t;
|
||||
|
||||
// Get Host OS type
|
||||
quirk_os_guessing_t quirk_os_guessing_get(void);
|
||||
|
||||
// Place at the start of tud_descriptor_device_cb()
|
||||
void quirk_os_guessing_desc_device_cb(void);
|
||||
|
||||
// Place at the start of tud_descriptor_configuration_cb()
|
||||
void quirk_os_guessing_desc_configuration_cb(void);
|
||||
|
||||
// Place at the start of tud_descriptor_bos_cb()
|
||||
void quirk_os_guessing_desc_bos_cb(void);
|
||||
|
||||
// Place at the start of tud_descriptor_string_cb()
|
||||
void quirk_os_guessing_desc_string_cb(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _QUIRK_OS_GUESSING_H_ */
|
||||
@ -87,14 +87,6 @@ extern "C" {
|
||||
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||
#endif
|
||||
|
||||
/* (Needed for Full-Speed only)
|
||||
* Enable host OS guessing to workaround UAC2 compatibility issues between Windows and OS X
|
||||
* The default configuration only support Windows and Linux, enable this option for OS X
|
||||
* support. Otherwise if you don't need Windows support you can make OS X's configuration as
|
||||
* default.
|
||||
*/
|
||||
#define CFG_QUIRK_OS_GUESSING 1
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
@ -128,33 +120,33 @@ extern "C" {
|
||||
// AUDIO CLASS DRIVER CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// Can be enabled with Full-Speed device on OSX, which forces feedback EP size to 3, in this case CFG_QUIRK_OS_GUESSING can be disabled
|
||||
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2
|
||||
|
||||
// Audio format type I specifications
|
||||
#if defined(__RX__)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 48000
|
||||
#else
|
||||
#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 96000
|
||||
#endif
|
||||
// 16bit data in 16bit slots
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX 2
|
||||
#define CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX 16
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2
|
||||
// UAC1 Full-Speed endpoint size
|
||||
#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE_FS 48000
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_FS TUD_AUDIO_EP_SIZE(false, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE_FS, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
|
||||
// UAC2 High-Speed endpoint size
|
||||
#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE_HS 96000
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_HS TUD_AUDIO_EP_SIZE(true, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE_HS, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
|
||||
|
||||
// 16bit in 16bit slots
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX 2
|
||||
#define CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX 16
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_FS, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_HS)
|
||||
|
||||
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
|
||||
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
|
||||
// AUDIO_FEEDBACK_METHOD_FIFO_COUNT needs buffer size >= 4* EP size to work correctly
|
||||
// Example read FIFO every 1ms (8 HS frames), so buffer size should be 8 times larger for HS device
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(4 * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_FS, 32 * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_HS)
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
// Enable OUT EP
|
||||
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
|
||||
|
||||
// Enable feedback EP
|
||||
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 1
|
||||
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 1
|
||||
|
||||
// Size of control request buffer
|
||||
#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
|
||||
#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -28,10 +28,6 @@
|
||||
#include "usb_descriptors.h"
|
||||
#include "common_types.h"
|
||||
|
||||
#ifdef CFG_QUIRK_OS_GUESSING
|
||||
#include "quirk_os_guessing.h"
|
||||
#endif
|
||||
|
||||
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
|
||||
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
|
||||
*
|
||||
@ -49,7 +45,7 @@ tusb_desc_device_t const desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0201,
|
||||
.bcdUSB = 0x0200,
|
||||
|
||||
// Use Interface Association Descriptor (IAD) for Audio
|
||||
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
|
||||
@ -71,12 +67,8 @@ tusb_desc_device_t const desc_device =
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const * tud_descriptor_device_cb(void)
|
||||
{
|
||||
#if CFG_QUIRK_OS_GUESSING
|
||||
quirk_os_guessing_desc_device_cb();
|
||||
#endif
|
||||
return (uint8_t const *)&desc_device;
|
||||
uint8_t const * tud_descriptor_device_cb(void) {
|
||||
return (uint8_t const *) &desc_device;
|
||||
}
|
||||
|
||||
#if CFG_AUDIO_DEBUG
|
||||
@ -84,8 +76,7 @@ uint8_t const * tud_descriptor_device_cb(void)
|
||||
// HID Report Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t const desc_hid_report[] =
|
||||
{
|
||||
uint8_t const desc_hid_report[] = {
|
||||
HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\
|
||||
HID_USAGE ( 0x01 ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\
|
||||
@ -101,8 +92,7 @@ uint8_t const desc_hid_report[] =
|
||||
// Invoked when received GET HID REPORT DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) {
|
||||
(void) itf;
|
||||
return desc_hid_report;
|
||||
}
|
||||
@ -112,109 +102,126 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if CFG_AUDIO_DEBUG
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO20_SPEAKER_STEREO_FB_DESC_LEN + TUD_HID_DESC_LEN)
|
||||
#else
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO20_SPEAKER_STEREO_FB_DESC_LEN)
|
||||
#endif
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
|
||||
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
|
||||
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
|
||||
#define EPNUM_AUDIO 0x03
|
||||
#define EPNUM_AUDIO_FB 0x03
|
||||
#define EPNUM_AUDIO_OUT 0x03
|
||||
#define EPNUM_DEBUG 0x04
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_NRF5X
|
||||
// ISO endpoints for NRF5x are fixed to 0x08 (0x88)
|
||||
#elif TU_CHECK_MCU(OPT_MCU_NRF5X)
|
||||
// nRF5x ISO can only be endpoint 8
|
||||
#define EPNUM_AUDIO 0x08
|
||||
#define EPNUM_AUDIO_FB 0x08
|
||||
#define EPNUM_AUDIO_OUT 0x08
|
||||
#define EPNUM_DEBUG 0x01
|
||||
|
||||
#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
|
||||
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
|
||||
// e.g EP1 OUT & EP1 IN cannot exist together
|
||||
#define EPNUM_AUDIO 0x02
|
||||
#define EPNUM_AUDIO_FB 0x01
|
||||
#define EPNUM_AUDIO_OUT 0x02
|
||||
#define EPNUM_DEBUG 0x03
|
||||
|
||||
#else
|
||||
#define EPNUM_AUDIO 0x01
|
||||
#define EPNUM_AUDIO_FB 0x01
|
||||
#define EPNUM_AUDIO_OUT 0x01
|
||||
#define EPNUM_DEBUG 0x02
|
||||
#endif
|
||||
|
||||
uint8_t const desc_configuration_default[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
#if CFG_AUDIO_DEBUG
|
||||
#define CONFIG_UAC1_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO10_SPEAKER_STEREO_FB_DESC_LEN(2) + TUD_HID_DESC_LEN)
|
||||
#else
|
||||
#define CONFIG_UAC1_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO10_SPEAKER_STEREO_FB_DESC_LEN(2))
|
||||
#endif
|
||||
|
||||
// Interface number, string index, byte per sample, bit per sample, EP Out, EP size, EP feedback, feedback EP size,
|
||||
TUD_AUDIO20_SPEAKER_STEREO_FB_DESCRIPTOR(0, 4, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX, EPNUM_AUDIO_OUT, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX, EPNUM_AUDIO_FB | 0x80, 4),
|
||||
uint8_t const desc_uac1_configuration[] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_UAC1_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
// Interface number, string index, byte per sample, bit per sample, EP Out, EP size, EP feedback, sample rates (44.1kHz, 48kHz)
|
||||
TUD_AUDIO10_SPEAKER_STEREO_FB_DESCRIPTOR(ITF_NUM_AUDIO_CONTROL, 5, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX, EPNUM_AUDIO, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_FS, EPNUM_AUDIO_FB | 0x80, 44100, 48000),
|
||||
|
||||
#if CFG_AUDIO_DEBUG
|
||||
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(ITF_NUM_DEBUG, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_DEBUG | 0x80, CFG_TUD_HID_EP_BUFSIZE, 7)
|
||||
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(ITF_NUM_DEBUG, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_DEBUG | 0x80, CFG_TUD_HID_EP_BUFSIZE, 7)
|
||||
#endif
|
||||
};
|
||||
|
||||
#if CFG_QUIRK_OS_GUESSING
|
||||
// OS X needs 3 bytes feedback endpoint on FS
|
||||
uint8_t const desc_configuration_osx_fs[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
TU_VERIFY_STATIC(sizeof(desc_uac1_configuration) == CONFIG_UAC1_TOTAL_LEN, "Incorrect size");
|
||||
|
||||
// Interface number, string index, byte per sample, bit per sample, EP Out, EP size, EP feedback, feedback EP size,
|
||||
TUD_AUDIO20_SPEAKER_STEREO_FB_DESCRIPTOR(0, 4, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX, EPNUM_AUDIO_OUT, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX, EPNUM_AUDIO_FB | 0x80, 3),
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
|
||||
#if CFG_AUDIO_DEBUG
|
||||
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(ITF_NUM_DEBUG, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_DEBUG | 0x80, CFG_TUD_HID_EP_BUFSIZE, 7)
|
||||
#define CONFIG_UAC2_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO20_SPEAKER_STEREO_FB_DESC_LEN + TUD_HID_DESC_LEN)
|
||||
#else
|
||||
#define CONFIG_UAC2_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO20_SPEAKER_STEREO_FB_DESC_LEN)
|
||||
#endif
|
||||
|
||||
uint8_t const desc_uac2_configuration[] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_UAC2_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
// Interface number, string index, byte per sample, bit per sample, EP Out, EP size, EP feedback, feedback EP size,
|
||||
TUD_AUDIO20_SPEAKER_STEREO_FB_DESCRIPTOR(ITF_NUM_AUDIO_CONTROL, 4, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX, EPNUM_AUDIO, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_HS, EPNUM_AUDIO_FB | 0x80, 4),
|
||||
|
||||
#if CFG_AUDIO_DEBUG
|
||||
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(ITF_NUM_DEBUG, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_DEBUG | 0x80, CFG_TUD_HID_EP_BUFSIZE, 7)
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(desc_uac2_configuration) == CONFIG_UAC2_TOTAL_LEN, "Incorrect size");
|
||||
|
||||
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
|
||||
tusb_desc_device_qualifier_t const desc_device_qualifier = {
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0x00
|
||||
};
|
||||
|
||||
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
|
||||
// device_qualifier descriptor describes information about a high-speed capable device that would
|
||||
// change if the device were operating at the other speed. If not highspeed capable stall this request.
|
||||
uint8_t const *tud_descriptor_device_qualifier_cb(void) {
|
||||
return (uint8_t const *) &desc_device_qualifier;
|
||||
}
|
||||
|
||||
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
|
||||
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
|
||||
(void) index;// for multiple configurations
|
||||
|
||||
// if link speed is high return fullspeed config, and vice versa
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_uac1_configuration : desc_uac2_configuration;
|
||||
}
|
||||
|
||||
#endif // highspeed
|
||||
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void)index; // for multiple configurations
|
||||
|
||||
#if CFG_QUIRK_OS_GUESSING
|
||||
quirk_os_guessing_desc_configuration_cb();
|
||||
if(tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) {
|
||||
return desc_configuration_osx_fs;
|
||||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index) {
|
||||
(void) index; // for multiple configurations
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
// Although we are highspeed, host may be fullspeed.
|
||||
if(tud_speed_get() == TUSB_SPEED_FULL) {
|
||||
return desc_uac1_configuration;
|
||||
} else {
|
||||
return desc_uac2_configuration;
|
||||
}
|
||||
#else
|
||||
return desc_uac1_configuration;
|
||||
#endif
|
||||
return desc_configuration_default;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// BOS Descriptor, required for OS guessing quirk
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define TUD_BOS_USB20_EXT_DESC_LEN 7
|
||||
|
||||
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_USB20_EXT_DESC_LEN)
|
||||
|
||||
// BOS Descriptor is required for webUSB
|
||||
uint8_t const desc_bos[] =
|
||||
{
|
||||
// total length, number of device caps
|
||||
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
|
||||
|
||||
// USB 2.0 Extension Descriptor
|
||||
0x07, TUSB_DESC_DEVICE_CAPABILITY, DEVICE_CAPABILITY_USB20_EXTENSION, 0x00, 0x00, 0x00,0x00
|
||||
};
|
||||
|
||||
uint8_t const * tud_descriptor_bos_cb(void)
|
||||
{
|
||||
#if CFG_QUIRK_OS_GUESSING
|
||||
quirk_os_guessing_desc_bos_cb();
|
||||
#endif
|
||||
return desc_bos;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -237,6 +244,7 @@ char const *string_desc_arr[] =
|
||||
"TinyUSB Speaker", // 2: Product
|
||||
NULL, // 3: Serials will use unique ID if possible
|
||||
"UAC2 Speaker", // 4: Audio Interface
|
||||
"UAC1 Speaker", // 5: UAC1 Audio Interface
|
||||
};
|
||||
|
||||
static uint16_t _desc_str[32 + 1];
|
||||
@ -247,10 +255,6 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
(void) langid;
|
||||
size_t chr_count;
|
||||
|
||||
#if CFG_QUIRK_OS_GUESSING
|
||||
quirk_os_guessing_desc_string_cb();
|
||||
#endif
|
||||
|
||||
switch ( index ) {
|
||||
case STRID_LANGID:
|
||||
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||
|
||||
@ -26,6 +26,10 @@
|
||||
#ifndef _USB_DESCRIPTORS_H_
|
||||
#define _USB_DESCRIPTORS_H_
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// UAC2 DESCRIPTOR TEMPLATES
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Defined in TUD_AUDIO20_SPEAKER_STEREO_FB_DESCRIPTOR
|
||||
#define UAC2_ENTITY_CLOCK 0x04
|
||||
#define UAC2_ENTITY_INPUT_TERMINAL 0x01
|
||||
@ -89,13 +93,13 @@
|
||||
#define UAC1_ENTITY_OUTPUT_TERMINAL 0x03
|
||||
|
||||
#define TUD_AUDIO10_SPEAKER_STEREO_FB_DESC_LEN(_nfreqs) (\
|
||||
+ TUD_AUDIO_DESC_STD_AC_LEN\
|
||||
+ TUD_AUDIO10_DESC_STD_AC_LEN\
|
||||
+ TUD_AUDIO10_DESC_CS_AC_LEN(1)\
|
||||
+ TUD_AUDIO10_DESC_INPUT_TERM_LEN\
|
||||
+ TUD_AUDIO10_DESC_OUTPUT_TERM_LEN\
|
||||
+ TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(2)\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_LEN\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_LEN\
|
||||
+ TUD_AUDIO10_DESC_STD_AS_LEN\
|
||||
+ TUD_AUDIO10_DESC_STD_AS_LEN\
|
||||
+ TUD_AUDIO10_DESC_CS_AS_INT_LEN\
|
||||
+ TUD_AUDIO10_DESC_TYPE_I_FORMAT_LEN(_nfreqs)\
|
||||
+ TUD_AUDIO10_DESC_STD_AS_ISO_EP_LEN\
|
||||
@ -124,9 +128,9 @@
|
||||
/* Type I Format Type Descriptor(2.2.5) */\
|
||||
TUD_AUDIO10_DESC_TYPE_I_FORMAT(/*_nrchannels*/ 0x02, /*_subframesize*/ _nBytesPerSample, /*_bitresolution*/ _nBitsUsedPerSample, /*_freqs*/ __VA_ARGS__),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.6.1.1) */\
|
||||
TUD_AUDIO10_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*/ _epoutsize, /*_interval*/ 0x01, /*_sync_ep*/ _epfb),\
|
||||
TUD_AUDIO10_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS), /*_maxEPsize*/ _epoutsize, /*_interval*/ 0x01, /*_sync_ep*/ _epfb),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.6.1.2) */\
|
||||
TUD_AUDIO10_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO10_CS_AS_ISO_DATA_EP_ATT_SAMPLING_FRQ, /*_lockdelayunits*/ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
|
||||
TUD_AUDIO10_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO10_CS_AS_ISO_DATA_EP_ATT_SAMPLING_FRQ, /*_lockdelayunits*/ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
|
||||
/* Standard AS Isochronous Synch Endpoint Descriptor (4.6.2.1) */\
|
||||
TUD_AUDIO10_DESC_STD_AS_ISO_SYNC_EP(/*_ep*/ _epfb, /*_bRefresh*/ 4)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user