From d89fc0772b0844703ae18912b8774df7c11d708c Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 3 Aug 2023 14:55:24 +0700 Subject: add makefile, add cdc_uac2 to cmake example list, update descriptor to build with nrf and samg/7x --- examples/device/CMakeLists.txt | 1 + examples/device/cdc_uac2/CMakeLists.txt | 16 +- examples/device/cdc_uac2/Makefile | 16 ++ examples/device/cdc_uac2/skip.txt | 52 ---- examples/device/cdc_uac2/src/cdc_app.c | 72 ++++++ examples/device/cdc_uac2/src/tusb_cdc.c | 72 ------ examples/device/cdc_uac2/src/tusb_uac2.c | 316 ------------------------- examples/device/cdc_uac2/src/uac2_app.c | 316 +++++++++++++++++++++++++ examples/device/cdc_uac2/src/usb_descriptors.c | 12 + 9 files changed, 425 insertions(+), 448 deletions(-) create mode 100644 examples/device/cdc_uac2/Makefile delete mode 100644 examples/device/cdc_uac2/skip.txt create mode 100644 examples/device/cdc_uac2/src/cdc_app.c delete mode 100644 examples/device/cdc_uac2/src/tusb_cdc.c delete mode 100644 examples/device/cdc_uac2/src/tusb_uac2.c create mode 100644 examples/device/cdc_uac2/src/uac2_app.c diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt index 5b077a5e1..f590fff3f 100644 --- a/examples/device/CMakeLists.txt +++ b/examples/device/CMakeLists.txt @@ -13,6 +13,7 @@ family_add_subdirectory(board_test) family_add_subdirectory(cdc_dual_ports) family_add_subdirectory(cdc_msc) family_add_subdirectory(cdc_msc_freertos) +family_add_subdirectory(cdc_uac2) family_add_subdirectory(dfu) family_add_subdirectory(dfu_runtime) family_add_subdirectory(dynamic_configuration) diff --git a/examples/device/cdc_uac2/CMakeLists.txt b/examples/device/cdc_uac2/CMakeLists.txt index efe8c74ba..64e4374e9 100644 --- a/examples/device/cdc_uac2/CMakeLists.txt +++ b/examples/device/cdc_uac2/CMakeLists.txt @@ -19,20 +19,20 @@ add_executable(${PROJECT}) # Example source target_sources(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/tusb_cdc.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/tusb_uac2.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c - ) + ${CMAKE_CURRENT_SOURCE_DIR}/src/cdc_app.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/uac2_app.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) # Example include target_include_directories(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src - ) + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) # Configure compilation flags and libraries for the example... see the corresponding function # in hw/bsp/FAMILY/family.cmake for details. -family_configure_device_example(${PROJECT}) +family_configure_device_example(${PROJECT} noos) # Uncomment me to enable UART based debugging # pico_enable_stdio_uart(${PROJECT} 1) diff --git a/examples/device/cdc_uac2/Makefile b/examples/device/cdc_uac2/Makefile new file mode 100644 index 000000000..b7a8302ce --- /dev/null +++ b/examples/device/cdc_uac2/Makefile @@ -0,0 +1,16 @@ +include ../../make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +EXAMPLE_SOURCE += \ + src/cdc_app.c \ + src/main.c \ + src/uac2_app.c \ + src/usb_descriptors.c \ + +SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) + +include ../../rules.mk diff --git a/examples/device/cdc_uac2/skip.txt b/examples/device/cdc_uac2/skip.txt deleted file mode 100644 index 8e70a5278..000000000 --- a/examples/device/cdc_uac2/skip.txt +++ /dev/null @@ -1,52 +0,0 @@ -board:curiosity_nano -board:frdm_kl25z -board:stm32l052dap52 -family:broadcom_32bit -family:broadcom_64bit -family:ch32v307 -family:fomu -family:gd32vf103 -family:kinetis_k32l -family:kinetis_kl -family:lpc11 -family:lpc13 -family:lpc15 -family:lpc17 -family:lpc51 -family:lpc54 -family:lpc55 -family:mm32 -family:msp430 -family:msp432e -family:msp432e4 -family:nrf -family:ra -family:rx -family:samd11 -family:samd21 -family:samd51 -family:same5x -family:saml2x -family:stm32f0 -family:stm32f1 -family:stm32f2 -family:stm32f3 -family:stm32f4 -family:stm32f7 -family:stm32g4 -family:stm32h7 -family:stm32l0 -family:stm32l4 -family:stm32u5 -family:stm32wb -family:tm4c123 -family:xmc4000 -mcu:LPC11UXX -mcu:LPC13XX -mcu:MKL25ZXX -mcu:MSP430x5xx -mcu:NUC121 -mcu:SAMD11 -mcu:SAME5X -mcu:SAMG -mcu:STM32L0 diff --git a/examples/device/cdc_uac2/src/cdc_app.c b/examples/device/cdc_uac2/src/cdc_app.c new file mode 100644 index 000000000..a95f638d9 --- /dev/null +++ b/examples/device/cdc_uac2/src/cdc_app.c @@ -0,0 +1,72 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2022 Angel Molina (angelmolinu@gmail.com) + * + * 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 "bsp/board.h" +#include "tusb.h" +#include "common.h" + +// Invoked when cdc when line state changed e.g connected/disconnected +void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) +{ + (void) itf; + (void) rts; + + if (dtr) + { + // Terminal connected + } + else + { + // Terminal disconnected + } +} + +// Invoked when CDC interface received data from host +void tud_cdc_rx_cb(uint8_t itf) +{ + uint8_t buf[64]; + uint32_t count; + + // connected() check for DTR bit + // Most but not all terminal client set this when making connection + if (tud_cdc_connected()) + { + if (tud_cdc_available()) // data is available + { + count = tud_cdc_n_read(itf, buf, sizeof(buf)); + (void) count; + + tud_cdc_n_write(itf, buf, count); + tud_cdc_n_write_flush(itf); + // dummy code to check that cdc serial is responding + board_led_write(0); + board_delay(50); + board_led_write(1); + board_delay(50); + board_led_write(0); + } + } +} diff --git a/examples/device/cdc_uac2/src/tusb_cdc.c b/examples/device/cdc_uac2/src/tusb_cdc.c deleted file mode 100644 index a95f638d9..000000000 --- a/examples/device/cdc_uac2/src/tusb_cdc.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * Copyright (c) 2022 Angel Molina (angelmolinu@gmail.com) - * - * 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 "bsp/board.h" -#include "tusb.h" -#include "common.h" - -// Invoked when cdc when line state changed e.g connected/disconnected -void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) -{ - (void) itf; - (void) rts; - - if (dtr) - { - // Terminal connected - } - else - { - // Terminal disconnected - } -} - -// Invoked when CDC interface received data from host -void tud_cdc_rx_cb(uint8_t itf) -{ - uint8_t buf[64]; - uint32_t count; - - // connected() check for DTR bit - // Most but not all terminal client set this when making connection - if (tud_cdc_connected()) - { - if (tud_cdc_available()) // data is available - { - count = tud_cdc_n_read(itf, buf, sizeof(buf)); - (void) count; - - tud_cdc_n_write(itf, buf, count); - tud_cdc_n_write_flush(itf); - // dummy code to check that cdc serial is responding - board_led_write(0); - board_delay(50); - board_led_write(1); - board_delay(50); - board_led_write(0); - } - } -} diff --git a/examples/device/cdc_uac2/src/tusb_uac2.c b/examples/device/cdc_uac2/src/tusb_uac2.c deleted file mode 100644 index 33dc33122..000000000 --- a/examples/device/cdc_uac2/src/tusb_uac2.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Jerzy Kasenberg - * Copyright (c) 2022 Angel Molina (angelmolinu@gmail.com) - * - * 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 -#include - -#include "bsp/board.h" -#include "tusb.h" -#include "usb_descriptors.h" -#include "common.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF PROTOTYPES -//--------------------------------------------------------------------+ - -// List of supported sample rates -const uint32_t sample_rates[] = {44100, 48000}; -uint32_t current_sample_rate = 44100; - -#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates) - -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 -int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 - -// Buffer for microphone data -int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4]; -// Buffer for speaker data -int32_t spk_buf[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 4]; -// Speaker data size received in the last frame -int spk_data_size; -// Resolution per format -const uint8_t resolutions_per_format[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = {CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX}; -// Current resolution, update on format change -uint8_t current_resolution; - -// Helper for clock get requests -static bool tud_audio_clock_get_request(uint8_t rhport, audio_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) - { - TU_LOG1("Clock get current freq %lu\r\n", current_sample_rate); - - audio_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 = - { - .wNumSubRanges = tu_htole16(N_SAMPLE_RATES) - }; - TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES); - for(uint8_t i = 0; i < N_SAMPLE_RATES; i++) - { - rangef.subrange[i].bMin = (int32_t) sample_rates[i]; - rangef.subrange[i].bMax = (int32_t) sample_rates[i]; - rangef.subrange[i].bRes = 0; - TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes); - } - - 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 }; - 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)); - } - TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); - 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; - - TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); - TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); - - if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) - { - TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t)); - - current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur; - - TU_LOG1("Clock set current freq: %ld\r\n", current_sample_rate); - - return true; - } - else - { - TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); - return false; - } -} - -// Helper for feature unit get requests -static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) -{ - TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_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] }; - 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 (UAC2_ENTITY_SPK_FEATURE_UNIT && request->bControlSelector == AUDIO_FU_CTRL_VOLUME) - { - if (request->bRequest == AUDIO_CS_REQ_RANGE) - { - audio_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]) }; - 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)); - } - } - TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); - - 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; - - TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); - TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); - - if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) - { - TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t)); - - mute[request->bChannelNumber] = ((audio_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)); - - volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur; - - TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256); - - return true; - } - else - { - TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); - return false; - } -} - -//--------------------------------------------------------------------+ -// Application Callback API Implementations -//--------------------------------------------------------------------+ - -// 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; - - 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); - } - 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; - - if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) - return tud_audio_feature_unit_set_request(rhport, request, buf); - if (request->bEntityID == UAC2_ENTITY_CLOCK) - return tud_audio_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); - - 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_SPK == itf && alt == 0) { - // Audio streaming stop - blink_interval_ms = BLINK_MOUNTED; - } - - return true; -} - -bool tud_audio_set_itf_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)); - - TU_LOG2("Set interface %d alt %d\r\n", itf, alt); - if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) { - // Audio streaming start - blink_interval_ms = BLINK_STREAMING; - } - - // Clear buffer when streaming format is changed - spk_data_size = 0; - if(alt != 0) - { - current_resolution = resolutions_per_format[alt-1]; - } - - return true; -} - -bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) -{ - (void)rhport; - (void)func_id; - (void)ep_out; - (void)cur_alt_setting; - - spk_data_size = tud_audio_read(spk_buf, n_bytes_received); - tud_audio_write(spk_buf, n_bytes_received); - - return true; -} - -bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void)rhport; - (void)itf; - (void)ep_in; - (void)cur_alt_setting; - - // This callback could be used to fill microphone data separately - return true; -} - -//--------------------------------------------------------------------+ -// BLINKING TASK -//--------------------------------------------------------------------+ -void led_blinking_task(void) -{ - static uint32_t start_ms = 0; - static bool led_state = false; - - // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) return; - start_ms += blink_interval_ms; - - board_led_write(led_state); - led_state = 1 - led_state; -} diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c new file mode 100644 index 000000000..33dc33122 --- /dev/null +++ b/examples/device/cdc_uac2/src/uac2_app.c @@ -0,0 +1,316 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Jerzy Kasenberg + * Copyright (c) 2022 Angel Molina (angelmolinu@gmail.com) + * + * 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 +#include + +#include "bsp/board.h" +#include "tusb.h" +#include "usb_descriptors.h" +#include "common.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTOTYPES +//--------------------------------------------------------------------+ + +// List of supported sample rates +const uint32_t sample_rates[] = {44100, 48000}; +uint32_t current_sample_rate = 44100; + +#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates) + +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 +int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 + +// Buffer for microphone data +int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4]; +// Buffer for speaker data +int32_t spk_buf[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 4]; +// Speaker data size received in the last frame +int spk_data_size; +// Resolution per format +const uint8_t resolutions_per_format[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = {CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX}; +// Current resolution, update on format change +uint8_t current_resolution; + +// Helper for clock get requests +static bool tud_audio_clock_get_request(uint8_t rhport, audio_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) + { + TU_LOG1("Clock get current freq %lu\r\n", current_sample_rate); + + audio_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 = + { + .wNumSubRanges = tu_htole16(N_SAMPLE_RATES) + }; + TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES); + for(uint8_t i = 0; i < N_SAMPLE_RATES; i++) + { + rangef.subrange[i].bMin = (int32_t) sample_rates[i]; + rangef.subrange[i].bMax = (int32_t) sample_rates[i]; + rangef.subrange[i].bRes = 0; + TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes); + } + + 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 }; + 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)); + } + TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", + request->bEntityID, request->bControlSelector, request->bRequest); + 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; + + TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); + TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); + + if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) + { + TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t)); + + current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur; + + TU_LOG1("Clock set current freq: %ld\r\n", current_sample_rate); + + return true; + } + else + { + TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", + request->bEntityID, request->bControlSelector, request->bRequest); + return false; + } +} + +// Helper for feature unit get requests +static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) +{ + TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_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] }; + 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 (UAC2_ENTITY_SPK_FEATURE_UNIT && request->bControlSelector == AUDIO_FU_CTRL_VOLUME) + { + if (request->bRequest == AUDIO_CS_REQ_RANGE) + { + audio_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]) }; + 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)); + } + } + TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", + request->bEntityID, request->bControlSelector, request->bRequest); + + 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; + + TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); + TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); + + if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) + { + TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t)); + + mute[request->bChannelNumber] = ((audio_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)); + + volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur; + + TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256); + + return true; + } + else + { + TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", + request->bEntityID, request->bControlSelector, request->bRequest); + return false; + } +} + +//--------------------------------------------------------------------+ +// Application Callback API Implementations +//--------------------------------------------------------------------+ + +// 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; + + 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); + } + 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; + + if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) + return tud_audio_feature_unit_set_request(rhport, request, buf); + if (request->bEntityID == UAC2_ENTITY_CLOCK) + return tud_audio_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); + + 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_SPK == itf && alt == 0) { + // Audio streaming stop + blink_interval_ms = BLINK_MOUNTED; + } + + return true; +} + +bool tud_audio_set_itf_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)); + + TU_LOG2("Set interface %d alt %d\r\n", itf, alt); + if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) { + // Audio streaming start + blink_interval_ms = BLINK_STREAMING; + } + + // Clear buffer when streaming format is changed + spk_data_size = 0; + if(alt != 0) + { + current_resolution = resolutions_per_format[alt-1]; + } + + return true; +} + +bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) +{ + (void)rhport; + (void)func_id; + (void)ep_out; + (void)cur_alt_setting; + + spk_data_size = tud_audio_read(spk_buf, n_bytes_received); + tud_audio_write(spk_buf, n_bytes_received); + + return true; +} + +bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) +{ + (void)rhport; + (void)itf; + (void)ep_in; + (void)cur_alt_setting; + + // This callback could be used to fill microphone data separately + return true; +} + +//--------------------------------------------------------------------+ +// BLINKING TASK +//--------------------------------------------------------------------+ +void led_blinking_task(void) +{ + static uint32_t start_ms = 0; + static bool led_state = false; + + // Blink every interval ms + if (board_millis() - start_ms < blink_interval_ms) return; + start_ms += blink_interval_ms; + + board_led_write(led_state); + led_state = 1 - led_state; +} diff --git a/examples/device/cdc_uac2/src/usb_descriptors.c b/examples/device/cdc_uac2/src/usb_descriptors.c index 1b2766829..58b711978 100644 --- a/examples/device/cdc_uac2/src/usb_descriptors.c +++ b/examples/device/cdc_uac2/src/usb_descriptors.c @@ -92,18 +92,30 @@ uint8_t const * tud_descriptor_device_cb(void) #define EPNUM_AUDIO_IN 0x08 #define EPNUM_AUDIO_OUT 0x08 + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x82 + #elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X // SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_AUDIO_IN 0x01 #define EPNUM_AUDIO_OUT 0x02 + #define EPNUM_CDC_NOTIF 0x83 + #define EPNUM_CDC_OUT 0x04 + #define EPNUM_CDC_IN 0x85 + #elif CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X // FT9XX doesn't support a same endpoint number with different direction IN and OUT // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_AUDIO_IN 0x01 #define EPNUM_AUDIO_OUT 0x02 + #define EPNUM_CDC_NOTIF 0x83 + #define EPNUM_CDC_OUT 0x04 + #define EPNUM_CDC_IN 0x85 + #else #define EPNUM_AUDIO_IN 0x01 #define EPNUM_AUDIO_OUT 0x01 -- cgit v1.3.1