summaryrefslogtreecommitdiff
path: root/examples/device/uac2_speaker_fb/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/device/uac2_speaker_fb/src/main.c')
-rw-r--r--examples/device/uac2_speaker_fb/src/main.c367
1 files changed, 287 insertions, 80 deletions
diff --git a/examples/device/uac2_speaker_fb/src/main.c b/examples/device/uac2_speaker_fb/src/main.c
index e742dc52a..f2cfb155e 100644
--- a/examples/device/uac2_speaker_fb/src/main.c
+++ b/examples/device/uac2_speaker_fb/src/main.c
@@ -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
@@ -82,7 +74,7 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
-int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
+uint8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];// +1 for master channel 0
// Buffer for speaker data
@@ -153,18 +145,189 @@ void tud_resume_cb(void) {
// Application Callback API Implementations
//--------------------------------------------------------------------+
-// Helper for clock get requests
-static bool tud_audio_clock_get_request(uint8_t rhport, audio_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 == AUDIO_CS_CTRL_SAM_FREQ) {
- if (request->bRequest == AUDIO_CS_REQ_CUR) {
+ if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) {
+ if (request->bRequest == AUDIO20_CS_REQ_CUR) {
TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate);
- audio_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
+ audio20_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf));
- } else if (request->bRequest == AUDIO_CS_REQ_RANGE) {
- audio_control_range_4_n_t(N_SAMPLE_RATES) rangef =
+ } else if (request->bRequest == AUDIO20_CS_REQ_RANGE) {
+ audio20_control_range_4_n_t(N_SAMPLE_RATES) rangef =
{
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)};
TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES);
@@ -177,9 +340,9 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, sizeof(rangef));
}
- } else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID &&
- request->bRequest == AUDIO_CS_REQ_CUR) {
- audio_control_cur_1_t cur_valid = {.bCur = 1};
+ } else if (request->bControlSelector == AUDIO20_CS_CTRL_CLK_VALID &&
+ request->bRequest == AUDIO20_CS_REQ_CUR) {
+ audio20_control_cur_1_t cur_valid = {.bCur = 1};
TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, sizeof(cur_valid));
}
@@ -188,17 +351,14 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t
return false;
}
-// Helper for clock set requests
-static bool tud_audio_clock_set_request(uint8_t rhport, audio_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 == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
- if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
- TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t));
+ if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) {
+ TU_VERIFY(request->wLength == sizeof(audio20_control_cur_4_t));
- current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *) buf)->bCur;
+ current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *) buf)->bCur;
TU_LOG1("Clock set current freq: %" PRIu32 "\r\n", current_sample_rate);
@@ -210,24 +370,23 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t
}
}
-// Helper for feature unit get requests
-static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_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 == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) {
- audio_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]};
+ if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE && request->bRequest == AUDIO20_CS_REQ_CUR) {
+ audio20_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]};
TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1));
- } else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
- if (request->bRequest == AUDIO_CS_REQ_RANGE) {
- audio_control_range_2_n_t(1) range_vol = {
+ } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) {
+ if (request->bRequest == AUDIO20_CS_REQ_RANGE) {
+ audio20_control_range_2_n_t(1) range_vol = {
.wNumSubRanges = tu_htole16(1),
.subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}};
TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber,
range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol));
- } else if (request->bRequest == AUDIO_CS_REQ_CUR) {
- audio_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])};
+ } else if (request->bRequest == AUDIO20_CS_REQ_CUR) {
+ audio20_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])};
TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_vol, sizeof(cur_vol));
}
@@ -238,25 +397,22 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req
return false;
}
-// Helper for feature unit set requests
-static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_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 == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
- if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) {
- TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t));
+ if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE) {
+ TU_VERIFY(request->wLength == sizeof(audio20_control_cur_1_t));
- mute[request->bChannelNumber] = ((audio_control_cur_1_t const *) buf)->bCur;
+ mute[request->bChannelNumber] = ((audio20_control_cur_1_t const *) buf)->bCur;
TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);
return true;
- } else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
- TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t));
+ } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) {
+ TU_VERIFY(request->wLength == sizeof(audio20_control_cur_2_t));
- volume[request->bChannelNumber] = ((audio_control_cur_2_t const *) buf)->bCur;
+ volume[request->bChannelNumber] = ((audio20_control_cur_2_t const *) buf)->bCur;
TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256);
@@ -268,14 +424,13 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req
}
}
-// 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) {
- audio_control_request_t const *request = (audio_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) {
- audio_control_request_t const *request = (audio_control_request_t const *) p_request;
+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
//--------------------------------------------------------------------+