diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 3beef45d0..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Bug Report -about: Create a report to help us improve -title: 'Please provide all details at least for Setup/Describe/Reproduce' -labels: Bug 🐞 -assignees: '' - ---- - -**Set Up** - -- **PC OS** e.g Ubuntu 20.04 / Windows 10 / macOS 10.15 -- **Board** e.g Feather nRF52840 Express (if custom specify your MCUs) -- **TinyUSB version** relase version or git hash (preferrably running with master for lastest code) -- **Firmware** e.g examples/device/cdc_msc - -**Describe The Bug** - -A clear and concise description of what the bug is. - -**To Reproduce** - -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. See error - -**Screenshots** - -If applicable, add screenshots, bus capture to help explain your problem. - -**Log** - -If applicable, provide the stack's log (uart/rtt/swo) where the issue occurred as attached txt file, best with comments to explain the actual events. -Note: To enable logging, add `LOG=2` to to the make command if building with stock examples or set `CFG_TUSB_DEBUG=2` in your tusb_config.h. More information can be found at [example's readme](/docs/getting_started.md) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..291b22079 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,74 @@ +name: Bug Report +description: Report a problem with TinyUSB +labels: 'Bug 🐞' +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + It's okay to leave some blank if it doesn't apply to your problem. + + - type: dropdown + attributes: + label: Operating System + options: + - Linux + - MacOS + - RaspberryPi OS + - Windows 7 + - Windows 10 + - Windows 11 + - Others + validations: + required: true + + - type: input + attributes: + label: Board + placeholder: e.g Feather nRF52840 Express + validations: + required: true + + - type: textarea + attributes: + label: Firmware + placeholder: | + e.g examples/device/cdc_msc. + If it is custom firmware, please provide links to your minimal sources or as attached files. + validations: + required: true + + - type: textarea + attributes: + label: What happened ? + placeholder: A clear and concise description of what the bug is. + validations: + required: true + + - type: textarea + attributes: + label: How to reproduce ? + placeholder: | + 1. Go to '...' + 2. Click on '....' + 3. See error + validations: + required: true + + - type: textarea + attributes: + label: Debug Log + placeholder: | + TinyUSB debug log where the issue occurred as attached txt file, best with comments to explain the actual events. + + Note: To enable logging, add `LOG=3` to to the make command if building with stock examples or set `CFG_TUSB_DEBUG=3` in your tusb_config.h. + More information can be found at [example's readme](https://github.com/hathach/tinyusb/blob/master/docs/getting_started.md) + validations: + required: false + + - type: textarea + attributes: + label: Screenshots + description: If applicable, add screenshots to help explain your problem. + validations: + required: false diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 529a77bb1..fc5b92b68 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,7 +49,7 @@ jobs: - 'samd11' - 'samd21' - 'samd51' - - 'saml22' + - 'saml2x' - 'stm32f0' - 'stm32f4' - 'stm32f7' diff --git a/.github/workflows/build_esp.yml b/.github/workflows/build_esp.yml index 08bb4f5d2..25f4e68f8 100644 --- a/.github/workflows/build_esp.yml +++ b/.github/workflows/build_esp.yml @@ -16,10 +16,10 @@ jobs: board: # Alphabetical order # ESP32-S2 - - 'adafruit_metro_esp32s2' - 'espressif_saola_1' # ESP32-S3 - - 'espressif_addax_1' + # latest IDF does not define USB0 in linker + #- 'espressif_addax_1' steps: - name: Setup Python diff --git a/.github/workflows/build_renesas.yml b/.github/workflows/build_renesas.yml index 45e0c5fd5..ee9ba8289 100644 --- a/.github/workflows/build_renesas.yml +++ b/.github/workflows/build_renesas.yml @@ -15,7 +15,7 @@ jobs: matrix: family: # Alphabetical order - - 'rx63n' + - 'rx' steps: - name: Setup Python uses: actions/setup-python@v2 diff --git a/README.md b/README.md index bea6d0d49..bbe3e0184 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ The stack supports the following MCUs: - **Dialog:** DA1469x - **Espressif:** ESP32-S2, ESP32-S3 -- **MicroChip:** SAMD11, SAMD21, SAMD51, SAME5x, SAMG55 +- **MicroChip:** SAMD11, SAMD21, SAMD51, SAME5x, SAMG55, SAML21, SAML22 - **NordicSemi:** nRF52833, nRF52840 - **Nuvoton:** NUC120, NUC121/NUC125, NUC126, NUC505 - **NXP:** @@ -40,7 +40,7 @@ The stack supports the following MCUs: - Kinetis: KL25 - LPC Series: 11u, 13, 15, 17, 18, 40, 43, 51u, 54, 55 - **Raspberry Pi:** RP2040 -- **Renesas:** RX63N +- **Renesas:** RX63N, RX65N - **Silabs:** EFM32GG12 - **Sony:** CXD56 - **ST:** STM32 series: L0, F0, F1, F2, F3, F4, F7, H7 both FullSpeed and HighSpeed @@ -106,6 +106,7 @@ TinyUSB is currently used by these other projects: - [Espressif IDF](https://github.com/espressif/esp-idf) - [MicroPython](https://github.com/micropython/micropython) - [mynewt](https://mynewt.apache.org) +- [openinput](https://github.com/openinput-fw/openinput) - [Raspberry Pi Pico SDK](https://github.com/raspberrypi/pico-sdk) - [TinyUF2 Bootloader](https://github.com/adafruit/tinyuf2) - [TinyUSB Arduino Library](https://github.com/adafruit/Adafruit_TinyUSB_Arduino) diff --git a/docs/boards.md b/docs/boards.md index ebba1f841..324500bd1 100644 --- a/docs/boards.md +++ b/docs/boards.md @@ -47,8 +47,9 @@ This code base already had supported for a handful of following boards (sorted a - [Microchip SAMG55 Xplained Pro](https://www.microchip.com/DevelopmentTools/ProductDetails/PartNO/ATSAMG55-XPRO) -### MicroChip SAML22 +### MicroChip SAML2x +- [SAML21 Xplaind Pro](https://www.microchip.com/DevelopmentTools/ProductDetails/ATSAML21-XPRO-B) - [SAML22 Feather](https://github.com/joeycastillo/Feather-Projects/tree/main/SAML22%20Feather) - [Sensor Watch](https://github.com/joeycastillo/Sensor-Watch) @@ -126,6 +127,7 @@ This code base already had supported for a handful of following boards (sorted a ### Renesas RX - [GR-CITRUS](https://www.renesas.com/us/en/products/gadget-renesas/boards/gr-citrus) +- [Renesas RX65N Target Board](https://www.renesas.com/us/en/products/microcontrollers-microprocessors/rx-32-bit-performance-efficiency-mcus/rtk5rx65n0c00000br-target-board-rx65n) ### Raspberry Pi RP2040 diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt index 4ed693cde..a0e4600f6 100644 --- a/examples/device/CMakeLists.txt +++ b/examples/device/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../hw/bsp/family_support.cmake) project(tinyusb_device_examples) family_initialize_project(tinyusb_device_examples ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/audio_4_channel_mic/CMakeLists.txt b/examples/device/audio_4_channel_mic/CMakeLists.txt index b653fc91f..f6e10e2ea 100644 --- a/examples/device/audio_4_channel_mic/CMakeLists.txt +++ b/examples/device/audio_4_channel_mic/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/audio_test/CMakeLists.txt b/examples/device/audio_test/CMakeLists.txt index fac0ca64f..cb321f9a8 100644 --- a/examples/device/audio_test/CMakeLists.txt +++ b/examples/device/audio_test/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/board_test/CMakeLists.txt b/examples/device/board_test/CMakeLists.txt index 8cd5b9ea4..37113578e 100644 --- a/examples/device/board_test/CMakeLists.txt +++ b/examples/device/board_test/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # Check for -DFAMILY= if(FAMILY MATCHES "^esp32s[2-3]") @@ -12,7 +12,6 @@ if(FAMILY MATCHES "^esp32s[2-3]") set(TOP "../../..") get_filename_component(TOP "${TOP}" REALPATH) - include(${TOP}/hw/bsp/${FAMILY}/family.cmake) project(${PROJECT}) else() diff --git a/examples/device/cdc_dual_ports/CMakeLists.txt b/examples/device/cdc_dual_ports/CMakeLists.txt index 18367a893..abc4d91da 100644 --- a/examples/device/cdc_dual_ports/CMakeLists.txt +++ b/examples/device/cdc_dual_ports/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/cdc_msc/CMakeLists.txt b/examples/device/cdc_msc/CMakeLists.txt index 5da761a04..fa6e83b7e 100644 --- a/examples/device/cdc_msc/CMakeLists.txt +++ b/examples/device/cdc_msc/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/cdc_msc_freertos/CMakeLists.txt b/examples/device/cdc_msc_freertos/CMakeLists.txt index 1d04a8518..639dde99a 100644 --- a/examples/device/cdc_msc_freertos/CMakeLists.txt +++ b/examples/device/cdc_msc_freertos/CMakeLists.txt @@ -1,20 +1,22 @@ cmake_minimum_required(VERSION 3.5) -# use BOARD-Directory name for project id -get_filename_component(PROJECT ${CMAKE_CURRENT_SOURCE_DIR} NAME) -set(PROJECT ${BOARD}-${PROJECT}) - # TOP is absolute path to root directory of TinyUSB git repo +# needed for esp32sx build. TOOD could be removed later on set(TOP "../../..") get_filename_component(TOP "${TOP}" REALPATH) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +# gets PROJECT name for the example (e.g. -) +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT}) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + # Check for -DFAMILY= if(FAMILY MATCHES "^esp32s[2-3]") - cmake_minimum_required(VERSION 3.5) - - include(${TOP}/hw/bsp/${FAMILY}/family.cmake) - project(${PROJECT}) - else() message(FATAL_ERROR "Invalid FAMILY specified: ${FAMILY}") endif() diff --git a/examples/device/cdc_msc_freertos/src/freertos_hook.c b/examples/device/cdc_msc_freertos/src/freertos_hook.c index 273ad46e6..ab885947c 100644 --- a/examples/device/cdc_msc_freertos/src/freertos_hook.c +++ b/examples/device/cdc_msc_freertos/src/freertos_hook.c @@ -94,7 +94,7 @@ void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, Stack *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; } -#if CFG_TUSB_MCU == OPT_MCU_RX63X +#if CFG_TUSB_MCU == OPT_MCU_RX63X | CFG_TUSB_MCU == OPT_MCU_RX65X #include "iodefine.h" void vApplicationSetupTimerInterrupt(void) { diff --git a/examples/device/dfu/CMakeLists.txt b/examples/device/dfu/CMakeLists.txt index 18367a893..abc4d91da 100644 --- a/examples/device/dfu/CMakeLists.txt +++ b/examples/device/dfu/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/dfu_runtime/CMakeLists.txt b/examples/device/dfu_runtime/CMakeLists.txt index 18367a893..abc4d91da 100644 --- a/examples/device/dfu_runtime/CMakeLists.txt +++ b/examples/device/dfu_runtime/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/dynamic_configuration/CMakeLists.txt b/examples/device/dynamic_configuration/CMakeLists.txt index 5da761a04..fa6e83b7e 100644 --- a/examples/device/dynamic_configuration/CMakeLists.txt +++ b/examples/device/dynamic_configuration/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/hid_composite/CMakeLists.txt b/examples/device/hid_composite/CMakeLists.txt index 18367a893..abc4d91da 100644 --- a/examples/device/hid_composite/CMakeLists.txt +++ b/examples/device/hid_composite/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/hid_composite/src/main.c b/examples/device/hid_composite/src/main.c index 512f6f9d9..d2a8a28fa 100644 --- a/examples/device/hid_composite/src/main.c +++ b/examples/device/hid_composite/src/main.c @@ -255,12 +255,31 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t // received data on OUT endpoint ( Report ID = 0, Type = 0 ) void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { - // TODO set LED based on CAPLOCK, NUMLOCK etc... (void) itf; - (void) report_id; - (void) report_type; - (void) buffer; - (void) bufsize; + + if (report_type == HID_REPORT_TYPE_OUTPUT) + { + // Set keyboard LED e.g Capslock, Numlock etc... + if (report_id == REPORT_ID_KEYBOARD) + { + // bufsize should be (at least) 1 + if ( bufsize < 1 ) return; + + uint8_t const kbd_leds = buffer[0]; + + if (kbd_leds & KEYBOARD_LED_CAPSLOCK) + { + // Capslock On: disable blink, turn led on + blink_interval_ms = 0; + board_led_write(true); + }else + { + // Caplocks Off: back to normal blink + board_led_write(false); + blink_interval_ms = BLINK_MOUNTED; + } + } + } } //--------------------------------------------------------------------+ @@ -271,6 +290,9 @@ void led_blinking_task(void) static uint32_t start_ms = 0; static bool led_state = false; + // blink is disabled + if (!blink_interval_ms) return; + // Blink every interval ms if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time start_ms += blink_interval_ms; diff --git a/examples/device/hid_composite_freertos/src/freertos_hook.c b/examples/device/hid_composite_freertos/src/freertos_hook.c index 273ad46e6..ab885947c 100644 --- a/examples/device/hid_composite_freertos/src/freertos_hook.c +++ b/examples/device/hid_composite_freertos/src/freertos_hook.c @@ -94,7 +94,7 @@ void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, Stack *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; } -#if CFG_TUSB_MCU == OPT_MCU_RX63X +#if CFG_TUSB_MCU == OPT_MCU_RX63X | CFG_TUSB_MCU == OPT_MCU_RX65X #include "iodefine.h" void vApplicationSetupTimerInterrupt(void) { diff --git a/examples/device/hid_composite_freertos/src/main.c b/examples/device/hid_composite_freertos/src/main.c index 47f976427..a61710858 100644 --- a/examples/device/hid_composite_freertos/src/main.c +++ b/examples/device/hid_composite_freertos/src/main.c @@ -315,12 +315,31 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t // received data on OUT endpoint ( Report ID = 0, Type = 0 ) void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { - // TODO set LED based on CAPLOCK, NUMLOCK etc... (void) itf; - (void) report_id; - (void) report_type; - (void) buffer; - (void) bufsize; + + if (report_type == HID_REPORT_TYPE_OUTPUT) + { + // Set keyboard LED e.g Capslock, Numlock etc... + if (report_id == REPORT_ID_KEYBOARD) + { + // bufsize should be (at least) 1 + if ( bufsize < 1 ) return; + + uint8_t const kbd_leds = buffer[0]; + + if (kbd_leds & KEYBOARD_LED_CAPSLOCK) + { + // Capslock On: disable blink, turn led on + xTimerStop(blinky_tm, portMAX_DELAY); + board_led_write(true); + }else + { + // Caplocks Off: back to normal blink + board_led_write(false); + xTimerStart(blinky_tm, portMAX_DELAY); + } + } + } } //--------------------------------------------------------------------+ diff --git a/examples/device/hid_generic_inout/CMakeLists.txt b/examples/device/hid_generic_inout/CMakeLists.txt index 18367a893..abc4d91da 100644 --- a/examples/device/hid_generic_inout/CMakeLists.txt +++ b/examples/device/hid_generic_inout/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/hid_multiple_interface/CMakeLists.txt b/examples/device/hid_multiple_interface/CMakeLists.txt index 18367a893..abc4d91da 100644 --- a/examples/device/hid_multiple_interface/CMakeLists.txt +++ b/examples/device/hid_multiple_interface/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/midi_test/CMakeLists.txt b/examples/device/midi_test/CMakeLists.txt index 18367a893..abc4d91da 100644 --- a/examples/device/midi_test/CMakeLists.txt +++ b/examples/device/midi_test/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/msc_dual_lun/CMakeLists.txt b/examples/device/msc_dual_lun/CMakeLists.txt index f60be8eda..9e834ae21 100644 --- a/examples/device/msc_dual_lun/CMakeLists.txt +++ b/examples/device/msc_dual_lun/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/net_lwip_webserver/CMakeLists.txt b/examples/device/net_lwip_webserver/CMakeLists.txt index f1f79b2d1..7500f6e9e 100644 --- a/examples/device/net_lwip_webserver/CMakeLists.txt +++ b/examples/device/net_lwip_webserver/CMakeLists.txt @@ -4,7 +4,7 @@ set(TOP "../../..") get_filename_component(TOP "${TOP}" REALPATH) if (EXISTS ${TOP}/lib/lwip/src) - include(${TOP}/hw/bsp/${FAMILY}/family.cmake) + include(${TOP}/hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/uac2_headset/.skip.MCU_LPC11UXX b/examples/device/uac2_headset/.skip.MCU_LPC11UXX new file mode 100644 index 000000000..e69de29bb diff --git a/examples/device/uac2_headset/.skip.MCU_LPC13XX b/examples/device/uac2_headset/.skip.MCU_LPC13XX new file mode 100644 index 000000000..e69de29bb diff --git a/examples/device/uac2_headset/.skip.MCU_NUC121 b/examples/device/uac2_headset/.skip.MCU_NUC121 new file mode 100644 index 000000000..e69de29bb diff --git a/examples/device/uac2_headset/CMakeLists.txt b/examples/device/uac2_headset/CMakeLists.txt index 18367a893..abc4d91da 100644 --- a/examples/device/uac2_headset/CMakeLists.txt +++ b/examples/device/uac2_headset/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c index 1b6a770a3..ba27350b4 100644 --- a/examples/device/uac2_headset/src/main.c +++ b/examples/device/uac2_headset/src/main.c @@ -34,9 +34,11 @@ // MACRO CONSTANT TYPEDEF PROTOTYPES //--------------------------------------------------------------------+ -#ifndef AUDIO_SAMPLE_RATE -#define AUDIO_SAMPLE_RATE 48000 -#endif +// List of supported sample rates +const uint32_t sample_rates[] = {44100, 48000, 88200, 96000}; +uint32_t current_sample_rate = 44100; + +#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates) /* Blink pattern * - 25 ms : streaming data @@ -76,11 +78,16 @@ int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master chan int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 // Buffer for microphone data -int16_t mic_buf[1000]; +int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4]; // Buffer for speaker data -int16_t spk_buf[1000]; +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, + CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX}; +// Current resolution, update on format change +uint8_t current_resolution; void led_blinking_task(void); void audio_task(void); @@ -135,55 +142,35 @@ void tud_resume_cb(void) blink_interval_ms = BLINK_MOUNTED; } -typedef struct TU_ATTR_PACKED -{ - union - { - struct TU_ATTR_PACKED - { - uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. - uint8_t type : 2; ///< Request type tusb_request_type_t. - uint8_t direction : 1; ///< Direction type. tusb_dir_t - } bmRequestType_bit; - - uint8_t bmRequestType; - }; - - uint8_t bRequest; ///< Request type audio_cs_req_t - uint8_t bChannelNumber; - uint8_t bControlSelector; - union - { - uint8_t bInterface; - uint8_t bEndpoint; - }; - uint8_t bEntityID; - uint16_t wLength; -} audio_control_request_t; - // 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); - // Example supports only single frequency, same value will be used for current value and range if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) { if (request->bRequest == AUDIO_CS_REQ_CUR) { - TU_LOG2("Clock get current freq %u\r\n", AUDIO_SAMPLE_RATE); + TU_LOG1("Clock get current freq %u\r\n", current_sample_rate); - audio_control_cur_4_t curf = { tu_htole32(AUDIO_SAMPLE_RATE) }; + audio_control_cur_4_t curf = { 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(1) rangef = + audio_control_range_4_n_t(N_SAMPLE_RATES) rangef = { - .wNumSubRanges = tu_htole16(1), - .subrange[0] = { tu_htole32(AUDIO_SAMPLE_RATE), tu_htole32(AUDIO_SAMPLE_RATE), 0} + .wNumSubRanges = tu_htole16(N_SAMPLE_RATES) }; - TU_LOG2("Clock get freq range (%d, %d, %d)\r\n", (int)rangef.subrange[0].bMin, (int)rangef.subrange[0].bMax, (int)rangef.subrange[0].bRes); + 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 = sample_rates[i]; + rangef.subrange[i].bMax = 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)); } } @@ -191,7 +178,7 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t request->bRequest == AUDIO_CS_REQ_CUR) { audio_control_cur_1_t cur_valid = { .bCur = 1 }; - TU_LOG2("Clock get is valid %u\r\n", cur_valid.bCur); + 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", @@ -199,6 +186,32 @@ 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; + + 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 = ((audio_control_cur_4_t *)buf)->bCur; + + TU_LOG1("Clock set current freq: %d\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) { @@ -207,7 +220,7 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) { audio_control_cur_1_t mute1 = { .bCur = mute[request->bChannelNumber] }; - TU_LOG2("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur); + 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) @@ -218,14 +231,14 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req .wNumSubRanges = tu_htole16(1), .subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) } }; - TU_LOG2("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber, + 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_LOG2("Get channel %u volume %u dB\r\n", request->bChannelNumber, cur_vol.bCur); + 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)); } } @@ -249,7 +262,7 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req mute[request->bChannelNumber] = ((audio_control_cur_1_t *)buf)->bCur; - TU_LOG2("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]); + TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]); return true; } @@ -259,7 +272,7 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur; - TU_LOG2("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256); + TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256); return true; } @@ -299,7 +312,8 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p 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); @@ -329,6 +343,13 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) 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; } @@ -362,20 +383,40 @@ void audio_task(void) { // When new data arrived, copy data from speaker buffer, to microphone buffer // and send it over + // Only support speaker & headphone both have the same resolution + // If one is 16bit another is 24bit be care of LOUD noise ! if (spk_data_size) { - int16_t *src = spk_buf; - int16_t *limit = spk_buf + spk_data_size / 2; - int16_t *dst = mic_buf; - while (src < limit) + if (current_resolution == 16) { - // Combine two channels into one - int32_t left = *src++; - int32_t right = *src++; - *dst++ = (int16_t)((left + right) / 2); + int16_t *src = (int16_t*)spk_buf; + int16_t *limit = (int16_t*)spk_buf + spk_data_size / 2; + int16_t *dst = (int16_t*)mic_buf; + while (src < limit) + { + // Combine two channels into one + int32_t left = *src++; + int32_t right = *src++; + *dst++ = (left >> 1) + (right >> 1); + } + tud_audio_write((uint8_t *)mic_buf, spk_data_size / 2); + spk_data_size = 0; + } + else if (current_resolution == 24) + { + int32_t *src = spk_buf; + int32_t *limit = spk_buf + spk_data_size / 4; + int32_t *dst = mic_buf; + while (src < limit) + { + // Combine two channels into one + int32_t left = *src++; + int32_t right = *src++; + *dst++ = ((left >> 1) + (right >> 1)) & 0xffffff00; + } + tud_audio_write((uint8_t *)mic_buf, spk_data_size / 2); + spk_data_size = 0; } - tud_audio_write((uint8_t *)mic_buf, spk_data_size / 2); - spk_data_size = 0; } } diff --git a/examples/device/uac2_headset/src/tusb_config.h b/examples/device/uac2_headset/src/tusb_config.h index 88f9efcff..d955a746d 100644 --- a/examples/device/uac2_headset/src/tusb_config.h +++ b/examples/device/uac2_headset/src/tusb_config.h @@ -93,33 +93,49 @@ extern "C" { //-------------------------------------------------------------------- // AUDIO CLASS DRIVER CONFIGURATION //-------------------------------------------------------------------- -#define CFG_TUD_AUDIO_IN_PATH (CFG_TUD_AUDIO) -#define CFG_TUD_AUDIO_OUT_PATH (CFG_TUD_AUDIO) -//#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN 220 // This equals TUD_AUDIO_HEADSET_STEREO_DESC_LEN, however, including it from usb_descriptors.h is not possible due to some strange include hassle -#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_HEADSET_STEREO_DESC_LEN +#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_HEADSET_STEREO_DESC_LEN + +// How many formats are used, need to adjust USB descriptor if changed +#define CFG_TUD_AUDIO_FUNC_1_N_FORMATS 2 // Audio format type I specifications -#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 -#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 -#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2 -#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX 2 -#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 +#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 96000 // 24bit/96kHz is the best quality for full-speed, high-speed is needed beyond this +#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 +#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2 + +// 16bit in 16bit slots +#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX 2 +#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX 16 +#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX 2 +#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX 16 + +// 24bit in 32bit slots +#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX 4 +#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX 24 +#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX 4 +#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX 24 // 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_IN 1 -#define CFG_TUD_AUDIO_EP_SZ_IN (CFG_TUD_AUDIO_IN_PATH * (48 + 1) * (CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX) * (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)) // 48 Samples (48 kHz) x 2 Bytes/Sample x n Channels -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN // Maximum EP IN size for all AS alternate settings used + +#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) +#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) + +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN)*2 +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used // 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 -#define CFG_TUD_AUDIO_EP_OUT_SZ (CFG_TUD_AUDIO_OUT_PATH * ((48 + CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * (CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX) * (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX))) // N Samples (N kHz) x 2 Bytes/Sample x n Channels -#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ CFG_TUD_AUDIO_EP_OUT_SZ*3 -#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX CFG_TUD_AUDIO_EP_OUT_SZ // Maximum EP IN size for all AS alternate settings used + +#define CFG_TUD_AUDIO_UNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX) +#define CFG_TUD_AUDIO_UNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX) + +#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_UNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_UNC_1_FORMAT_2_EP_SZ_OUT)*2 +#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_UNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_UNC_1_FORMAT_2_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used // Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) -#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1 +#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 2 // Size of control request buffer #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 diff --git a/examples/device/uac2_headset/src/usb_descriptors.c b/examples/device/uac2_headset/src/usb_descriptors.c index cd749eb65..6a01f8938 100644 --- a/examples/device/uac2_headset/src/usb_descriptors.c +++ b/examples/device/uac2_headset/src/usb_descriptors.c @@ -93,7 +93,7 @@ uint8_t const desc_configuration[] = TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), // Interface number, string index, EP Out & EP In address, EP size - TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, 2, 16, EPNUM_AUDIO, CFG_TUD_AUDIO_EP_OUT_SZ, EPNUM_AUDIO | 0x80, CFG_TUD_AUDIO_EP_SZ_IN) + TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, EPNUM_AUDIO, EPNUM_AUDIO | 0x80) }; // Invoked when received GET CONFIGURATION DESCRIPTOR diff --git a/examples/device/uac2_headset/src/usb_descriptors.h b/examples/device/uac2_headset/src/usb_descriptors.h index d9c5a63a7..d9510ea4f 100644 --- a/examples/device/uac2_headset/src/usb_descriptors.h +++ b/examples/device/uac2_headset/src/usb_descriptors.h @@ -55,21 +55,36 @@ enum + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ + TUD_AUDIO_DESC_INPUT_TERM_LEN\ + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ + /* Interface 1, Alternate 0 */\ + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + /* Interface 1, Alternate 0 */\ + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + TUD_AUDIO_DESC_CS_AS_INT_LEN\ + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\ + /* Interface 1, Alternate 2 */\ + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_CS_AS_INT_LEN\ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\ + /* Interface 2, Alternate 0 */\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + /* Interface 2, Alternate 1 */\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_CS_AS_INT_LEN\ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\ + /* Interface 2, Alternate 2 */\ + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + TUD_AUDIO_DESC_CS_AS_INT_LEN\ + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) - -#define TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(_stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epoutsize, _epin, _epinsize) \ +#define TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(_stridx, _epout, _epin) \ /* Standard Interface Association Descriptor (IAD) */\ TUD_AUDIO_DESC_IAD(/*_firstitfs*/ ITF_NUM_AUDIO_CONTROL, /*_nitfs*/ ITF_NUM_TOTAL, /*_stridx*/ 0x00),\ /* Standard AC Interface Descriptor(4.7.1) */\ @@ -77,13 +92,13 @@ enum /* Class-Specific AC Interface Header Descriptor(4.7.2) */\ TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\ /* Clock Source Descriptor(4.7.2.1) */\ - TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ 3, /*_ctrl*/ 5, /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ 3, /*_ctrl*/ 7, /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \ /* Input Terminal Descriptor(4.7.2.4) */\ TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\ /* Feature Unit Descriptor(4.7.2.8) */\ TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrlch0master*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch2*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_stridx*/ 0x00),\ /* Output Terminal Descriptor(4.7.2.5) */\ - TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_HEADPHONES, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ /* Input Terminal Descriptor(4.7.2.4) */\ TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\ /* Output Terminal Descriptor(4.7.2.5) */\ @@ -95,26 +110,46 @@ enum /* Interface 1, Alternate 1 - alternate interface for data streaming */\ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x05),\ /* Class-Specific AS Interface Descriptor(4.9.2) */\ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ - TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ + TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX),\ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ - TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ADAPTIVE | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epoutsize, /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 0x01),\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ADAPTIVE | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01),\ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\ + /* Interface 1, Alternate 2 - alternate interface for data streaming */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ 0x05),\ + /* Class-Specific AS Interface Descriptor(4.9.2) */\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ + TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX),\ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ADAPTIVE | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01),\ /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\ /* Standard AS Interface Descriptor(4.9.1) */\ /* Interface 2, Alternate 0 - default alternate setting with 0 bandwidth */\ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x04),\ /* Standard AS Interface Descriptor(4.9.1) */\ - /* Interface 1, Alternate 1 - alternate interface for data streaming */\ + /* Interface 2, Alternate 1 - alternate interface for data streaming */\ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x04),\ /* Class-Specific AS Interface Descriptor(4.9.2) */\ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ - TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ + TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX),\ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ - TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epinsize, /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 0x01),\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01),\ /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ - TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)\ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\ + /* Interface 2, Alternate 2 - alternate interface for data streaming */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ 0x04),\ + /* Class-Specific AS Interface Descriptor(4.9.2) */\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ + TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX),\ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01),\ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) #endif diff --git a/examples/device/usbtmc/CMakeLists.txt b/examples/device/usbtmc/CMakeLists.txt index 4306da8cb..c49603c26 100644 --- a/examples/device/usbtmc/CMakeLists.txt +++ b/examples/device/usbtmc/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/device/webusb_serial/CMakeLists.txt b/examples/device/webusb_serial/CMakeLists.txt index 18367a893..abc4d91da 100644 --- a/examples/device/webusb_serial/CMakeLists.txt +++ b/examples/device/webusb_serial/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/host/CMakeLists.txt b/examples/host/CMakeLists.txt index c70d11d5b..f185ac4f8 100644 --- a/examples/host/CMakeLists.txt +++ b/examples/host/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../hw/bsp/family_support.cmake) project(tinyusb_host_examples) family_initialize_project(tinyusb_host_examples ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/host/cdc_msc_hid/CMakeLists.txt b/examples/host/cdc_msc_hid/CMakeLists.txt index ad0f412e2..0a99bc3a9 100644 --- a/examples/host/cdc_msc_hid/CMakeLists.txt +++ b/examples/host/cdc_msc_hid/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/${FAMILY}/family.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) diff --git a/examples/host/cdc_msc_hid/src/hid_app.c b/examples/host/cdc_msc_hid/src/hid_app.c index 23ab12404..420600f9a 100644 --- a/examples/host/cdc_msc_hid/src/hid_app.c +++ b/examples/host/cdc_msc_hid/src/hid_app.c @@ -30,7 +30,8 @@ // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ -// If your host terminal support ansi escape code, it can be use to simulate mouse cursor +// If your host terminal support ansi escape code such as TeraTerm +// it can be use to simulate mouse cursor movement within terminal #define USE_ANSI_ESCAPE 0 #define MAX_REPORT 4 @@ -38,11 +39,15 @@ static uint8_t const keycode2ascii[128][2] = { HID_KEYCODE_TO_ASCII }; // Each HID instance can has multiple reports -static uint8_t _report_count[CFG_TUH_HID]; -static tuh_hid_report_info_t _report_info_arr[CFG_TUH_HID][MAX_REPORT]; +static struct +{ + uint8_t report_count; + tuh_hid_report_info_t report_info[MAX_REPORT]; +}hid_info[CFG_TUH_HID]; static void process_kbd_report(hid_keyboard_report_t const *report); static void process_mouse_report(hid_mouse_report_t const * report); +static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len); void hid_app_task(void) { @@ -60,13 +65,19 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re { printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance); - // Interface protocol - const char* protocol_str[] = { "None", "Keyboard", "Mouse" }; // hid_protocol_type_t - uint8_t const interface_protocol = tuh_hid_interface_protocol(dev_addr, instance); + // Interface protocol (hid_interface_protocol_enum_t) + const char* protocol_str[] = { "None", "Keyboard", "Mouse" }; + uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); - // Parse report descriptor with built-in parser - _report_count[instance] = tuh_hid_parse_report_descriptor(_report_info_arr[instance], MAX_REPORT, desc_report, desc_len); - printf("HID has %u reports and interface protocol = %s\r\n", _report_count[instance], protocol_str[interface_protocol]); + printf("HID Interface Protocol = %s\r\n", protocol_str[itf_protocol]); + + // By default host stack will use activate boot protocol on supported interface. + // Therefore for this simple example, we only need to parse generic report descriptor (with built-in parser) + if ( itf_protocol == HID_ITF_PROTOCOL_NONE ) + { + hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT, desc_report, desc_len); + printf("HID has %u reports \r\n", hid_info[instance].report_count); + } } // Invoked when device with hid interface is un-mounted @@ -78,59 +89,24 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) // Invoked when received report from device via interrupt endpoint void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) { - (void) dev_addr; + uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); - uint8_t const rpt_count = _report_count[instance]; - tuh_hid_report_info_t* rpt_info_arr = _report_info_arr[instance]; - tuh_hid_report_info_t* rpt_info = NULL; - - if ( rpt_count == 1 && rpt_info_arr[0].report_id == 0) + switch (itf_protocol) { - // Simple report without report ID as 1st byte - rpt_info = &rpt_info_arr[0]; - }else - { - // Composite report, 1st byte is report ID, data starts from 2nd byte - uint8_t const rpt_id = report[0]; + case HID_ITF_PROTOCOL_KEYBOARD: + TU_LOG2("HID receive boot keyboard report\r\n"); + process_kbd_report( (hid_keyboard_report_t const*) report ); + break; - // Find report id in the arrray - for(uint8_t i=0; iusage_page == HID_USAGE_PAGE_DESKTOP ) - { - switch (rpt_info->usage) - { - case HID_USAGE_DESKTOP_KEYBOARD: - TU_LOG1("HID receive keyboard report\r\n"); - // Assume keyboard follow boot report layout - process_kbd_report( (hid_keyboard_report_t const*) report ); - break; - - case HID_USAGE_DESKTOP_MOUSE: - TU_LOG1("HID receive mouse report\r\n"); - // Assume mouse follow boot report layout - process_mouse_report( (hid_mouse_report_t const*) report ); - break; - - default: break; - } + default: + // Generic report requires matching ReportID and contents with previous parsed report info + process_generic_report(dev_addr, instance, report, len); + break; } } @@ -164,7 +140,7 @@ static void process_kbd_report(hid_keyboard_report_t const *report) }else { // not existed in previous report means the current key is pressed - bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT); + bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT); uint8_t ch = keycode2ascii[report->keycode[i]][is_shift ? 1 : 0]; putchar(ch); if ( ch == '\r' ) putchar('\n'); // added new line for enter key @@ -235,3 +211,71 @@ static void process_mouse_report(hid_mouse_report_t const * report) //------------- cursor movement -------------// cursor_movement(report->x, report->y, report->wheel); } + +//--------------------------------------------------------------------+ +// Generic Report +//--------------------------------------------------------------------+ +static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) +{ + (void) dev_addr; + + uint8_t const rpt_count = hid_info[instance].report_count; + tuh_hid_report_info_t* rpt_info_arr = hid_info[instance].report_info; + tuh_hid_report_info_t* rpt_info = NULL; + + if ( rpt_count == 1 && rpt_info_arr[0].report_id == 0) + { + // Simple report without report ID as 1st byte + rpt_info = &rpt_info_arr[0]; + }else + { + // Composite report, 1st byte is report ID, data starts from 2nd byte + uint8_t const rpt_id = report[0]; + + // Find report id in the arrray + for(uint8_t i=0; iusage_page == HID_USAGE_PAGE_DESKTOP ) + { + switch (rpt_info->usage) + { + case HID_USAGE_DESKTOP_KEYBOARD: + TU_LOG1("HID receive keyboard report\r\n"); + // Assume keyboard follow boot report layout + process_kbd_report( (hid_keyboard_report_t const*) report ); + break; + + case HID_USAGE_DESKTOP_MOUSE: + TU_LOG1("HID receive mouse report\r\n"); + // Assume mouse follow boot report layout + process_mouse_report( (hid_mouse_report_t const*) report ); + break; + + default: break; + } + } +} diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h index 8253964c0..b574acf9b 100644 --- a/examples/host/cdc_msc_hid/src/tusb_config.h +++ b/examples/host/cdc_msc_hid/src/tusb_config.h @@ -72,11 +72,11 @@ //-------------------------------------------------------------------- // Size of buffer to hold descriptors and other data used for enumeration -#define CFG_TUH_ENUMERATION_BUFSZIE 256 +#define CFG_TUH_ENUMERATION_BUFSIZE 256 #define CFG_TUH_HUB 1 #define CFG_TUH_CDC 1 -#define CFG_TUH_HID 2 +#define CFG_TUH_HID 4 #define CFG_TUH_MSC 1 #define CFG_TUH_VENDOR 0 diff --git a/hw/bsp/board.c b/hw/bsp/board.c index 383a02ef4..6a26f55b7 100644 --- a/hw/bsp/board.c +++ b/hw/bsp/board.c @@ -25,7 +25,60 @@ #include "board.h" -#if defined(__MSP430__) +#if 0 +#define LED_PHASE_MAX 8 + +static struct +{ + uint32_t phase[LED_PHASE_MAX]; + uint8_t phase_count; + + bool led_state; + uint8_t current_phase; + uint32_t current_ms; +}led_pattern; + +void board_led_pattern(uint32_t const phase_ms[], uint8_t count) +{ + memcpy(led_pattern.phase, phase_ms, 4*count); + led_pattern.phase_count = count; + + // reset with 1st phase is on + led_pattern.current_ms = board_millis(); + led_pattern.current_phase = 0; + led_pattern.led_state = true; + board_led_on(); +} + +void board_led_task(void) +{ + if ( led_pattern.phase_count == 0 ) return; + + uint32_t const duration = led_pattern.phase[led_pattern.current_phase]; + + // return if not enough time + if (board_millis() - led_pattern.current_ms < duration) return; + + led_pattern.led_state = !led_pattern.led_state; + board_led_write(led_pattern.led_state); + + led_pattern.current_ms += duration; + led_pattern.current_phase++; + + if (led_pattern.current_phase == led_pattern.phase_count) + { + led_pattern.current_phase = 0; + led_pattern.led_state = true; + board_led_on(); + } +} +#endif + +//--------------------------------------------------------------------+ +// newlib read()/write() retarget +//--------------------------------------------------------------------+ + +#if defined(__MSP430__) || defined(__RX__) #define sys_write write #define sys_read read #else @@ -33,10 +86,6 @@ #define sys_read _read #endif -//--------------------------------------------------------------------+ -// newlib read()/write() retarget -//--------------------------------------------------------------------+ - #if defined(LOGGER_RTT) // Logging with RTT diff --git a/hw/bsp/board.h b/hw/bsp/board.h index 7b77def50..782e0939c 100644 --- a/hw/bsp/board.h +++ b/hw/bsp/board.h @@ -54,6 +54,10 @@ void board_init(void); // Turn LED on or off void board_led_write(bool state); +// Control led pattern using phase duration in ms. +// For each phase, LED is toggle then repeated, board_led_task() is required to be called +//void board_led_pattern(uint32_t const phase_ms[], uint8_t count); + // Get the current state of button // a '1' means active (pressed), a '0' means inactive. uint32_t board_button_read(void); @@ -81,11 +85,12 @@ int board_uart_write(void const * buf, int len); } #elif CFG_TUSB_OS == OPT_OS_PICO -#include "pico/time.h" -static inline uint32_t board_millis(void) + #include "pico/time.h" + static inline uint32_t board_millis(void) { return to_ms_since_boot(get_absolute_time()); } + #elif CFG_TUSB_OS == OPT_OS_RTTHREAD static inline uint32_t board_millis(void) { diff --git a/hw/bsp/board_mcu.h b/hw/bsp/board_mcu.h index 1a659c410..9f6b489ea 100644 --- a/hw/bsp/board_mcu.h +++ b/hw/bsp/board_mcu.h @@ -54,7 +54,7 @@ #elif CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \ CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X || \ - CFG_TUSB_MCU == OPT_MCU_SAML22 + CFG_TUSB_MCU == OPT_MCU_SAML22 || CFG_TUSB_MCU == OPT_MCU_SAML21 #include "sam.h" #elif CFG_TUSB_MCU == OPT_MCU_SAMG @@ -127,7 +127,7 @@ #elif CFG_TUSB_MCU == OPT_MCU_EFM32GG || CFG_TUSB_MCU == OPT_MCU_EFM32GG11 || CFG_TUSB_MCU == OPT_MCU_EFM32GG12 #include "em_device.h" -#elif CFG_TUSB_MCU == OPT_MCU_RX63X +#elif CFG_TUSB_MCU == OPT_MCU_RX63X || CFG_TUSB_MCU == OPT_MCU_RX65X // no header needed #else diff --git a/hw/bsp/esp32s2/family.cmake b/hw/bsp/esp32s2/family.cmake index 9eb1627a1..f3d41d041 100644 --- a/hw/bsp/esp32s2/family.cmake +++ b/hw/bsp/esp32s2/family.cmake @@ -4,7 +4,4 @@ cmake_minimum_required(VERSION 3.5) set(EXTRA_COMPONENT_DIRS "src" "${TOP}/hw/bsp/esp32s2/boards" "${TOP}/hw/bsp/esp32s2/components") include($ENV{IDF_PATH}/tools/cmake/project.cmake) set(SUPPORTED_TARGETS esp32s2) - -# include basic family CMake functionality set(FAMILY_MCUS ESP32S2) -include(${CMAKE_CURRENT_LIST_DIR}/../family_common.cmake) diff --git a/hw/bsp/esp32s3/family.cmake b/hw/bsp/esp32s3/family.cmake index c99c3728b..511dd58bb 100644 --- a/hw/bsp/esp32s3/family.cmake +++ b/hw/bsp/esp32s3/family.cmake @@ -4,7 +4,4 @@ cmake_minimum_required(VERSION 3.5) set(EXTRA_COMPONENT_DIRS "src" "${TOP}/hw/bsp/esp32s3/boards" "${TOP}/hw/bsp/esp32s3/components") include($ENV{IDF_PATH}/tools/cmake/project.cmake) set(SUPPORTED_TARGETS esp32s3) - -# include basic family CMake functionality set(FAMILY_MCUS ESP32S3) -include(${CMAKE_CURRENT_LIST_DIR}/../family_common.cmake) diff --git a/hw/bsp/family_common.cmake b/hw/bsp/family_common.cmake deleted file mode 100644 index 2544c58c6..000000000 --- a/hw/bsp/family_common.cmake +++ /dev/null @@ -1,57 +0,0 @@ -if (NOT FAMILY_MCUS) - set(FAMILY_MCUS ${FAMILY}) -endif() - -# save it in case of re-inclusion -set(FAMILY_MCUS ${FAMILY_MCUS} CACHE INTERNAL "") - -function(family_filter RESULT DIR) - get_filename_component(DIR ${DIR} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) - file(GLOB ONLYS "${DIR}/.only.MCU_*") - if (ONLYS) - foreach(MCU IN LISTS FAMILY_MCUS) - if (EXISTS ${DIR}/.only.MCU_${MCU}) - set(${RESULT} 1 PARENT_SCOPE) - return() - endif() - endforeach() - else() - foreach(MCU IN LISTS FAMILY_MCUS) - if (EXISTS ${DIR}/.skip.MCU_${MCU}) - set(${RESULT} 0 PARENT_SCOPE) - return() - endif() - endforeach() - endif() - set(${RESULT} 1 PARENT_SCOPE) -endfunction() - -function(family_add_subdirectory DIR) - family_filter(SHOULD_ADD "${DIR}") - if (SHOULD_ADD) - add_subdirectory(${DIR}) - endif() -endfunction() - -function(family_get_project_name OUTPUT_NAME DIR) - get_filename_component(SHORT_NAME ${DIR} NAME) - set(${OUTPUT_NAME} ${TINYUSB_FAMILY_PROJECT_NAME_PREFIX}${SHORT_NAME} PARENT_SCOPE) -endfunction() - -function(family_initialize_project PROJECT DIR) - family_filter(ALLOWED "${DIR}") - if (NOT ALLOWED) - get_filename_component(SHORT_NAME ${DIR} NAME) - message(FATAL_ERROR "${SHORT_NAME} is not supported on FAMILY=${FAMILY}") - endif() -endfunction() - -# configure an executable target to link to tinyusb in device mode, and add the board implementation -function(family_configure_device_example TARGET) - # default implentation is empty, the function should be redefined in the FAMILY/family.cmake -endfunction() - -# configure an executable target to link to tinyusb in host mode, and add the board implementation -function(family_configure_host_example TARGET) - # default implentation is empty, the function should be redefined in the FAMILY/family.cmake -endfunction() diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake new file mode 100644 index 000000000..af0e00b2d --- /dev/null +++ b/hw/bsp/family_support.cmake @@ -0,0 +1,71 @@ +if (NOT TARGET _family_support_marker) + add_library(_family_support_marker INTERFACE) + + if (NOT FAMILY) + message(FATAL_ERROR "You must set a FAMILY variable for the build (e.g. rp2040, eps32s2, esp32s3). You can do this via -DFAMILY=xxx on the camke command line") + endif() + + if (NOT EXISTS ${CMAKE_CURRENT_LIST_DIR}/${FAMILY}/family.cmake) + message(FATAL_ERROR "Family '${FAMILY}' is not known/supported") + endif() + + function(family_filter RESULT DIR) + get_filename_component(DIR ${DIR} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + file(GLOB ONLYS "${DIR}/.only.MCU_*") + if (ONLYS) + foreach(MCU IN LISTS FAMILY_MCUS) + if (EXISTS ${DIR}/.only.MCU_${MCU}) + set(${RESULT} 1 PARENT_SCOPE) + return() + endif() + endforeach() + else() + foreach(MCU IN LISTS FAMILY_MCUS) + if (EXISTS ${DIR}/.skip.MCU_${MCU}) + set(${RESULT} 0 PARENT_SCOPE) + return() + endif() + endforeach() + endif() + set(${RESULT} 1 PARENT_SCOPE) + endfunction() + + function(family_add_subdirectory DIR) + family_filter(SHOULD_ADD "${DIR}") + if (SHOULD_ADD) + add_subdirectory(${DIR}) + endif() + endfunction() + + function(family_get_project_name OUTPUT_NAME DIR) + get_filename_component(SHORT_NAME ${DIR} NAME) + set(${OUTPUT_NAME} ${TINYUSB_FAMILY_PROJECT_NAME_PREFIX}${SHORT_NAME} PARENT_SCOPE) + endfunction() + + function(family_initialize_project PROJECT DIR) + family_filter(ALLOWED "${DIR}") + if (NOT ALLOWED) + get_filename_component(SHORT_NAME ${DIR} NAME) + message(FATAL_ERROR "${SHORT_NAME} is not supported on FAMILY=${FAMILY}") + endif() + endfunction() + + # configure an executable target to link to tinyusb in device mode, and add the board implementation + function(family_configure_device_example TARGET) + # default implentation is empty, the function should be redefined in the FAMILY/family.cmake + endfunction() + + # configure an executable target to link to tinyusb in host mode, and add the board implementation + function(family_configure_host_example TARGET) + # default implentation is empty, the function should be redefined in the FAMILY/family.cmake + endfunction() + + include(${CMAKE_CURRENT_LIST_DIR}/${FAMILY}/family.cmake) + + if (NOT FAMILY_MCUS) + set(FAMILY_MCUS ${FAMILY}) + endif() + + # save it in case of re-inclusion + set(FAMILY_MCUS ${FAMILY_MCUS} CACHE INTERNAL "") +endif() \ No newline at end of file diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index 752eb0cab..41960b6cd 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -3,7 +3,8 @@ if (NOT TARGET _rp2040_family_inclusion_marker) add_library(_rp2040_family_inclusion_marker INTERFACE) if (NOT BOARD) - message(FATAL_ERROR "BOARD must be specified") + message("BOARD not specified, defaulting to pico_sdk") + set(BOARD pico_sdk) endif() # add the SDK in case we are standalone tinyusb example (noop if already present) @@ -11,7 +12,6 @@ if (NOT TARGET _rp2040_family_inclusion_marker) # include basic family CMake functionality set(FAMILY_MCUS RP2040) - include(${CMAKE_CURRENT_LIST_DIR}/../family_common.cmake) include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake) diff --git a/hw/bsp/rx/boards/gr_citrus/board.mk b/hw/bsp/rx/boards/gr_citrus/board.mk new file mode 100644 index 000000000..0eba94610 --- /dev/null +++ b/hw/bsp/rx/boards/gr_citrus/board.mk @@ -0,0 +1,24 @@ +DEPS_SUBMODULES += hw/mcu/renesas/rx + +CFLAGS += \ + -mcpu=rx610 \ + -misa=v1 \ + -DCFG_TUSB_MCU=OPT_MCU_RX63X + +MCU_DIR = hw/mcu/renesas/rx/rx63n + +# All source paths should be relative to the top level. +LD_FILE = $(BOARD_PATH)/r5f5631fd.ld + +# For freeRTOS port source +FREERTOS_PORT = RX600 + +# For flash-jlink target +JLINK_DEVICE = R5F5631F +JLINK_IF = JTAG + +# For flash-pyocd target +PYOCD_TARGET = + +# flash using jlink +flash: flash-jlink diff --git a/hw/bsp/rx63n/boards/gr_citrus/gr_citrus.c b/hw/bsp/rx/boards/gr_citrus/gr_citrus.c similarity index 87% rename from hw/bsp/rx63n/boards/gr_citrus/gr_citrus.c rename to hw/bsp/rx/boards/gr_citrus/gr_citrus.c index 6697012a7..caf5fd6fa 100644 --- a/hw/bsp/rx63n/boards/gr_citrus/gr_citrus.c +++ b/hw/bsp/rx/boards/gr_citrus/gr_citrus.c @@ -34,26 +34,22 @@ * * The pads are [the back side of GR-CITRUS](https://www.slideshare.net/MinaoYamamoto/grcitrusrx631/2). * - * Connet the pins between GR-CITRUS and JLink as follows. + * Connect the pins between GR-CITRUS and JLink as follows. * - * | JTAG Function | GR-CITRUS pin name| JLink pin No.| note | - * |:-------------:|:-----------------:|:------------:|:--------:| - * | VTref | 3.3V | 1 | | - * | TRST | 5 | 3 | | - * | GND | GND | 4 | | - * | TDI | 3 | 5 | | - * | TMS | 2 | 7 | | - * | TCK | 14 | 9 | short J4 | - * | TDO | 9 | 13 | short J5 | - * | nRES | RST | 15 | | + * | Function | GR-CITRUS pin | JLink pin No.| note | + * |:---------:|:-------------:|:------------:|:--------:| + * | VTref | 3.3V | 1 | | + * | TRST | 5 | 3 | | + * | GND | GND | 4 | | + * | TDI | 3 | 5 | | + * | TMS | 2 | 7 | | + * | TCK/FINEC | 14 | 9 | short J4 | + * | TDO | 9 | 13 | short J5 | + * | nRES | RST | 15 | | * * JLink firmware needs to update to V6.96 or newer version to avoid * [a bug](https://forum.segger.com/index.php/Thread/7758-SOLVED-Bug-in-JLink-from-V6-88b-regarding-RX65N) * regarding downloading. - * - * When using SEGGER RTT, `RX_NEWLIB=0` should be added to make command arguments. - * The option is used to change the C runtime library to `optlib` from `newlib`. - * RTT may not work with `newlib`. */ #include "../board.h" @@ -253,3 +249,27 @@ uint32_t board_millis(void) #else uint32_t SystemCoreClock = 96000000; #endif + +int close(int fd) +{ + (void)fd; + return -1; +} +int fstat(int fd, void *pstat) +{ + (void)fd; + (void)pstat; + return 0; +} +off_t lseek(int fd, off_t pos, int whence) +{ + (void)fd; + (void)pos; + (void)whence; + return 0; +} +int isatty(int fd) +{ + (void)fd; + return 1; +} diff --git a/hw/bsp/rx63n/boards/gr_citrus/hwinit.c b/hw/bsp/rx/boards/gr_citrus/hwinit.c similarity index 100% rename from hw/bsp/rx63n/boards/gr_citrus/hwinit.c rename to hw/bsp/rx/boards/gr_citrus/hwinit.c diff --git a/hw/bsp/rx63n/boards/gr_citrus/r5f5631fd.ld b/hw/bsp/rx/boards/gr_citrus/r5f5631fd.ld similarity index 96% rename from hw/bsp/rx63n/boards/gr_citrus/r5f5631fd.ld rename to hw/bsp/rx/boards/gr_citrus/r5f5631fd.ld index fa8142936..bb9c297c7 100644 --- a/hw/bsp/rx63n/boards/gr_citrus/r5f5631fd.ld +++ b/hw/bsp/rx/boards/gr_citrus/r5f5631fd.ld @@ -1,5 +1,5 @@ -__USTACK_SIZE = 0x00000200; -__ISTACK_SIZE = 0x00000200; +__USTACK_SIZE = 0x00000400; +__ISTACK_SIZE = 0x00000400; MEMORY { diff --git a/hw/bsp/rx/boards/rx65n_target/board.mk b/hw/bsp/rx/boards/rx65n_target/board.mk new file mode 100644 index 000000000..fc76d79fa --- /dev/null +++ b/hw/bsp/rx/boards/rx65n_target/board.mk @@ -0,0 +1,25 @@ +CFLAGS += \ + -mcpu=rx64m \ + -misa=v2 \ + -DCFG_TUSB_MCU=OPT_MCU_RX65X \ + -DIR_USB0_USBI0=IR_PERIB_INTB185 \ + -DIER_USB0_USBI0=IER_PERIB_INTB185 \ + -DIEN_USB0_USBI0=IEN_PERIB_INTB185 + +MCU_DIR = hw/mcu/renesas/rx/rx65n + +# All source paths should be relative to the top level. +LD_FILE = $(BOARD_PATH)/r5f565ne.ld + +# For freeRTOS port source +FREERTOS_PORT = RX600 + +# For flash-jlink target +JLINK_DEVICE = R5F565NE +JLINK_IF = JTAG + +# For flash-pyocd target +PYOCD_TARGET = + +# flash using rfp-cli +flash: flash-rfp diff --git a/hw/bsp/rx/boards/rx65n_target/r5f565ne.ld b/hw/bsp/rx/boards/rx65n_target/r5f565ne.ld new file mode 100644 index 000000000..8e5617f23 --- /dev/null +++ b/hw/bsp/rx/boards/rx65n_target/r5f565ne.ld @@ -0,0 +1,168 @@ +__USTACK_SIZE = 0x00000400; +__ISTACK_SIZE = 0x00000400; + +MEMORY +{ + RAM : ORIGIN = 0x4, LENGTH = 0x3fffc + RAM2 : ORIGIN = 0x00800000, LENGTH = 0x60000 + OFS : ORIGIN = 0xFE7F5D00, LENGTH = 128 + ROM : ORIGIN = 0xFFE00000, LENGTH = 0x200000 +} +SECTIONS +{ + .exvectors 0xFFFFFF80: AT(0xFFFFFF80) + { + "_exvectors_start" = .; + KEEP(*(.exvectors)) + "_exvectors_end" = .; + } >ROM + .fvectors 0xFFFFFFFC: AT(0xFFFFFFFC) + { + KEEP(*(.fvectors)) + } > ROM + .text 0xFFE00000: AT(0xFFE00000) + { + *(.text) + *(.text.*) + *(P) + KEEP(*(.text.*_isr)) + etext = .; + } > ROM + .rvectors ALIGN(4): + { + _rvectors_start = .; + KEEP(*(.rvectors)) + _rvectors_end = .; + } > ROM + .init : + { + KEEP(*(.init)) + __preinit_array_start = .; + KEEP(*(.preinit_array)) + __preinit_array_end = .; + __init_array_start = (. + 3) & ~ 3; + KEEP(*(.init_array)) + KEEP(*(SORT(.init_array.*))) + __init_array_end = .; + __fini_array_start = .; + KEEP(*(.fini_array)) + KEEP(*(SORT(.fini_array.*))) + __fini_array_end = .; + } > ROM + .fini : + { + KEEP(*(.fini)) + } > ROM + .got : + { + *(.got) + *(.got.plt) + } > ROM + .rodata : + { + *(.rodata) + *(.rodata.*) + *(C_1) + *(C_2) + *(C) + _erodata = .; + } > ROM + .eh_frame_hdr : + { + *(.eh_frame_hdr) + } > ROM + .eh_frame : + { + *(.eh_frame) + } > ROM + .jcr : + { + *(.jcr) + } > ROM + .tors : + { + __CTOR_LIST__ = .; + . = ALIGN(2); + ___ctors = .; + *(.ctors) + ___ctors_end = .; + __CTOR_END__ = .; + __DTOR_LIST__ = .; + ___dtors = .; + *(.dtors) + ___dtors_end = .; + __DTOR_END__ = .; + . = ALIGN(2); + _mdata = .; + } > ROM + .data : AT(_mdata) + { + _data = .; + *(.data) + *(.data.*) + *(D) + *(D_1) + *(D_2) + _edata = .; + } > RAM + .gcc_exc : + { + *(.gcc_exc) + } > RAM + .bss : + { + _bss = .; + *(.bss) + *(.bss.**) + *(COMMON) + *(B) + *(B_1) + *(B_2) + _ebss = .; + _end = .; + } > RAM + .ustack : + { + . = ALIGN(8); + . = . + __USTACK_SIZE; + PROVIDE(_ustack = .); + } > RAM + .istack : + { + . = ALIGN(8); + . = . + __ISTACK_SIZE; + PROVIDE(_istack = .); + } > RAM + .ofs1 0xFE7F5D00: AT(0xFE7F5D00) + { + KEEP(*(.ofs1)) + } > OFS + .ofs2 0xFE7F5D10: AT(0xFE7F5D10) + { + KEEP(*(.ofs2)) + } > OFS + .ofs3 0xFE7F5D20: AT(0xFE7F5D20) + { + KEEP(*(.ofs3)) + } > OFS + .ofs4 0xFE7F5D40: AT(0xFE7F5D40) + { + KEEP(*(.ofs4)) + } > OFS + .ofs5 0xFE7F5D48: AT(0xFE7F5D48) + { + KEEP(*(.ofs5)) + } > OFS + .ofs6 0xFE7F5D50: AT(0xFE7F5D50) + { + KEEP(*(.ofs6)) + } > OFS + .ofs7 0xFE7F5D64: AT(0xFE7F5D64) + { + KEEP(*(.ofs7)) + } > OFS + .ofs8 0xFE7F5D70: AT(0xFE7F5D70) + { + KEEP(*(.ofs8)) + } > OFS +} diff --git a/hw/bsp/rx/boards/rx65n_target/rx65n_target.c b/hw/bsp/rx/boards/rx65n_target/rx65n_target.c new file mode 100644 index 000000000..20867455e --- /dev/null +++ b/hw/bsp/rx/boards/rx65n_target/rx65n_target.c @@ -0,0 +1,320 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Koji Kitayama + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +/* How to connect JLink and RX65n Target and option board + * (For original comment https://github.com/hathach/tinyusb/pull/922#issuecomment-869786131) + * + * To enable JTAG, RX65N requires following connections on main board. + * - short EJ2 jumper header, to disable onboard E2L. + * - short EMLE(J1-2) and 3V3(J1-14 or J2-10), to enable In-Circuit Emulator. + * + * Note: For RX65N-Cloud-Kit, the option board's JTAG pins to some switches or floating. + * To use JLink with the option board, I think some further modifications will be necessary. + * + * | Function | RX65N pin | main board | option board | JLink connector | + * |:---------:|:----------:|:----------:|:------------:|:---------------:| + * | 3V3 | VCC | J1-14 | CN5-6 | 1 | + * | TRST | P34 | J1-16 | CN5-7 | 3 | + * | GND | VSS | J1-12 | CN5-5 | 4 | + * | TDI | P30 | J1-20 | CN5-10 | 5 | + * | TMS | P31 | J1-19 | USER_SW | 7 | + * | TCK/FINEC | P27 | J1-21 | N/A | 9 | + * | TDO | P26 | J1-22 | CN5-9 | 13 | + * | nRES | RES# | J1-10 | RESET_SW | 15 | + * + * JLink firmware needs to update to V6.96 or newer version to avoid + * [a bug](https://forum.segger.com/index.php/Thread/7758-SOLVED-Bug-in-JLink-from-V6-88b-regarding-RX65N) + * regarding downloading. + */ + +#include "bsp/board.h" +#include "iodefine.h" +#include "interrupt_handlers.h" + +#define IRQ_PRIORITY_CMT0 5 +#define IRQ_PRIORITY_USBI0 6 +#define IRQ_PRIORITY_SCI5 5 + +#define SYSTEM_PRCR_PRC1 (1<<1) +#define SYSTEM_PRCR_PRKEY (0xA5u<<8) + +#define CMT_PCLK 60000000 +#define CMT_CMCR_CKS_DIV_128 2 +#define CMT_CMCR_CMIE (1<<6) +#define MPC_PFS_ISEL (1<<6) + +#define SCI_PCLK 60000000 +#define SCI_SSR_FER (1<<4) +#define SCI_SSR_ORER (1<<5) + +#define SCI_SCR_TEIE (1u<<2) +#define SCI_SCR_RE (1u<<4) +#define SCI_SCR_TE (1u<<5) +#define SCI_SCR_RIE (1u<<6) +#define SCI_SCR_TIE (1u<<7) +#define INT_Excep_SCI5_TEI5 INT_Excep_ICU_GROUPBL0 + +#define IRQ_USB0_USBI0 62 +#define SLIBR_USBI0 SLIBR185 +#define IPR_USB0_USBI0 IPR_PERIB_INTB185 +#define INT_Excep_USB0_USBI0 INT_Excep_PERIB_INTB185 + +void HardwareSetup(void) +{ + FLASH.ROMCIV.WORD = 1; + while (FLASH.ROMCIV.WORD) ; + FLASH.ROMCE.WORD = 1; + while (!FLASH.ROMCE.WORD) ; + + SYSTEM.PRCR.WORD = 0xA503u; + if (!SYSTEM.RSTSR1.BYTE) { + RTC.RCR4.BYTE = 0; + RTC.RCR3.BYTE = 12; + while (12 != RTC.RCR3.BYTE) ; + } + SYSTEM.SOSCCR.BYTE = 1; + + if (SYSTEM.HOCOCR.BYTE) { + SYSTEM.HOCOCR.BYTE = 0; + while (!SYSTEM.OSCOVFSR.BIT.HCOVF) ; + } + SYSTEM.PLLCR.WORD = 0x1D10u; /* HOCO x 15 */ + SYSTEM.PLLCR2.BYTE = 0; + while (!SYSTEM.OSCOVFSR.BIT.PLOVF) ; + + SYSTEM.SCKCR.LONG = 0x21C11222u; + SYSTEM.SCKCR2.WORD = 0x0041u; + SYSTEM.ROMWT.BYTE = 0x02u; + while (0x02u != SYSTEM.ROMWT.BYTE) ; + SYSTEM.SCKCR3.WORD = 0x400u; + SYSTEM.PRCR.WORD = 0xA500u; +} + +//--------------------------------------------------------------------+ +// SCI handling +//--------------------------------------------------------------------+ +typedef struct { + uint8_t *buf; + uint32_t cnt; +} sci_buf_t; +static volatile sci_buf_t sci_buf[2]; + +void INT_Excep_SCI5_TXI5(void) +{ + uint8_t *buf = sci_buf[0].buf; + uint32_t cnt = sci_buf[0].cnt; + + if (!buf || !cnt) { + SCI5.SCR.BYTE &= ~(SCI_SCR_TEIE | SCI_SCR_TE | SCI_SCR_TIE); + return; + } + SCI5.TDR = *buf; + if (--cnt) { + ++buf; + } else { + buf = NULL; + SCI5.SCR.BIT.TIE = 0; + SCI5.SCR.BIT.TEIE = 1; + } + sci_buf[0].buf = buf; + sci_buf[0].cnt = cnt; +} + +void INT_Excep_SCI5_TEI5(void) +{ + SCI5.SCR.BYTE &= ~(SCI_SCR_TEIE | SCI_SCR_TE | SCI_SCR_TIE); +} + +void INT_Excep_SCI5_RXI5(void) +{ + uint8_t *buf = sci_buf[1].buf; + uint32_t cnt = sci_buf[1].cnt; + + if (!buf || !cnt || + (SCI5.SSR.BYTE & (SCI_SSR_FER | SCI_SSR_ORER))) { + sci_buf[1].buf = NULL; + SCI5.SSR.BYTE = 0; + SCI5.SCR.BYTE &= ~(SCI_SCR_RE | SCI_SCR_RIE); + return; + } + *buf = SCI5.RDR; + if (--cnt) { + ++buf; + } else { + buf = NULL; + SCI5.SCR.BYTE &= ~(SCI_SCR_RE | SCI_SCR_RIE); + } + sci_buf[1].buf = buf; + sci_buf[1].cnt = cnt; +} + +//--------------------------------------------------------------------+ +// Forward USB interrupt events to TinyUSB IRQ Handler +//--------------------------------------------------------------------+ +void INT_Excep_USB0_USBI0(void) +{ + tud_int_handler(0); +} + +void board_init(void) +{ + /* setup software configurable interrupts */ + ICU.SLIBR_USBI0.BYTE = IRQ_USB0_USBI0; + ICU.SLIPRCR.BYTE = 1; + +#if CFG_TUSB_OS == OPT_OS_NONE + /* Enable CMT0 */ + SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1; + MSTP(CMT0) = 0; + SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY; + /* Setup 1ms tick timer */ + CMT0.CMCNT = 0; + CMT0.CMCOR = CMT_PCLK / 1000 / 128; + CMT0.CMCR.WORD = CMT_CMCR_CMIE | CMT_CMCR_CKS_DIV_128; + IR(CMT0, CMI0) = 0; + IPR(CMT0, CMI0) = IRQ_PRIORITY_CMT0; + IEN(CMT0, CMI0) = 1; + CMT.CMSTR0.BIT.STR0 = 1; +#endif + + /* Unlock MPC registers */ + MPC.PWPR.BIT.B0WI = 0; + MPC.PWPR.BIT.PFSWE = 1; + // SW PB1 + PORTB.PMR.BIT.B1 = 0U; + PORTB.PDR.BIT.B1 = 0U; + // LED PD6 + PORTD.PODR.BIT.B6 = 1U; + PORTD.ODR1.BIT.B4 = 1U; + PORTD.PMR.BIT.B6 = 0U; + PORTD.PDR.BIT.B6 = 1U; + /* UART TXD5 => PA4, RXD5 => PA3 */ + PORTA.PMR.BIT.B4 = 1U; + PORTA.PCR.BIT.B4 = 1U; + MPC.PA4PFS.BYTE = 0b01010; + PORTA.PMR.BIT.B3 = 1U; + MPC.PA5PFS.BYTE = 0b01010; + /* USB VBUS -> P16 */ + PORT1.PMR.BIT.B6 = 1U; + MPC.P16PFS.BYTE = MPC_PFS_ISEL | 0b10001; + /* Lock MPC registers */ + MPC.PWPR.BIT.PFSWE = 0; + MPC.PWPR.BIT.B0WI = 1; + + /* Enable SCI5 */ + SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1; + MSTP(SCI5) = 0; + SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY; + SCI5.SEMR.BIT.ABCS = 1; + SCI5.SEMR.BIT.BGDM = 1; + SCI5.BRR = (SCI_PCLK / (8 * 115200)) - 1; + IR(SCI5, RXI5) = 0; + IR(SCI5, TXI5) = 0; + IS(SCI5, TEI5) = 0; + IR(ICU, GROUPBL0) = 0; + IPR(SCI5, RXI5) = IRQ_PRIORITY_SCI5; + IPR(SCI5, TXI5) = IRQ_PRIORITY_SCI5; + IPR(ICU,GROUPBL0) = IRQ_PRIORITY_SCI5; + IEN(SCI5, RXI5) = 1; + IEN(SCI5, TXI5) = 1; + IEN(ICU,GROUPBL0) = 1; + EN(SCI5, TEI5) = 1; + + /* setup USBI0 interrupt. */ + IR(USB0, USBI0) = 0; + IPR(USB0, USBI0) = IRQ_PRIORITY_USBI0; +} + +//--------------------------------------------------------------------+ +// Board porting API +//--------------------------------------------------------------------+ + +void board_led_write(bool state) +{ + PORTD.PODR.BIT.B6 = state ? 0 : 1; +} + +uint32_t board_button_read(void) +{ + return PORTB.PIDR.BIT.B1 ? 0 : 1; +} + +int board_uart_read(uint8_t* buf, int len) +{ + sci_buf[1].buf = buf; + sci_buf[1].cnt = len; + SCI5.SCR.BYTE |= SCI_SCR_RE | SCI_SCR_RIE; + while (SCI5.SCR.BIT.RE) ; + return len - sci_buf[1].cnt; +} + +int board_uart_write(void const *buf, int len) +{ + sci_buf[0].buf = (uint8_t*)buf; + sci_buf[0].cnt = len; + SCI5.SCR.BYTE |= SCI_SCR_TE | SCI_SCR_TIE; + while (SCI5.SCR.BIT.TE) ; + return len; +} + +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; +void INT_Excep_CMT0_CMI0(void) +{ + ++system_ticks; +} + +uint32_t board_millis(void) +{ + return system_ticks; +} +#else +uint32_t SystemCoreClock = 120000000; +#endif + +int close(int fd) +{ + (void)fd; + return -1; +} +int fstat(int fd, void *pstat) +{ + (void)fd; + (void)pstat; + return 0; +} +off_t lseek(int fd, off_t pos, int whence) +{ + (void)fd; + (void)pos; + (void)whence; + return 0; +} +int isatty(int fd) +{ + (void)fd; + return 1; +} diff --git a/hw/bsp/rx/family.mk b/hw/bsp/rx/family.mk new file mode 100644 index 000000000..5a8281718 --- /dev/null +++ b/hw/bsp/rx/family.mk @@ -0,0 +1,32 @@ +DEPS_SUBMODULES += hw/mcu/renesas/rx + +# Cross Compiler for RX +CROSS_COMPILE = rx-elf- + +include $(TOP)/$(BOARD_PATH)/board.mk + +CFLAGS += \ + -nostartfiles \ + -ffunction-sections \ + -fdata-sections \ + -fshort-enums \ + -mlittle-endian-data \ + -DSSIZE_MAX=__INT_MAX__ + +SRC_C += \ + src/portable/renesas/usba/dcd_usba.c \ + $(MCU_DIR)/vects.c + +INC += \ + $(TOP)/$(BOARD_PATH) \ + $(TOP)/$(MCU_DIR) + +SRC_S += $(MCU_DIR)/start.S + +$(BUILD)/$(PROJECT).mot: $(BUILD)/$(PROJECT).elf + @echo CREATE $@ + $(OBJCOPY) -O srec -I elf32-rx-be-ns $^ $@ + +# flash using rfp-cli +flash-rfp: $(BUILD)/$(PROJECT).mot + rfp-cli -device rx65x -tool e2l -if fine -fo id FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -auth id FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -auto $^ diff --git a/hw/bsp/rx63n/boards/gr_citrus/board.mk b/hw/bsp/rx63n/boards/gr_citrus/board.mk deleted file mode 100644 index feab5b5e6..000000000 --- a/hw/bsp/rx63n/boards/gr_citrus/board.mk +++ /dev/null @@ -1,61 +0,0 @@ -DEPS_SUBMODULES += hw/mcu/renesas/rx - -CFLAGS += \ - -nostartfiles \ - -ffunction-sections \ - -fdata-sections \ - -fshort-enums \ - -mcpu=rx610 \ - -misa=v1 \ - -mlittle-endian-data \ - -DCFG_TUSB_MCU=OPT_MCU_RX63X - -# Cross Compiler for RX -CROSS_COMPILE = rx-elf- - -RX_NEWLIB ?= 1 - -ifeq ($(CMDEXE),1) -OPTLIBINC="$(shell for /F "usebackq delims=" %%i in (`where rx-elf-gcc`) do echo %%~dpi..\rx-elf\optlibinc)" -else -OPTLIBINC=$(shell dirname `which rx-elf-gcc`)../rx-elf/optlibinc -endif - -ifeq ($(RX_NEWLIB),1) -CFLAGS += -DSSIZE_MAX=__INT_MAX__ -else -# setup for optlib -CFLAGS += -nostdinc \ - -isystem $(OPTLIBINC) \ - -DLWIP_NO_INTTYPES_H - -LIBS += -loptc -loptm -endif - -MCU_DIR = hw/mcu/renesas/rx/rx63n - -# All source paths should be relative to the top level. -LD_FILE = $(BOARD_PATH)/r5f5631fd.ld - -SRC_C += \ - src/portable/renesas/usba/dcd_usba.c \ - $(MCU_DIR)/vects.c - -INC += \ - $(TOP)/$(BOARD_PATH) \ - $(TOP)/$(MCU_DIR) - -SRC_S += $(MCU_DIR)/start.S - -# For freeRTOS port source -FREERTOS_PORT = RX600 - -# For flash-jlink target -JLINK_DEVICE = R5F5631F -JLINK_IF = JTAG - -# For flash-pyocd target -PYOCD_TARGET = - -# flash using jlink -flash: flash-jlink diff --git a/hw/bsp/rx63n/family.mk b/hw/bsp/rx63n/family.mk deleted file mode 100644 index d3c743ed1..000000000 --- a/hw/bsp/rx63n/family.mk +++ /dev/null @@ -1 +0,0 @@ -include $(TOP)/$(BOARD_PATH)/board.mk \ No newline at end of file diff --git a/hw/bsp/saml22/family.mk b/hw/bsp/saml22/family.mk deleted file mode 100644 index fbe4e66f8..000000000 --- a/hw/bsp/saml22/family.mk +++ /dev/null @@ -1,50 +0,0 @@ -UF2_FAMILY_ID = 0x68ed2b88 -DEPS_SUBMODULES += hw/mcu/microchip - -include $(TOP)/$(BOARD_PATH)/board.mk - -CFLAGS += \ - -mthumb \ - -mabi=aapcs \ - -mcpu=cortex-m0plus \ - -nostdlib -nostartfiles \ - -DCONF_OSC32K_CALIB_ENABLE=0 \ - -DCFG_TUSB_MCU=OPT_MCU_SAML22 - -SRC_C += \ - src/portable/microchip/samd/dcd_samd.c \ - hw/mcu/microchip/saml22/gcc/gcc/startup_saml22.c \ - hw/mcu/microchip/saml22/gcc/system_saml22.c \ - hw/mcu/microchip/saml22/hpl/gclk/hpl_gclk.c \ - hw/mcu/microchip/saml22/hpl/mclk/hpl_mclk.c \ - hw/mcu/microchip/saml22/hpl/pm/hpl_pm.c \ - hw/mcu/microchip/saml22/hpl/osc32kctrl/hpl_osc32kctrl.c \ - hw/mcu/microchip/saml22/hpl/oscctrl/hpl_oscctrl.c \ - hw/mcu/microchip/saml22/hal/src/hal_atomic.c - -INC += \ - $(TOP)/$(BOARD_PATH) \ - $(TOP)/hw/mcu/microchip/saml22/ \ - $(TOP)/hw/mcu/microchip/saml22/config \ - $(TOP)/hw/mcu/microchip/saml22/include \ - $(TOP)/hw/mcu/microchip/saml22/hal/include \ - $(TOP)/hw/mcu/microchip/saml22/hal/utils/include \ - $(TOP)/hw/mcu/microchip/saml22/hpl/port \ - $(TOP)/hw/mcu/microchip/saml22/hri \ - $(TOP)/hw/mcu/microchip/saml22/CMSIS/Core/Include - -# For TinyUSB port source -VENDOR = microchip -CHIP_FAMILY = samd - -# For freeRTOS port source -FREERTOS_PORT = ARM_CM0 - -# flash using bossac at least version 1.8 -# can be found in arduino15/packages/arduino/tools/bossac/ -# Add it to your PATH or change BOSSAC variable to match your installation -BOSSAC = bossac - -flash-bossac: $(BUILD)/$(PROJECT).bin - @:$(call check_defined, SERIAL, example: SERIAL=/dev/ttyACM0) - $(BOSSAC) --port=$(SERIAL) -U -i --offset=0x2000 -e -w $^ -R diff --git a/hw/bsp/saml2x/boards/atsaml21_xpro/board.h b/hw/bsp/saml2x/boards/atsaml21_xpro/board.h new file mode 100644 index 000000000..a3e03997c --- /dev/null +++ b/hw/bsp/saml2x/boards/atsaml21_xpro/board.h @@ -0,0 +1,50 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020, Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef BOARD_H_ +#define BOARD_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +// LED +#define LED_PIN (32 + 30) // PB30 +#define LED_STATE_ON 0 + +// Button +#define BUTTON_PIN (0 + 15) // PA15 +#define BUTTON_STATE_ACTIVE 0 + +// UART +#define UART_RX_PIN 4 +#define UART_TX_PIN 5 + +#ifdef __cplusplus + } +#endif + +#endif /* BOARD_H_ */ diff --git a/hw/bsp/saml2x/boards/atsaml21_xpro/board.mk b/hw/bsp/saml2x/boards/atsaml21_xpro/board.mk new file mode 100644 index 000000000..81b4ecdcb --- /dev/null +++ b/hw/bsp/saml2x/boards/atsaml21_xpro/board.mk @@ -0,0 +1,12 @@ +CFLAGS += -D__SAML21J18B__ + +SAML_VARIANT = saml21 + +# All source paths should be relative to the top level. +LD_FILE = $(BOARD_PATH)/saml21j18b_flash.ld + +# For flash-jlink target +JLINK_DEVICE = ATSAML21J18 + +# flash using jlink +flash: flash-jlink diff --git a/hw/bsp/saml2x/boards/atsaml21_xpro/saml21j18b_flash.ld b/hw/bsp/saml2x/boards/atsaml21_xpro/saml21j18b_flash.ld new file mode 100644 index 000000000..7f6b7fa99 --- /dev/null +++ b/hw/bsp/saml2x/boards/atsaml21_xpro/saml21j18b_flash.ld @@ -0,0 +1,153 @@ +/** + * \file + * + * \brief Linker script for running in internal FLASH on the SAML21J18B + * + * Copyright (c) 2016 Atmel Corporation, + * a wholly owned subsidiary of Microchip Technology Inc. + * + * \asf_license_start + * + * \page License + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the Licence at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * \asf_license_stop + * + */ + + +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +SEARCH_DIR(.) + +/* Memory Spaces Definitions */ +MEMORY +{ + rom (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 + lpram (rwx) : ORIGIN = 0x30000000, LENGTH = 0x00002000 +} + +/* The stack size used by the application. NOTE: you need to adjust according to your application. */ +STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000; + +/* Section Definitions */ +SECTIONS +{ + .text : + { + . = ALIGN(4); + _sfixed = .; + KEEP(*(.vectors .vectors.*)) + *(.text .text.* .gnu.linkonce.t.*) + *(.glue_7t) *(.glue_7) + *(.rodata .rodata* .gnu.linkonce.r.*) + *(.ARM.extab* .gnu.linkonce.armextab.*) + + /* Support C constructors, and C destructors in both user code + and the C library. This also provides support for C++ code. */ + . = ALIGN(4); + KEEP(*(.init)) + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + + . = ALIGN(4); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + + . = ALIGN(4); + KEEP(*(.fini)) + + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + . = ALIGN(4); + _efixed = .; /* End of text section */ + } > rom + + /* .ARM.exidx is sorted, so has to go in its own output section. */ + PROVIDE_HIDDEN (__exidx_start = .); + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > rom + PROVIDE_HIDDEN (__exidx_end = .); + + . = ALIGN(4); + _etext = .; + + .relocate : AT (_etext) + { + . = ALIGN(4); + _srelocate = .; + *(.ramfunc .ramfunc.*); + *(.data .data.*); + . = ALIGN(4); + _erelocate = .; + } > ram + + .lpram (NOLOAD): + { + . = ALIGN(8); + _slpram = .; + *(.lpram .lpram.*); + . = ALIGN(8); + _elpram = .; + } > lpram + + /* .bss section which is used for uninitialized data */ + .bss (NOLOAD) : + { + . = ALIGN(4); + _sbss = . ; + _szero = .; + *(.bss .bss.*) + *(COMMON) + . = ALIGN(4); + _ebss = . ; + _ezero = .; + end = .; + } > ram + + /* stack section */ + .stack (NOLOAD): + { + . = ALIGN(8); + _sstack = .; + . = . + STACK_SIZE; + . = ALIGN(8); + _estack = .; + } > ram + + . = ALIGN(4); + _end = . ; +} diff --git a/hw/bsp/saml22/boards/saml22_feather/board.h b/hw/bsp/saml2x/boards/saml22_feather/board.h similarity index 100% rename from hw/bsp/saml22/boards/saml22_feather/board.h rename to hw/bsp/saml2x/boards/saml22_feather/board.h diff --git a/hw/bsp/saml22/boards/saml22_feather/board.mk b/hw/bsp/saml2x/boards/saml22_feather/board.mk similarity index 89% rename from hw/bsp/saml22/boards/saml22_feather/board.mk rename to hw/bsp/saml2x/boards/saml22_feather/board.mk index a6a773137..0605dca19 100644 --- a/hw/bsp/saml22/boards/saml22_feather/board.mk +++ b/hw/bsp/saml2x/boards/saml22_feather/board.mk @@ -1,5 +1,7 @@ CFLAGS += -D__SAML22J18A__ +SAML_VARIANT = saml22 + # All source paths should be relative to the top level. LD_FILE = $(BOARD_PATH)/$(BOARD).ld diff --git a/hw/bsp/saml22/boards/saml22_feather/saml22_feather.ld b/hw/bsp/saml2x/boards/saml22_feather/saml22_feather.ld similarity index 100% rename from hw/bsp/saml22/boards/saml22_feather/saml22_feather.ld rename to hw/bsp/saml2x/boards/saml22_feather/saml22_feather.ld diff --git a/hw/bsp/saml22/boards/sensorwatch_m0/board.h b/hw/bsp/saml2x/boards/sensorwatch_m0/board.h similarity index 100% rename from hw/bsp/saml22/boards/sensorwatch_m0/board.h rename to hw/bsp/saml2x/boards/sensorwatch_m0/board.h diff --git a/hw/bsp/saml22/boards/sensorwatch_m0/board.mk b/hw/bsp/saml2x/boards/sensorwatch_m0/board.mk similarity index 89% rename from hw/bsp/saml22/boards/sensorwatch_m0/board.mk rename to hw/bsp/saml2x/boards/sensorwatch_m0/board.mk index a6a773137..0605dca19 100644 --- a/hw/bsp/saml22/boards/sensorwatch_m0/board.mk +++ b/hw/bsp/saml2x/boards/sensorwatch_m0/board.mk @@ -1,5 +1,7 @@ CFLAGS += -D__SAML22J18A__ +SAML_VARIANT = saml22 + # All source paths should be relative to the top level. LD_FILE = $(BOARD_PATH)/$(BOARD).ld diff --git a/hw/bsp/saml22/boards/sensorwatch_m0/sensorwatch_m0.ld b/hw/bsp/saml2x/boards/sensorwatch_m0/sensorwatch_m0.ld similarity index 100% rename from hw/bsp/saml22/boards/sensorwatch_m0/sensorwatch_m0.ld rename to hw/bsp/saml2x/boards/sensorwatch_m0/sensorwatch_m0.ld diff --git a/hw/bsp/saml22/family.c b/hw/bsp/saml2x/family.c similarity index 93% rename from hw/bsp/saml22/family.c rename to hw/bsp/saml2x/family.c index fff11cbe5..470fde750 100644 --- a/hw/bsp/saml22/family.c +++ b/hw/bsp/saml2x/family.c @@ -108,13 +108,13 @@ void board_init(void) gpio_set_pin_function(PIN_PA25, PINMUX_PA25G_USB_DP); // Output 500hz PWM on PB23 (TCC0 WO[3]) so we can validate the GCLK1 clock speed - hri_mclk_set_APBCMASK_TCC0_bit(MCLK); - TCC0->PER.bit.PER = 48000000 / 1000; - TCC0->CC[3].bit.CC = 48000000 / 2000; - TCC0->CTRLA.bit.ENABLE = true; - - gpio_set_pin_function(PIN_PB23, PINMUX_PB23F_TCC0_WO3); - hri_gclk_write_PCHCTRL_reg(GCLK, TCC0_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK1_Val | GCLK_PCHCTRL_CHEN); +// hri_mclk_set_APBCMASK_TCC0_bit(MCLK); +// TCC0->PER.bit.PER = 48000000 / 1000; +// TCC0->CC[3].bit.CC = 48000000 / 2000; +// TCC0->CTRLA.bit.ENABLE = true; +// +// gpio_set_pin_function(PIN_PB23, PINMUX_PB23F_TCC0_WO3); +// hri_gclk_write_PCHCTRL_reg(GCLK, TCC0_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK1_Val | GCLK_PCHCTRL_CHEN); } //--------------------------------------------------------------------+ @@ -160,4 +160,4 @@ uint32_t board_millis(void) void _init(void) { -} \ No newline at end of file +} diff --git a/hw/bsp/saml2x/family.mk b/hw/bsp/saml2x/family.mk new file mode 100644 index 000000000..e0f6b2f77 --- /dev/null +++ b/hw/bsp/saml2x/family.mk @@ -0,0 +1,48 @@ +UF2_FAMILY_ID = 0x68ed2b88 +DEPS_SUBMODULES += lib/CMSIS_5 hw/mcu/microchip + +include $(TOP)/$(BOARD_PATH)/board.mk + +MCU_DIR = hw/mcu/microchip/$(SAML_VARIANT) + +CFLAGS += \ + -mthumb \ + -mabi=aapcs \ + -mcpu=cortex-m0plus \ + -nostdlib -nostartfiles \ + -DCONF_OSC32K_CALIB_ENABLE=0 \ + -DCFG_TUSB_MCU=OPT_MCU_SAML22 + +SRC_C += \ + src/portable/microchip/samd/dcd_samd.c \ + $(MCU_DIR)/gcc/gcc/startup_$(SAML_VARIANT).c \ + $(MCU_DIR)/gcc/system_$(SAML_VARIANT).c \ + $(MCU_DIR)/hpl/gclk/hpl_gclk.c \ + $(MCU_DIR)/hpl/mclk/hpl_mclk.c \ + $(MCU_DIR)/hpl/pm/hpl_pm.c \ + $(MCU_DIR)/hpl/osc32kctrl/hpl_osc32kctrl.c \ + $(MCU_DIR)/hpl/oscctrl/hpl_oscctrl.c \ + $(MCU_DIR)/hal/src/hal_atomic.c + +INC += \ + $(TOP)/$(BOARD_PATH) \ + $(TOP)/$(MCU_DIR)/ \ + $(TOP)/$(MCU_DIR)/config \ + $(TOP)/$(MCU_DIR)/include \ + $(TOP)/$(MCU_DIR)/hal/include \ + $(TOP)/$(MCU_DIR)/hal/utils/include \ + $(TOP)/$(MCU_DIR)/hpl/port \ + $(TOP)/$(MCU_DIR)/hri \ + $(TOP)/lib/CMSIS_5/CMSIS/Core/Include + +# For freeRTOS port source +FREERTOS_PORT = ARM_CM0 + +# flash using bossac at least version 1.8 +# can be found in arduino15/packages/arduino/tools/bossac/ +# Add it to your PATH or change BOSSAC variable to match your installation +BOSSAC = bossac + +flash-bossac: $(BUILD)/$(PROJECT).bin + @:$(call check_defined, SERIAL, example: SERIAL=/dev/ttyACM0) + $(BOSSAC) --port=$(SERIAL) -U -i --offset=0x2000 -e -w $^ -R diff --git a/hw/mcu/microchip b/hw/mcu/microchip index f7087f047..58eb37632 160000 --- a/hw/mcu/microchip +++ b/hw/mcu/microchip @@ -1 +1 @@ -Subproject commit f7087f04783c896627061fc151fa3527b73733c7 +Subproject commit 58eb3763200ff51a998be5f537acf67299add227 diff --git a/hw/mcu/renesas/rx b/hw/mcu/renesas/rx index 4a51dfe6e..706b4e0cf 160000 --- a/hw/mcu/renesas/rx +++ b/hw/mcu/renesas/rx @@ -1 +1 @@ -Subproject commit 4a51dfe6ecdf936d2d89f223f069e24a2d109207 +Subproject commit 706b4e0cf485605c32351e2f90f5698267996023 diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h index 936f09104..f99061eae 100644 --- a/src/class/audio/audio.h +++ b/src/class/audio/audio.h @@ -489,7 +489,7 @@ typedef enum AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2), AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3), AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4), - AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x100000000, + AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000, } audio_data_format_type_I_t; /// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification @@ -823,6 +823,33 @@ typedef struct TU_ATTR_PACKED uint16_t wLockDelay ; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field. } audio_desc_cs_as_iso_data_ep_t; +// 5.2.2 Control Request Layout +typedef struct TU_ATTR_PACKED +{ + union + { + struct TU_ATTR_PACKED + { + uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. + uint8_t type : 2; ///< Request type tusb_request_type_t. + uint8_t direction : 1; ///< Direction type. tusb_dir_t + } bmRequestType_bit; + + uint8_t bmRequestType; + }; + + uint8_t bRequest; ///< Request type audio_cs_req_t + uint8_t bChannelNumber; + uint8_t bControlSelector; + union + { + uint8_t bInterface; + uint8_t bEndpoint; + }; + uint8_t bEntityID; + uint16_t wLength; +} audio_control_request_t; + //// 5.2.3 Control Request Parameter Block Layout // 5.2.3.1 1-byte Control CUR Parameter Block diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 8e4420ed0..404d28817 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -102,19 +102,19 @@ // EP IN software buffers and mutexes #if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ]; #if CFG_FIFO_MUTEX osal_mutex_def_t ep_in_ff_mutex_wr_1; // No need for read mutex as only USB driver reads from FIFO #endif #endif // CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0 #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ]; #if CFG_FIFO_MUTEX osal_mutex_def_t ep_in_ff_mutex_wr_2; // No need for read mutex as only USB driver reads from FIFO #endif #endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0 #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ]; #if CFG_FIFO_MUTEX osal_mutex_def_t ep_in_ff_mutex_wr_3; // No need for read mutex as only USB driver reads from FIFO #endif @@ -126,32 +126,32 @@ osal_mutex_def_t ep_in_ff_mutex_wr_3; // - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into #if CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING) #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0 -CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX]; #endif #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0 -CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX]; #endif #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0 -CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX]; #endif #endif // CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING) // EP OUT software buffers and mutexes #if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ]; #if CFG_FIFO_MUTEX osal_mutex_def_t ep_out_ff_mutex_rd_1; // No need for write mutex as only USB driver writes into FIFO #endif #endif // CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0 #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ]; #if CFG_FIFO_MUTEX osal_mutex_def_t ep_out_ff_mutex_rd_2; // No need for write mutex as only USB driver writes into FIFO #endif #endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0 #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ]; #if CFG_FIFO_MUTEX osal_mutex_def_t ep_out_ff_mutex_rd_3; // No need for write mutex as only USB driver writes into FIFO #endif @@ -163,52 +163,52 @@ osal_mutex_def_t ep_out_ff_mutex_rd_3; // - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into #if CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING) #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0 -CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX]; #endif #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0 -CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX]; #endif #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0 -CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX]; #endif #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING) // Control buffers -CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ]; #if CFG_TUD_AUDIO > 1 -CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ]; #endif #if CFG_TUD_AUDIO > 2 -CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ]; #endif // Active alternate setting of interfaces -CFG_TUSB_MEM_ALIGN uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT]; +uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT]; #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_N_AS_INT > 0 -CFG_TUSB_MEM_ALIGN uint8_t alt_setting_2[CFG_TUD_AUDIO_FUNC_2_N_AS_INT]; +uint8_t alt_setting_2[CFG_TUD_AUDIO_FUNC_2_N_AS_INT]; #endif #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_N_AS_INT > 0 -CFG_TUSB_MEM_ALIGN uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT]; +uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT]; #endif // Software encoding/decoding support FIFOs #if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING #if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ]; tu_fifo_t tx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO]; #if CFG_FIFO_MUTEX osal_mutex_def_t tx_supp_ff_mutex_wr_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO #endif #endif #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ]; tu_fifo_t tx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO]; #if CFG_FIFO_MUTEX osal_mutex_def_t tx_supp_ff_mutex_wr_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO #endif #endif #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ]; tu_fifo_t tx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO]; #if CFG_FIFO_MUTEX osal_mutex_def_t tx_supp_ff_mutex_wr_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO @@ -218,21 +218,21 @@ osal_mutex_def_t tx_supp_ff_mutex_wr_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO]; #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING #if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ]; tu_fifo_t rx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO]; #if CFG_FIFO_MUTEX osal_mutex_def_t rx_supp_ff_mutex_rd_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO #endif #endif #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ]; tu_fifo_t rx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO]; #if CFG_FIFO_MUTEX osal_mutex_def_t rx_supp_ff_mutex_rd_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO #endif #endif #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0 -CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ]; tu_fifo_t rx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO]; #if CFG_FIFO_MUTEX osal_mutex_def_t rx_supp_ff_mutex_rd_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO @@ -294,7 +294,7 @@ typedef struct // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74) #if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN - CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE]; + CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE]; #endif // Decoding parameters - parameters are set when alternate AS interface is set by host @@ -393,12 +393,12 @@ static uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio); #if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const as_itf); -#endif static inline uint8_t tu_desc_subtype(void const* desc) { return ((uint8_t const*) desc)[2]; } +#endif bool tud_audio_n_mounted(uint8_t func_id) { @@ -643,7 +643,6 @@ static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, u // Determine amount of samples uint8_t const n_ff_used = audio->n_ff_used_rx; - uint16_t const nBytesToCopy = audio->n_channels_per_ff_rx * audio->n_bytes_per_sampe_rx; uint16_t const nBytesPerFFToRead = n_bytes_received / n_ff_used; uint8_t cnt_ff; @@ -662,14 +661,14 @@ static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, u info.len_lin = tu_min16(nBytesPerFFToRead, info.len_lin); src = &audio->lin_buf_out[cnt_ff*audio->n_channels_per_ff_rx * audio->n_bytes_per_sampe_rx]; dst_end = info.ptr_lin + info.len_lin; - src = audiod_interleaved_copy_bytes_fast_decode(nBytesToCopy, info.ptr_lin, dst_end, src, n_ff_used); + src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_lin, dst_end, src, n_ff_used); // Handle wrapped part of FIFO info.len_wrap = tu_min16(nBytesPerFFToRead - info.len_lin, info.len_wrap); if (info.len_wrap != 0) { dst_end = info.ptr_wrap + info.len_wrap; - audiod_interleaved_copy_bytes_fast_decode(nBytesToCopy, info.ptr_wrap, dst_end, src, n_ff_used); + audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_wrap, dst_end, src, n_ff_used); } tu_fifo_advance_write_pointer(&audio->rx_supp_ff[cnt_ff], info.len_lin + info.len_wrap); } @@ -885,7 +884,7 @@ range [-1, +1) * */ // Helper function -static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const nBytesToCopy, void * src, uint8_t * src_end, uint8_t * dst, uint8_t const n_ff_used) +static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const nBytesToCopy, uint8_t * src, uint8_t * src_end, uint8_t * dst, uint8_t const n_ff_used) { // Optimize for fast half word copies typedef struct{ @@ -900,15 +899,15 @@ static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const switch (nBytesToCopy) { case 1: - while((uint8_t *)src < src_end) + while(src < src_end) { - *dst = *(uint8_t *)src++; + *dst = *src++; dst += n_ff_used; } break; case 2: - while((uint8_t *)src < src_end) + while(src < src_end) { *(unaligned_uint16_t*)dst = *(unaligned_uint16_t*)src; src += 2; @@ -917,23 +916,23 @@ static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const break; case 3: - while((uint8_t *)src < src_end) + while(src < src_end) { // memcpy(dst, src, 3); // src = (uint8_t *)src + 3; // dst += 3 * n_ff_used; // TODO: Is there a faster way to copy 3 bytes? - *dst++ = *(uint8_t *)src++; - *dst++ = *(uint8_t *)src++; - *dst++ = *(uint8_t *)src++; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; dst += 3 * (n_ff_used - 1); } break; case 4: - while((uint8_t *)src < src_end) + while(src < src_end) { *(unaligned_uint32_t*)dst = *(unaligned_uint32_t*)src; src += 4; @@ -993,8 +992,8 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi if (info.len_lin != 0) { info.len_lin = tu_min16(nBytesPerFFToSend, info.len_lin); // Limit up to desired length - src_end = info.ptr_lin + info.len_lin; - dst = audiod_interleaved_copy_bytes_fast_encode(nBytesToCopy, info.ptr_lin, src_end, dst, n_ff_used); + src_end = (uint8_t *)info.ptr_lin + info.len_lin; + dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_lin, src_end, dst, n_ff_used); // Limit up to desired length info.len_wrap = tu_min16(nBytesPerFFToSend - info.len_lin, info.len_wrap); @@ -1002,8 +1001,8 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi // Handle wrapped part of FIFO if (info.len_wrap != 0) { - src_end = info.ptr_wrap + info.len_wrap; - audiod_interleaved_copy_bytes_fast_encode(nBytesToCopy, info.ptr_wrap, src_end, dst, n_ff_used); + src_end = (uint8_t *)info.ptr_wrap + info.len_wrap; + audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_wrap, src_end, dst, n_ff_used); } tu_fifo_advance_read_pointer(&audio->tx_supp_ff[cnt_ff], info.len_lin + info.len_wrap); @@ -1481,18 +1480,20 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * audio->ep_in_as_intf_num = 0; usbd_edpt_close(rhport, audio->ep_in); - // Invoke callback - can be used to stop data sampling - if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request)); - - audio->ep_in = 0; // Necessary? - - // Clear support FIFOs if used -#if CFG_TUD_AUDIO_ENABLE_ENCODING + // Clear FIFOs, since data is no longer valid +#if !CFG_TUD_AUDIO_ENABLE_ENCODING + tu_fifo_clear(&audio->ep_in_ff); +#else for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++) { tu_fifo_clear(&audio->tx_supp_ff[cnt]); } #endif + + // Invoke callback - can be used to stop data sampling + if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request)); + + audio->ep_in = 0; // Necessary? } #endif @@ -1502,16 +1503,22 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * { audio->ep_out_as_intf_num = 0; usbd_edpt_close(rhport, audio->ep_out); - audio->ep_out = 0; // Necessary? - // Clear support FIFOs if used -#if CFG_TUD_AUDIO_ENABLE_DECODING + // Clear FIFOs, since data is no longer valid +#if !CFG_TUD_AUDIO_ENABLE_DECODING + tu_fifo_clear(&audio->ep_out_ff); +#else for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++) { tu_fifo_clear(&audio->rx_supp_ff[cnt]); } #endif + // Invoke callback - can be used to stop data sampling + if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request)); + + audio->ep_out = 0; // Necessary? + // Close corresponding feedback EP #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP usbd_edpt_close(rhport, audio->ep_fb); @@ -1605,9 +1612,17 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * TU_ASSERT( audio->n_ff_used_rx <= audio->n_rx_supp_ff ); #endif #endif + +#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + // In case of asynchronous EP, call Cb after ep_fb is set + if (!(((tusb_desc_endpoint_t const *) p_desc)->bmAttributes.sync == 0x01 && audio->ep_fb == 0)) + { + if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request)); + } +#else // Invoke callback if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request)); - +#endif // Prepare for incoming data #if USE_LINEAR_BUFFER_RX TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false); @@ -1621,8 +1636,11 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * { audio->ep_fb = ep_addr; - // Invoke callback - if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request)); + // Invoke callback after ep_out is set + if (audio->ep_out != 0) + { + if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request)); + } } #endif #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT @@ -1658,64 +1676,65 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const switch (p_request->bmRequestType_bit.recipient) { - case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label - - uint8_t itf = TU_U16_LOW(p_request->wIndex); - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - - if (entityID != 0) + case TUSB_REQ_RCPT_INTERFACE: { - if (tud_audio_set_req_entity_cb) - { - // Check if entity is present and get corresponding driver index - TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id)); + uint8_t itf = TU_U16_LOW(p_request->wIndex); + uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - // Invoke callback - return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf); + if (entityID != 0) + { + if (tud_audio_set_req_entity_cb) + { + // Check if entity is present and get corresponding driver index + TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id)); + + // Invoke callback + return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf); + } + else + { + TU_LOG2(" No entity set request callback available!\r\n"); + return false; // In case no callback function is present or request can not be conducted we stall it + } } else { - TU_LOG2(" No entity set request callback available!\r\n"); - return false; // In case no callback function is present or request can not be conducted we stall it + if (tud_audio_set_req_itf_cb) + { + // Find index of audio driver structure and verify interface really exists + TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); + + // Invoke callback + return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf); + } + else + { + TU_LOG2(" No interface set request callback available!\r\n"); + return false; // In case no callback function is present or request can not be conducted we stall it + } } } - else - { - if (tud_audio_set_req_itf_cb) - { - // Find index of audio driver structure and verify interface really exists - TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); - - // Invoke callback - return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf); - } - else - { - TU_LOG2(" No interface set request callback available!\r\n"); - return false; // In case no callback function is present or request can not be conducted we stall it - } - } - break; - case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label - - uint8_t ep = TU_U16_LOW(p_request->wIndex); - - if (tud_audio_set_req_ep_cb) + case TUSB_REQ_RCPT_ENDPOINT: { - // Check if entity is present and get corresponding driver index - TU_VERIFY(audiod_verify_ep_exists(ep, &func_id)); + uint8_t ep = TU_U16_LOW(p_request->wIndex); - // Invoke callback - return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf); - } - else - { - TU_LOG2(" No EP set request callback available!\r\n"); - return false; // In case no callback function is present or request can not be conducted we stall it - } + if (tud_audio_set_req_ep_cb) + { + // Check if entity is present and get corresponding driver index + TU_VERIFY(audiod_verify_ep_exists(ep, &func_id)); + // Invoke callback + return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf); + } + else + { + TU_LOG2(" No EP set request callback available!\r\n"); + return false; // In case no callback function is present or request can not be conducted we stall it + } + } + break; // Unknown/Unsupported recipient default: TU_BREAKPOINT(); return false; } @@ -1754,69 +1773,71 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const // Conduct checks which depend on the recipient switch (p_request->bmRequestType_bit.recipient) { - case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label - - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - - // Verify if entity is present - if (entityID != 0) + case TUSB_REQ_RCPT_INTERFACE: { - // Find index of audio driver structure and verify entity really exists - TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id)); + uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests - if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) + // Verify if entity is present + if (entityID != 0) { - if (tud_audio_get_req_entity_cb) + // Find index of audio driver structure and verify entity really exists + TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id)); + + // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests + if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) { - return tud_audio_get_req_entity_cb(rhport, p_request); - } - else - { - TU_LOG2(" No entity get request callback available!\r\n"); - return false; // Stall + if (tud_audio_get_req_entity_cb) + { + return tud_audio_get_req_entity_cb(rhport, p_request); + } + else + { + TU_LOG2(" No entity get request callback available!\r\n"); + return false; // Stall + } } } - } - else - { - // Find index of audio driver structure and verify interface really exists - TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); - - // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests - if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) + else { - if (tud_audio_get_req_itf_cb) + // Find index of audio driver structure and verify interface really exists + TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); + + // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests + if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) { - return tud_audio_get_req_itf_cb(rhport, p_request); - } - else - { - TU_LOG2(" No interface get request callback available!\r\n"); - return false; // Stall + if (tud_audio_get_req_itf_cb) + { + return tud_audio_get_req_itf_cb(rhport, p_request); + } + else + { + TU_LOG2(" No interface get request callback available!\r\n"); + return false; // Stall + } } } } break; - case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label - - uint8_t ep = TU_U16_LOW(p_request->wIndex); - - // Find index of audio driver structure and verify EP really exists - TU_VERIFY(audiod_verify_ep_exists(ep, &func_id)); - - // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests - if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) + case TUSB_REQ_RCPT_ENDPOINT: { - if (tud_audio_get_req_ep_cb) + uint8_t ep = TU_U16_LOW(p_request->wIndex); + + // Find index of audio driver structure and verify EP really exists + TU_VERIFY(audiod_verify_ep_exists(ep, &func_id)); + + // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests + if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) { - return tud_audio_get_req_ep_cb(rhport, p_request); - } - else - { - TU_LOG2(" No EP get request callback available!\r\n"); - return false; // Stall + if (tud_audio_get_req_ep_cb) + { + return tud_audio_get_req_ep_cb(rhport, p_request); + } + else + { + TU_LOG2(" No EP get request callback available!\r\n"); + return false; // Stall + } } } break; @@ -1913,8 +1934,12 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 { if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport)); - // Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent - return audiod_fb_send(rhport, &_audiod_fct[func_id]); + // Schedule a transmit with the new value if EP is not busy + if (!usbd_edpt_busy(rhport, _audiod_fct[func_id].ep_fb)) + { + // Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent + return audiod_fb_send(rhport, &_audiod_fct[func_id]); + } } #endif #endif @@ -1935,29 +1960,31 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req // Conduct checks which depend on the recipient switch (p_request->bmRequestType_bit.recipient) { - case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label - - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - - // Verify if entity is present - if (entityID != 0) + case TUSB_REQ_RCPT_INTERFACE: { - // Find index of audio driver structure and verify entity really exists - TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id)); - } - else - { - // Find index of audio driver structure and verify interface really exists - TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); + uint8_t entityID = TU_U16_HIGH(p_request->wIndex); + + // Verify if entity is present + if (entityID != 0) + { + // Find index of audio driver structure and verify entity really exists + TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id)); + } + else + { + // Find index of audio driver structure and verify interface really exists + TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); + } } break; - case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label + case TUSB_REQ_RCPT_ENDPOINT: + { + uint8_t ep = TU_U16_LOW(p_request->wIndex); - uint8_t ep = TU_U16_LOW(p_request->wIndex); - - // Find index of audio driver structure and verify EP really exists - TU_VERIFY(audiod_verify_ep_exists(ep, &func_id)); + // Find index of audio driver structure and verify EP really exists + TU_VERIFY(audiod_verify_ep_exists(ep, &func_id)); + } break; // Unknown/Unsupported recipient @@ -1992,15 +2019,17 @@ static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t * audio while (p_desc < p_desc_end) { // We assume the number of alternate settings is increasing thus we return the index of alternate setting zero! - if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf) + if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == 0) { - *idxItf = tmp; - *pp_desc_int = p_desc; - return true; + if (((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf) + { + *idxItf = tmp; + *pp_desc_int = p_desc; + return true; + } + // Increase index, bytes read, and pointer + tmp++; } - - // Increase index, bytes read, and pointer - tmp++; p_desc = tu_desc_next(p_desc); } } @@ -2137,10 +2166,10 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * if (as_itf == audio->ep_in_as_intf_num) { audio->n_channels_tx = ((audio_desc_cs_as_interface_t const * )p_desc)->bNrChannels; - audio->format_type_tx = ((audio_desc_cs_as_interface_t const * )p_desc)->bFormatType; + audio->format_type_tx = (audio_format_type_t)(((audio_desc_cs_as_interface_t const * )p_desc)->bFormatType); #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING - audio->format_type_I_tx = ((audio_desc_cs_as_interface_t const * )p_desc)->bmFormats; + audio->format_type_I_tx = (audio_data_format_type_I_t)(((audio_desc_cs_as_interface_t const * )p_desc)->bmFormats); #endif } #endif @@ -2202,22 +2231,19 @@ bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL); // Format the feedback value - if (_audiod_fct[func_id].rhport == 0) - { - uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].fb_val; +#if !TUD_OPT_HIGH_SPEED + uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].fb_val; - // For FS format is 10.14 - *(fb++) = (feedback >> 2) & 0xFF; - *(fb++) = (feedback >> 10) & 0xFF; - *(fb++) = (feedback >> 18) & 0xFF; - // 4th byte is needed to work correctly with MS Windows - *fb = 0; - } - else - { - // For HS format is 16.16 as originally demanded - _audiod_fct[func_id].fb_val = feedback; - } + // For FS format is 10.14 + *(fb++) = (feedback >> 2) & 0xFF; + *(fb++) = (feedback >> 10) & 0xFF; + *(fb++) = (feedback >> 18) & 0xFF; + // 4th byte is needed to work correctly with MS Windows + *fb = 0; +#else + // For HS format is 16.16 as originally demanded + _audiod_fct[func_id].fb_val = feedback; +#endif // Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value if (!usbd_edpt_busy(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_fb)) diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index a926081cc..68080c61d 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -273,9 +273,6 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1 TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass && CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0); - // Note: 0xFF can be used with RNDIS - TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA), 0); - // Find available interface cdcd_interface_t * p_cdc = NULL; for(uint8_t cdc_id=0; cdc_idep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress; + TU_ASSERT( usbd_edpt_open(rhport, desc_ep), 0 ); + p_cdc->ep_notif = desc_ep->bEndpointAddress; drv_len += tu_desc_len(p_desc); p_desc = tu_desc_next(p_desc); diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 33f1a8ad3..fc7100e81 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -149,29 +149,27 @@ void cdch_init(void) tu_memclr(cdch_data, sizeof(cdch_data_t)*CFG_TUSB_HOST_DEVICE_MAX); } -bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length) +uint16_t cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { - // Only support ACM - TU_VERIFY( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass); + (void) max_len; - // Only support AT commands, no protocol and vendor specific commands. - TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) || - 0xff == itf_desc->bInterfaceProtocol); + // Only support ACM subclass + // Protocol 0xFF can be RNDIS device for windows XP + TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass && + CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass && + 0xFF != itf_desc->bInterfaceProtocol, 0); - uint8_t const * p_desc; - cdch_data_t * p_cdc; + cdch_data_t * p_cdc = get_itf(dev_addr); - p_desc = tu_desc_next(itf_desc); - p_cdc = get_itf(dev_addr); - - p_cdc->itf_num = itf_desc->bInterfaceNumber; - p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; // TODO 0xff is consider as rndis candidate, other is virtual Com + p_cdc->itf_num = itf_desc->bInterfaceNumber; + p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; //------------- Communication Interface -------------// - (*p_length) = sizeof(tusb_desc_interface_t); + uint16_t drv_len = tu_desc_len(itf_desc); + uint8_t const * p_desc = tu_desc_next(itf_desc); // Communication Functional Descriptors - while( TUSB_DESC_CS_INTERFACE == p_desc[DESC_OFFSET_TYPE] ) + while( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len ) { if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) ) { @@ -179,52 +177,52 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities; } - (*p_length) += p_desc[DESC_OFFSET_LEN]; + drv_len += tu_desc_len(p_desc); p_desc = tu_desc_next(p_desc); } - if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE]) + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) { // notification endpoint - tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) p_desc; + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT( usbh_edpt_open(rhport, dev_addr, ep_desc) ); - p_cdc->ep_notif = ep_desc->bEndpointAddress; + TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep), 0 ); + p_cdc->ep_notif = desc_ep->bEndpointAddress; - (*p_length) += p_desc[DESC_OFFSET_LEN]; + drv_len += tu_desc_len(p_desc); p_desc = tu_desc_next(p_desc); } //------------- Data Interface (if any) -------------// - if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) && + if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) { - (*p_length) += p_desc[DESC_OFFSET_LEN]; + // next to endpoint descriptor + drv_len += tu_desc_len(p_desc); p_desc = tu_desc_next(p_desc); // data endpoints expected to be in pairs for(uint32_t i=0; i<2; i++) { - tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType); - TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer); + tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc; + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer, 0); - TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc)); + TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep), 0); - if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ) + if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) { - p_cdc->ep_in = ep_desc->bEndpointAddress; + p_cdc->ep_in = desc_ep->bEndpointAddress; }else { - p_cdc->ep_out = ep_desc->bEndpointAddress; + p_cdc->ep_out = desc_ep->bEndpointAddress; } - (*p_length) += p_desc[DESC_OFFSET_LEN]; + drv_len += tu_desc_len(p_desc); p_desc = tu_desc_next( p_desc ); } } - return true; + return drv_len; } bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num) diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index 6ff392709..edcd258a8 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -121,11 +121,11 @@ void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_i //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ -void cdch_init(void); -bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length); -bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num); -bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); -void cdch_close(uint8_t dev_addr); +void cdch_init (void); +uint16_t cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num); +bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void cdch_close (uint8_t dev_addr); #ifdef __cplusplus } diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index 81045ff7b..c696d70a2 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -247,7 +247,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_reque static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength) { - TU_VERIFY( wLength <= CFG_TUD_DFU_TRANSFER_BUFFER_SIZE); + TU_VERIFY( wLength <= CFG_TUD_DFU_TRANSFER_BUFFER_SIZE, 0); uint16_t retval = tud_dfu_req_upload_data_cb(block_num, (uint8_t *)_dfu_state_ctx.transfer_buf, wLength); tud_control_xfer(rhport, request, _dfu_state_ctx.transfer_buf, retval); return retval; @@ -276,6 +276,7 @@ static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * // if they wish, there still will be the internal control buffer copy to this buffer // but this mode would provide zero copy from the class driver to the application + TU_VERIFY( request->wLength <= CFG_TUD_DFU_TRANSFER_BUFFER_SIZE, ); // setup for data phase tud_control_xfer(rhport, request, _dfu_state_ctx.transfer_buf, request->wLength); } @@ -283,6 +284,7 @@ static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) { (void) rhport; + TU_VERIFY( request->wLength <= CFG_TUD_DFU_TRANSFER_BUFFER_SIZE, ); tud_dfu_req_dnload_data_cb(request->wValue, (uint8_t *)_dfu_state_ctx.transfer_buf, request->wLength); _dfu_state_ctx.blk_transfer_in_proc = false; } diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c index e44f282c4..a10e3a5e3 100644 --- a/src/class/hid/hid_device.c +++ b/src/class/hid/hid_device.c @@ -280,7 +280,21 @@ bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t uint8_t const report_type = tu_u16_high(request->wValue); uint8_t const report_id = tu_u16_low(request->wValue); - uint16_t xferlen = tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, p_hid->epin_buf, request->wLength); + uint8_t* report_buf = p_hid->epin_buf; + uint16_t req_len = request->wLength; + + uint16_t xferlen = 0; + + // If host request a specific Report ID, add ID to as 1 byte of response + if ( (report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1) ) + { + *report_buf++ = report_id; + req_len--; + + xferlen++; + } + + xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, req_len); TU_ASSERT( xferlen > 0 ); tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen); @@ -298,7 +312,17 @@ bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t uint8_t const report_type = tu_u16_high(request->wValue); uint8_t const report_id = tu_u16_low(request->wValue); - tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, p_hid->epout_buf, request->wLength); + uint8_t const* report_buf = p_hid->epout_buf; + uint16_t report_len = request->wLength; + + // If host request a specific Report ID, extract report ID in buffer before invoking callback + if ( (report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0]) ) + { + report_buf++; + report_len--; + } + + tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, report_len); } break; diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index cde5e23a5..40b6f5631 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -37,16 +37,6 @@ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ -/* - "KEYBOARD" : in_len=8 , out_len=1, usage_page=0x01, usage=0x06 # Generic Desktop, Keyboard - "MOUSE" : in_len=4 , out_len=0, usage_page=0x01, usage=0x02 # Generic Desktop, Mouse - "CONSUMER" : in_len=2 , out_len=0, usage_page=0x0C, usage=0x01 # Consumer, Consumer Control - "SYS_CONTROL" : in_len=1 , out_len=0, usage_page=0x01, usage=0x80 # Generic Desktop, Sys Control - "GAMEPAD" : in_len=6 , out_len=0, usage_page=0x01, usage=0x05 # Generic Desktop, Game Pad - "DIGITIZER" : in_len=5 , out_len=0, usage_page=0x0D, usage=0x02 # Digitizers, Pen - "XAC_COMPATIBLE_GAMEPAD" : in_len=3 , out_len=0, usage_page=0x01, usage=0x05 # Generic Desktop, Game Pad - "RAW" : in_len=64, out_len=0, usage_page=0xFFAF, usage=0xAF # Vendor 0xFFAF "Adafruit", 0xAF - */ typedef struct { uint8_t itf_num; @@ -108,7 +98,7 @@ uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance) return hid_itf->itf_protocol; } -bool tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance) +uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance) { hidh_interface_t* hid_itf = get_instance(dev_addr, instance); return hid_itf->protocol_mode; @@ -253,33 +243,37 @@ void hidh_close(uint8_t dev_addr) // Enumeration //--------------------------------------------------------------------+ -static bool config_get_protocol (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); +static bool config_set_protocol (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); static bool config_get_report_desc (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); static bool config_get_report_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); -bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t *p_length) +uint16_t hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) { - TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass); + (void) max_len; + TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0); + + uint16_t drv_len = sizeof(tusb_desc_interface_t); uint8_t const *p_desc = (uint8_t const *) desc_itf; //------------- HID descriptor -------------// p_desc = tu_desc_next(p_desc); tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc; - TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType); + TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType, 0); // not enough interface, try to increase CFG_TUH_HID // TODO multiple devices hidh_device_t* hid_dev = get_dev(dev_addr); - TU_ASSERT(hid_dev->inst_count < CFG_TUH_HID); + TU_ASSERT(hid_dev->inst_count < CFG_TUH_HID, 0); //------------- Endpoint Descriptor -------------// + drv_len += tu_desc_len(p_desc); p_desc = tu_desc_next(p_desc); tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType); + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType, 0); // TODO also open endpoint OUT - TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) ); + TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep), 0 ); hidh_interface_t* hid_itf = get_instance(dev_addr, hid_dev->inst_count); hid_dev->inst_count++; @@ -292,12 +286,13 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de hid_itf->report_desc_type = desc_hid->bReportType; hid_itf->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength); - hid_itf->protocol_mode = HID_PROTOCOL_REPORT; // Per Specs: default is report mode + // Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config + hid_itf->protocol_mode = HID_PROTOCOL_BOOT; if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass ) hid_itf->itf_protocol = desc_itf->bInterfaceProtocol; - *p_length = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t); + drv_len += desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t); - return true; + return drv_len; } bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num) @@ -324,43 +319,49 @@ bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num) .wLength = 0 }; - TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? config_get_protocol : config_get_report_desc) ); + TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? config_set_protocol : config_get_report_desc) ); return true; } -static bool config_get_protocol(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) +// Force device to work in BOOT protocol +static bool config_set_protocol(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) { - // Stall is a valid response for SET_IDLE GET_PROTOCOL, therefore we could ignore its result + // Stall is a valid response for SET_IDLE, therefore we could ignore its result (void) result; uint8_t const itf_num = (uint8_t) request->wIndex; uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); hidh_interface_t* hid_itf = get_instance(dev_addr, instance); - TU_LOG2("HID Get Protocol\r\n"); + TU_LOG2("HID Set Protocol\r\n"); + hid_itf->protocol_mode = HID_PROTOCOL_BOOT; tusb_control_request_t const new_request = { .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_IN + .direction = TUSB_DIR_OUT }, - .bRequest = HID_REQ_CONTROL_GET_PROTOCOL, - .wValue = 0, + .bRequest = HID_REQ_CONTROL_SET_PROTOCOL, + .wValue = HID_PROTOCOL_BOOT, .wIndex = hid_itf->itf_num, - .wLength = 1 + .wLength = 0 }; - TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, &hid_itf->protocol_mode, config_get_report_desc) ); - return false; + TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, NULL, config_get_report_desc) ); + return true; } static bool config_get_report_desc(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) { - // Stall is a valid response for SET_IDLE GET_PROTOCOL, therefore we could ignore its result - (void) result; + // We can be here after SET_IDLE or SET_PROTOCOL (boot device) + // Trigger assert if result is not successful with set protocol + if ( request->bRequest != HID_REQ_CONTROL_SET_IDLE ) + { + TU_ASSERT(result == XFER_RESULT_SUCCESS); + } uint8_t const itf_num = (uint8_t) request->wIndex; uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); @@ -368,7 +369,7 @@ static bool config_get_report_desc(uint8_t dev_addr, tusb_control_request_t cons // Get Report Descriptor // using usbh enumeration buffer since report descriptor can be very long - TU_ASSERT( hid_itf->report_desc_len <= CFG_TUH_ENUMERATION_BUFSZIE ); + TU_ASSERT( hid_itf->report_desc_len <= CFG_TUH_ENUMERATION_BUFSIZE ); TU_LOG2("HID Get Report Descriptor\r\n"); tusb_control_request_t const new_request = @@ -452,9 +453,9 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t const data8 = desc_report[0]; - TU_LOG2("tag = %d, type = %d, size = %d, data = ", tag, type, size); - for(uint32_t i=0; irx_ff, packet, 4); - _prep_out_transaction(p_midi); + midid_interface_t* midi = &_midid_itf[itf]; + TU_VERIFY(midi->ep_out); + + uint32_t const num_read = tu_fifo_read_n(&midi->rx_ff, packet, 4); + _prep_out_transaction(midi); return (num_read == 4); } @@ -234,19 +236,19 @@ static uint32_t write_flush(midid_interface_t* midi) uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize) { midid_interface_t* midi = &_midid_itf[itf]; - TU_VERIFY(midi->itf_num, 0); + TU_VERIFY(midi->ep_in, 0); midid_stream_t* stream = &midi->stream_write; - uint32_t total_written = 0; uint32_t i = 0; - while ( i < bufsize ) + while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) ) { uint8_t const data = buffer[i]; + i++; if ( stream->index == 0 ) { - // new event packet + //------------- New event packet -------------// uint8_t const msg = data >> 4; @@ -308,9 +310,9 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* } else { - // On-going (buffering) packet + //------------- On-going (buffering) packet -------------// - TU_ASSERT(stream->index < 4, total_written); + TU_ASSERT(stream->index < 4, i); stream->buffer[stream->index] = data; stream->index++; @@ -333,27 +335,20 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* // complete current event packet, reset stream stream->index = stream->total = 0; - // fifo overflow, here we assume FIFO is multiple of 4 and didn't check remaining before writing - if ( count != 4 ) break; - - // updated written if succeeded - total_written = i; + // FIFO overflown, since we already check fifo remaining. It is probably race condition + TU_ASSERT(count == 4, i); } - - i++; } write_flush(midi); - return total_written; + return i; } bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4]) { midid_interface_t* midi = &_midid_itf[itf]; - if (midi->itf_num == 0) { - return 0; - } + TU_VERIFY(midi->ep_in); if (tu_fifo_remaining(&midi->tx_ff) < 4) return false; @@ -435,6 +430,7 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint } p_midi->itf_num = desc_midi->bInterfaceNumber; + (void) p_midi->itf_num; // next descriptor drv_len += tu_desc_len(p_desc); diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index 7f13b4891..014e8b69e 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -37,6 +37,10 @@ //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ + +// Can be selectively disabled to reduce logging when troubleshooting other driver +#define MSC_DEBUG 2 + enum { MSC_STAGE_CMD = 0, @@ -99,7 +103,7 @@ static inline uint16_t rdwr10_get_blockcount(uint8_t const command[]) //--------------------------------------------------------------------+ #if CFG_TUSB_DEBUG >= 2 -static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] = +TU_ATTR_UNUSED static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] = { { .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" }, { .key = SCSI_CMD_INQUIRY , .data = "Inquiry" }, @@ -114,7 +118,7 @@ static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] = { .key = SCSI_CMD_WRITE_10 , .data = "Write10" } }; -static tu_lookup_table_t const _msc_scsi_cmd_table = +TU_ATTR_UNUSED static tu_lookup_table_t const _msc_scsi_cmd_table = { .count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup), .items = _msc_scsi_cmd_lookup @@ -232,8 +236,8 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t TU_ASSERT( event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE ); - TU_LOG2(" SCSI Command: %s\r\n", tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0])); - // TU_LOG2_MEM(p_cbw, xferred_bytes, 2); + TU_LOG(MSC_DEBUG, " SCSI Command: %s\r\n", tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0])); + // TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2); p_csw->signature = MSC_CSW_SIGNATURE; p_csw->tag = p_cbw->tag; @@ -305,8 +309,8 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t break; case MSC_STAGE_DATA: - TU_LOG2(" SCSI Data\r\n"); - //TU_LOG2_MEM(_mscd_buf, xferred_bytes, 2); + TU_LOG(MSC_DEBUG, " SCSI Data\r\n"); + //TU_LOG_MEM(MSC_DEBUG, _mscd_buf, xferred_bytes, 2); // OUT transfer, invoke callback if needed if ( !tu_bit_test(p_cbw->dir, 7) ) @@ -402,8 +406,8 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // Wait for the Status phase to complete if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) ) { - TU_LOG2(" SCSI Status: %u\r\n", p_csw->status); - // TU_LOG2_MEM(p_csw, xferred_bytes, 2); + TU_LOG(MSC_DEBUG, " SCSI Status: %u\r\n", p_csw->status); + // TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2); // Invoke complete callback if defined // Note: There is racing issue with samd51 + qspi flash testing with arduino diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c index 7f98ef9b1..2c027deff 100644 --- a/src/class/msc/msc_host.c +++ b/src/class/msc/msc_host.c @@ -360,18 +360,22 @@ static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* c static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw); static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw); -bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t *p_length) +uint16_t msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) { TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass && MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol); + // msc driver length is fixed + uint16_t const drv_len = sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t); + TU_ASSERT(drv_len <= max_len, 0); + msch_interface_t* p_msc = get_itf(dev_addr); tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(desc_itf); for(uint32_t i=0; i<2; i++) { - TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer); - TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc)); + TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer, 0); + TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc), 0); if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ) { @@ -385,9 +389,8 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de } p_msc->itf_num = desc_itf->bInterfaceNumber; - (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t); - return true; + return drv_len; } bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h index ce4fe64dc..9e4217ba5 100644 --- a/src/class/msc/msc_host.h +++ b/src/class/msc/msc_host.h @@ -41,13 +41,6 @@ #define CFG_TUH_MSC_MAXLUN 4 #endif - -/** \addtogroup ClassDriver_MSC - * @{ - * \defgroup MSC_Host Host - * The interface API includes status checking function, data transferring function and callback functions - * @{ */ - typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw); //--------------------------------------------------------------------+ @@ -113,17 +106,14 @@ TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr); // Internal Class Driver API //--------------------------------------------------------------------+ -void msch_init(void); -bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t *p_length); -bool msch_set_config(uint8_t dev_addr, uint8_t itf_num); -bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); -void msch_close(uint8_t dev_addr); +void msch_init (void); +uint16_t msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len); +bool msch_set_config (uint8_t dev_addr, uint8_t itf_num); +bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void msch_close (uint8_t dev_addr); #ifdef __cplusplus } #endif #endif /* _TUSB_MSC_HOST_H_ */ - -/// @} -/// @} diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index 889ad7b25..c8a66a879 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -123,7 +123,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4k (uint32_t value) { retur TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); } //------------- Mathematics -------------// -TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_abs(int32_t value) { return (uint32_t)((value < 0) ? (-value) : value); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return (v + d -1)/d; } /// inclusive range checking TODO remove TU_ATTR_ALWAYS_INLINE static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper) @@ -305,11 +305,11 @@ void tu_print_var(uint8_t const* buf, uint32_t bufsize) } // Log with Level -#define TU_LOG(n, ...) TU_LOG##n(__VA_ARGS__) -#define TU_LOG_MEM(n, ...) TU_LOG##n##_MEM(__VA_ARGS__) -#define TU_LOG_VAR(n, ...) TU_LOG##n##_VAR(__VA_ARGS__) -#define TU_LOG_INT(n, ...) TU_LOG##n##_INT(__VA_ARGS__) -#define TU_LOG_HEX(n, ...) TU_LOG##n##_HEX(__VA_ARGS__) +#define TU_LOG(n, ...) TU_XSTRCAT(TU_LOG, n)(__VA_ARGS__) +#define TU_LOG_MEM(n, ...) TU_XSTRCAT3(TU_LOG, n, _MEM)(__VA_ARGS__) +#define TU_LOG_VAR(n, ...) TU_XSTRCAT3(TU_LOG, n, _VAR)(__VA_ARGS__) +#define TU_LOG_INT(n, ...) TU_XSTRCAT3(TU_LOG, n, _INT)(__VA_ARGS__) +#define TU_LOG_HEX(n, ...) TU_XSTRCAT3(TU_LOG, n, _HEX)(__VA_ARGS__) #define TU_LOG_LOCATION() tu_printf("%s: %d:\r\n", __PRETTY_FUNCTION__, __LINE__) #define TU_LOG_FAILED() tu_printf("%s: %d: Failed\r\n", __PRETTY_FUNCTION__, __LINE__) @@ -317,8 +317,8 @@ void tu_print_var(uint8_t const* buf, uint32_t bufsize) #define TU_LOG1 tu_printf #define TU_LOG1_MEM tu_print_mem #define TU_LOG1_VAR(_x) tu_print_var((uint8_t const*)(_x), sizeof(*(_x))) -#define TU_LOG1_INT(_x) tu_printf(#_x " = %ld\n", (uint32_t) (_x) ) -#define TU_LOG1_HEX(_x) tu_printf(#_x " = %lX\n", (uint32_t) (_x) ) +#define TU_LOG1_INT(_x) tu_printf(#_x " = %ld\r\n", (uint32_t) (_x) ) +#define TU_LOG1_HEX(_x) tu_printf(#_x " = %lX\r\n", (uint32_t) (_x) ) // Log Level 2: Warn #if CFG_TUSB_DEBUG >= 2 @@ -373,6 +373,14 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3 #endif // TODO replace all TU_LOGn with TU_LOG(n) + +#define TU_LOG0(...) +#define TU_LOG0_MEM(...) +#define TU_LOG0_VAR(...) +#define TU_LOG0_INT(...) +#define TU_LOG0_HEX(...) + + #ifndef TU_LOG1 #define TU_LOG1(...) #define TU_LOG1_MEM(...) diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h index 679060b20..0dd9ba016 100644 --- a/src/common/tusb_compiler.h +++ b/src/common/tusb_compiler.h @@ -32,10 +32,14 @@ #ifndef _TUSB_COMPILER_H_ #define _TUSB_COMPILER_H_ -#define TU_STRING(x) #x ///< stringify without expand -#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify -#define TU_STRCAT(a, b) a##b ///< concat without expand -#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat +#define TU_STRING(x) #x ///< stringify without expand +#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify + +#define TU_STRCAT(a, b) a##b ///< concat without expand +#define TU_STRCAT3(a, b, c) a##b##c ///< concat without expand + +#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat +#define TU_XSTRCAT3(a, b, c) TU_STRCAT3(a, b, c) ///< expand then concat 3 tokens #if defined __COUNTER__ && __COUNTER__ != __COUNTER__ #define _TU_COUNTER_ __COUNTER__ @@ -83,6 +87,10 @@ #define TU_BSWAP16(u16) (__builtin_bswap16(u16)) #define TU_BSWAP32(u32) (__builtin_bswap32(u32)) + // List of obsolete callback function that is renamed and should not be defined. + // Put it here since only gcc support this pragma + #pragma GCC poison tud_vendor_control_request_cb + #elif defined(__TI_COMPILER_VERSION__) #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index 1eb886aa1..73217cf75 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -325,7 +325,7 @@ static uint16_t advance_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset) // We are exploiting the wrap around to the correct index // TODO warning: assuming signed overflow does not occur when assuming that (X + c) < X is always false [-Wstrict-overflow] - if ((p > p + offset) || (p + offset > f->max_pointer_idx)) + if ((p > (uint16_t)(p + offset)) || ((uint16_t)(p + offset) > f->max_pointer_idx)) { p = (p + offset) + f->non_used_index_space; } @@ -342,7 +342,7 @@ static uint16_t backward_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset) // We limit the index space of p such that a correct wrap around happens // Check for a wrap around or if we are in unused index space - This has to be checked first!! // We are exploiting the wrap around to the correct index - if ((p < p - offset) || (p - offset > f->max_pointer_idx)) + if ((p < (uint16_t)(p - offset)) || ((uint16_t)(p - offset) > f->max_pointer_idx)) { p = (p - offset) - f->non_used_index_space; } diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h index cf299269d..18db289a1 100644 --- a/src/common/tusb_fifo.h +++ b/src/common/tusb_fifo.h @@ -86,8 +86,8 @@ typedef struct .depth = _depth, \ .item_size = sizeof(_type), \ .overwritable = _overwritable, \ - .max_pointer_idx = 2*(_depth)-1, \ .non_used_index_space = UINT16_MAX - (2*(_depth)-1), \ + .max_pointer_idx = 2*(_depth)-1, \ } #define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \ @@ -120,9 +120,9 @@ bool tu_fifo_peek (tu_fifo_t* f, void * p_buffer); uint16_t tu_fifo_peek_n (tu_fifo_t* f, void * p_buffer, uint16_t n); uint16_t tu_fifo_count (tu_fifo_t* f); +uint16_t tu_fifo_remaining (tu_fifo_t* f); bool tu_fifo_empty (tu_fifo_t* f); bool tu_fifo_full (tu_fifo_t* f); -uint16_t tu_fifo_remaining (tu_fifo_t* f); bool tu_fifo_overflowed (tu_fifo_t* f); void tu_fifo_correct_read_pointer (tu_fifo_t* f); diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h index 542809899..56ba8bcd1 100644 --- a/src/common/tusb_verify.h +++ b/src/common/tusb_verify.h @@ -75,7 +75,7 @@ #if CFG_TUSB_DEBUG #include #define _MESS_ERR(_err) tu_printf("%s %d: failed, error = %s\r\n", __func__, __LINE__, tusb_strerr[_err]) - #define _MESS_FAILED() tu_printf("%s %d: assert failed\r\n", __func__, __LINE__) + #define _MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__) #else #define _MESS_ERR(_err) do {} while (0) #define _MESS_FAILED() do {} while (0) diff --git a/src/device/usbd.c b/src/device/usbd.c index 2935defd0..af4fd58c4 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -1243,7 +1243,7 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes); + TU_LOG2(" Queue EP %02X with %u bytes ...\r\n", ep_addr, total_bytes); // Attempt to transfer on a busy endpoint, sound like an race condition ! TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0); @@ -1254,14 +1254,13 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t if ( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) ) { - TU_LOG2("OK\r\n"); return true; }else { // DCD error, mark endpoint as ready to allow next transfer _usbd_dev.ep_status[epnum][dir].busy = false; _usbd_dev.ep_status[epnum][dir].claimed = 0; - TU_LOG2("failed\r\n"); + TU_LOG2("FAILED\r\n"); TU_BREAKPOINT(); return false; } diff --git a/src/device/usbd.h b/src/device/usbd.h index 0a0f0bf00..2de6a5cfd 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -542,6 +542,11 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb /* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\ TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_interval*/ 1)\ +// Calculate wMaxPacketSize of Endpoints +#define TUD_AUDIO_EP_SIZE(_maxFrequency, _nBytesPerSample, _nChannels) \ + ((((_maxFrequency + ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 7999 : 999)) / ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 8000 : 1000)) + 1) * _nBytesPerSample * _nChannels) + + //------------- TUD_USBTMC/USB488 -------------// #define TUD_USBTMC_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC) #define TUD_USBTMC_APP_SUBCLASS 0x03u diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index 724c652e6..7a8244699 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -186,6 +186,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result { TU_VERIFY(_ctrl_xfer.buffer); memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes); + TU_LOG_MEM(2, _usbd_ctrl_buf, xferred_bytes, 2); } _ctrl_xfer.total_xferred += xferred_bytes; diff --git a/src/host/hub.c b/src/host/hub.c index b2761184d..2ead5bed1 100644 --- a/src/host/hub.c +++ b/src/host/hub.c @@ -144,29 +144,32 @@ bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp, tuh_con //--------------------------------------------------------------------+ void hub_init(void) { - tu_memclr(hub_data, CFG_TUSB_HOST_DEVICE_MAX*sizeof( hub_interface_t)); + tu_memclr(hub_data, CFG_TUSB_HOST_DEVICE_MAX*sizeof(hub_interface_t)); } -bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length) +uint16_t hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { - // not support multiple TT yet - if ( itf_desc->bInterfaceProtocol > 1 ) return false; + // hub driver does not support multiple TT yet + TU_VERIFY(TUSB_CLASS_HUB == itf_desc->bInterfaceClass && + 0 == itf_desc->bInterfaceSubClass && + 1 <= itf_desc->bInterfaceProtocol, 0); - //------------- Open Interrupt Status Pipe -------------// - tusb_desc_endpoint_t const *ep_desc; - ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + // msc driver length is fixed + uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t); + TU_ASSERT(drv_len <= max_len, 0); - TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType); - TU_ASSERT(TUSB_XFER_INTERRUPT == ep_desc->bmAttributes.xfer); + //------------- Interrupt Status endpoint -------------// + tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && + TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0); - TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc)); + TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep)); hub_data[dev_addr-1].itf_num = itf_desc->bInterfaceNumber; - hub_data[dev_addr-1].ep_in = ep_desc->bEndpointAddress; + hub_data[dev_addr-1].ep_in = desc_ep->bEndpointAddress; - (*p_length) = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t); - - return true; + return drv_len; } void hub_close(uint8_t dev_addr) diff --git a/src/host/hub.h b/src/host/hub.h index a5111b8e7..c9ffe4985 100644 --- a/src/host/hub.h +++ b/src/host/hub.h @@ -181,11 +181,11 @@ bool hub_status_pipe_queue(uint8_t dev_addr); //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ -void hub_init(void); -bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length); -bool hub_set_config(uint8_t dev_addr, uint8_t itf_num); -bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); -void hub_close(uint8_t dev_addr); +void hub_init (void); +uint16_t hub_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool hub_set_config (uint8_t dev_addr, uint8_t itf_num); +bool hub_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void hub_close (uint8_t dev_addr); #ifdef __cplusplus } diff --git a/src/host/usbh.c b/src/host/usbh.c index 59246a663..93621c42a 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -130,7 +130,7 @@ CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[CFG_TUSB_HOST_DEVICE_MAX+1]; OSAL_QUEUE_DEF(OPT_MODE_HOST, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t); static osal_queue_t _usbh_q; -CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSZIE]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE]; //------------- Helper Function Prototypes -------------// static bool enum_new_device(hcd_event_t* event); @@ -795,6 +795,8 @@ static bool enum_get_addr0_device_desc_complete(uint8_t dev_addr, tusb_control_r return false; } + TU_ASSERT(tu_desc_type(_usbh_ctrl_buf) == TUSB_DESC_DEVICE); + // Reset device again before Set Address TU_LOG2("Port reset \r\n"); @@ -907,7 +909,7 @@ static bool enum_get_9byte_config_desc_complete(uint8_t dev_addr, tusb_control_r // Use offsetof to avoid pointer to the odd/misaligned address memcpy(&total_len, (uint8_t*) desc_config + offsetof(tusb_desc_configuration_t, wTotalLength), 2); - TU_ASSERT(total_len <= CFG_TUH_ENUMERATION_BUFSZIE); + TU_ASSERT(total_len <= CFG_TUH_ENUMERATION_BUFSIZE); // Get full configuration descriptor TU_LOG2("Get Configuration Descriptor\r\n"); @@ -938,7 +940,7 @@ static bool enum_get_config_desc_complete(uint8_t dev_addr, tusb_control_request // Parse configuration & set up drivers // Driver open aren't allowed to make any usb transfer yet - parse_configuration_descriptor(dev_addr, (tusb_desc_configuration_t*) _usbh_ctrl_buf); + TU_ASSERT( parse_configuration_descriptor(dev_addr, (tusb_desc_configuration_t*) _usbh_ctrl_buf) ); TU_LOG2("Set Configuration = %d\r\n", CONFIG_NUM); tusb_control_request_t const new_request = @@ -982,55 +984,61 @@ static bool enum_set_config_complete(uint8_t dev_addr, tusb_control_request_t co static bool parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg) { usbh_device_t* dev = &_usbh_devices[dev_addr]; - uint8_t const* p_desc = (uint8_t const*) desc_cfg; - p_desc = tu_desc_next(p_desc); + + uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + tu_le16toh(desc_cfg->wTotalLength); + uint8_t const* p_desc = tu_desc_next(desc_cfg); // parse each interfaces - while( p_desc < _usbh_ctrl_buf + desc_cfg->wTotalLength ) + while( p_desc < desc_end ) { - // skip until we see interface descriptor - if ( TUSB_DESC_INTERFACE != tu_desc_type(p_desc) ) - { - p_desc = tu_desc_next(p_desc); // skip the descriptor, increase by the descriptor's length - }else - { - tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc; + // TODO Do we need to use IAD + // tusb_desc_interface_assoc_t const * desc_itf_assoc = NULL; - // Check if class is supported - uint8_t drv_id; - for (drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) - { - if ( usbh_class_drivers[drv_id].class_code == desc_itf->bInterfaceClass ) break; - } + // Class will always starts with Interface Association (if any) and then Interface descriptor + if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) + { + // desc_itf_assoc = (tusb_desc_interface_assoc_t const *) p_desc; + p_desc = tu_desc_next(p_desc); + } - if( drv_id >= USBH_CLASS_DRIVER_COUNT ) + TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) ); + + tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc; + uint16_t const remaining_len = desc_end-p_desc; + + // Check if class is supported TODO drop class_code + uint8_t drv_id; + for (drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) + { + if ( usbh_class_drivers[drv_id].class_code == desc_itf->bInterfaceClass ) break; + } + + if( drv_id >= USBH_CLASS_DRIVER_COUNT ) + { + // skip unsupported class + p_desc = tu_desc_next(p_desc); + } + else + { + usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id]; + + // Interface number must not be used already TODO alternate interface + TU_ASSERT( dev->itf2drv[desc_itf->bInterfaceNumber] == 0xff ); + dev->itf2drv[desc_itf->bInterfaceNumber] = drv_id; + + if (desc_itf->bInterfaceClass == TUSB_CLASS_HUB && dev->hub_addr != 0) { - // skip unsupported class + // TODO Attach hub to Hub is not currently supported + // skip this interface p_desc = tu_desc_next(p_desc); } else { - usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id]; + TU_LOG2("%s open\r\n", driver->name); - // Interface number must not be used already TODO alternate interface - TU_ASSERT( dev->itf2drv[desc_itf->bInterfaceNumber] == 0xff ); - dev->itf2drv[desc_itf->bInterfaceNumber] = drv_id; - - if (desc_itf->bInterfaceClass == TUSB_CLASS_HUB && dev->hub_addr != 0) - { - // TODO Attach hub to Hub is not currently supported - // skip this interface - p_desc = tu_desc_next(p_desc); - } - else - { - TU_LOG2("%s open\r\n", driver->name); - - uint16_t itf_len = 0; - TU_ASSERT( driver->open(dev->rhport, dev_addr, desc_itf, &itf_len) ); - TU_ASSERT( itf_len >= sizeof(tusb_desc_interface_t) ); - p_desc += itf_len; - } + uint16_t const itf_len = driver->open(dev->rhport, dev_addr, desc_itf, remaining_len); + TU_ASSERT( sizeof(tusb_desc_interface_t) <= itf_len && itf_len <= remaining_len); + p_desc += itf_len; } } } diff --git a/src/host/usbh_classdriver.h b/src/host/usbh_classdriver.h index 07480fe8e..0736fefa1 100644 --- a/src/host/usbh_classdriver.h +++ b/src/host/usbh_classdriver.h @@ -45,11 +45,11 @@ typedef struct { uint8_t class_code; - void (* const init )(void); - bool (* const open )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t* outlen); - bool (* const set_config )(uint8_t dev_addr, uint8_t itf_num); - bool (* const xfer_cb )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); - void (* const close )(uint8_t dev_addr); + void (* const init )(void); + uint16_t (* const open )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t max_len); + bool (* const set_config )(uint8_t dev_addr, uint8_t itf_num); + bool (* const xfer_cb )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + void (* const close )(uint8_t dev_addr); } usbh_class_driver_t; // Call by class driver to tell USBH that it has complete the enumeration diff --git a/src/host/usbh_control.c b/src/host/usbh_control.c index 4dbf8592a..0bdb66fb5 100644 --- a/src/host/usbh_control.c +++ b/src/host/usbh_control.c @@ -50,7 +50,7 @@ typedef struct static usbh_control_xfer_t _ctrl_xfer; //CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN -//static uint8_t _tuh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSZIE]; +//static uint8_t _tuh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE]; //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM DECLARATION @@ -68,7 +68,7 @@ bool tuh_control_xfer (uint8_t dev_addr, tusb_control_request_t const* request, _ctrl_xfer.stage = STAGE_SETUP; _ctrl_xfer.complete_cb = complete_cb; - TU_LOG2("Control Setup: "); + TU_LOG2("Control Setup (addr = %u): ", dev_addr); TU_LOG2_VAR(request); TU_LOG2("\r\n"); @@ -119,7 +119,7 @@ bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t resu if (request->wLength) { - TU_LOG2("Control data:\r\n"); + TU_LOG2("Control data (addr = %u):\r\n", dev_addr); TU_LOG2_MEM(_ctrl_xfer.buffer, request->wLength, 2); } diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c index 13e56453d..577bd0e05 100644 --- a/src/portable/microchip/samd/dcd_samd.c +++ b/src/portable/microchip/samd/dcd_samd.c @@ -29,7 +29,7 @@ #if TUSB_OPT_DEVICE_ENABLED && \ (CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \ CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X || \ - CFG_TUSB_MCU == OPT_MCU_SAML22) + CFG_TUSB_MCU == OPT_MCU_SAML22 || CFG_TUSB_MCU == OPT_MCU_SAML21) #include "sam.h" #include "device/dcd.h" @@ -125,7 +125,7 @@ void dcd_int_disable(uint8_t rhport) } #elif CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \ - CFG_TUSB_MCU == OPT_MCU_SAML22 + CFG_TUSB_MCU == OPT_MCU_SAML22 || CFG_TUSB_MCU == OPT_MCU_SAML21 void dcd_int_enable(uint8_t rhport) { diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index 977ec6cdb..5f4fe08ad 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -108,13 +108,21 @@ static inline uint32_t NVIC_GetEnableIRQ(IRQn_Type IRQn) } #endif +// check if we are in ISR +TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void) +{ + return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false; +} + // helper to start DMA +// TODO use Cortex M4 LDREX and STREX command (atomic) to have better mutex access to EasyDMA +// since current implementation does not 100% guarded against race condition static void edpt_dma_start(volatile uint32_t* reg_startep) { // Only one dma can be active if ( _dcd.dma_pending ) { - if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) + if (is_in_isr()) { // Called within ISR, use usbd task to defer later usbd_defer_func( (osal_task_func_t) edpt_dma_start, (void*) reg_startep, true ); @@ -159,6 +167,17 @@ static void edpt_dma_end(void) _dcd.dma_pending = 0; } +// helper to set TASKS_EP0STATUS / TASKS_EP0RCVOUT since they also need EasyDMA +// However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently +// Therefore dma_running state will be corrected right away +void start_ep0_task(volatile uint32_t* reg_task) +{ + edpt_dma_start(reg_task); + + // correct the dma_running++ in dma start + if (_dcd.dma_pending) _dcd.dma_pending--; +} + // helper getting td static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir) { @@ -407,21 +426,18 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t if ( control_status ) { - // Status Phase also requires Easy DMA has to be available as well !!!! - // However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently - // Therefore dma_running state will be corrected right away - edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS); - if (_dcd.dma_pending) _dcd.dma_pending--; // correct the dma_running++ in dma start + // Status Phase also requires EasyDMA has to be available as well !!!! + start_ep0_task(&NRF_USBD->TASKS_EP0STATUS); // The nRF doesn't interrupt on status transmit so we queue up a success response. - dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, false); + dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, is_in_isr()); } else if ( dir == TUSB_DIR_OUT ) { if ( epnum == 0 ) { - // Accept next Control Out packet - NRF_USBD->TASKS_EP0RCVOUT = 1; + // Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA + start_ep0_task(&NRF_USBD->TASKS_EP0RCVOUT); }else { if ( xfer->data_received ) @@ -581,12 +597,6 @@ void dcd_int_handler(uint8_t rhport) } } - if ( int_status & EDPT_END_ALL_MASK ) - { - // DMA complete move data from SRAM -> Endpoint - edpt_dma_end(); - } - // Setup tokens are specific to the Control endpoint. if ( int_status & USBD_INTEN_EP0SETUP_Msk ) { @@ -607,6 +617,12 @@ void dcd_int_handler(uint8_t rhport) } } + if ( int_status & EDPT_END_ALL_MASK ) + { + // DMA complete move data from SRAM -> Endpoint + edpt_dma_end(); + } + //--------------------------------------------------------------------+ /* Control/Bulk/Interrupt (CBI) Transfer * @@ -655,8 +671,15 @@ void dcd_int_handler(uint8_t rhport) { if ( epnum == 0 ) { - // Accept next Control Out packet - NRF_USBD->TASKS_EP0RCVOUT = 1; + // Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA + if ( _dcd.dma_pending ) + { + // use usbd task to defer later + usbd_defer_func( (osal_task_func_t) start_ep0_task, (void*) &NRF_USBD->TASKS_EP0RCVOUT, true ); + }else + { + start_ep0_task(&NRF_USBD->TASKS_EP0RCVOUT); + } }else { // nRF auto accept next Bulk/Interrupt OUT packet @@ -764,7 +787,7 @@ static bool hfclk_running(void) #ifdef SOFTDEVICE_PRESENT if ( is_sd_enabled() ) { - uint32_t is_running; + uint32_t is_running = 0; (void) sd_clock_hfclk_is_running(&is_running); return (is_running ? true : false); } @@ -973,7 +996,7 @@ void tusb_hal_nrf_power_event (uint32_t event) hfclk_disable(); - dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false); + dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, is_in_isr()); } break; diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 0048270a6..f78a932f1 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -64,67 +64,48 @@ static struct hw_endpoint *hw_endpoint_get_by_addr(uint8_t ep_addr) static void _hw_endpoint_alloc(struct hw_endpoint *ep) { - uint16_t size = tu_min16(64, ep->wMaxPacketSize); + // size must be multiple of 64 + uint16_t size = tu_div_ceil(ep->wMaxPacketSize, 64) * 64u; - // Assumes single buffered for now - ep->hw_data_buf = next_buffer_ptr; - next_buffer_ptr += size; - // Bits 0-5 are ignored by the controller so make sure these are 0 - if ((uintptr_t)next_buffer_ptr & 0b111111u) - { - // Round up to the next 64 - uint32_t fixptr = (uintptr_t)next_buffer_ptr; - fixptr &= ~0b111111u; - fixptr += 64; - pico_info("Rounding non 64 byte boundary buffer up from %x to %x\n", (uintptr_t)next_buffer_ptr, fixptr); - next_buffer_ptr = (uint8_t*)fixptr; - } - assert(((uintptr_t)next_buffer_ptr & 0b111111u) == 0); - uint dpram_offset = hw_data_offset(ep->hw_data_buf); - assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX); + // double buffered for Control and Bulk endpoint + if ( ep->transfer_type == TUSB_XFER_CONTROL || ep->transfer_type == TUSB_XFER_BULK) + { + size *= 2u; + } - pico_info("Alloced %d bytes at offset 0x%x (0x%p) for ep %d %s\n", - size, - dpram_offset, - ep->hw_data_buf, - ep->num, - ep_dir_string[ep->in]); + ep->hw_data_buf = next_buffer_ptr; + next_buffer_ptr += size; - // Fill in endpoint control register with buffer offset - uint32_t reg = EP_CTRL_ENABLE_BITS - | EP_CTRL_INTERRUPT_PER_BUFFER - | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) - | dpram_offset; + assert(((uintptr_t )next_buffer_ptr & 0b111111u) == 0); + uint dpram_offset = hw_data_offset(ep->hw_data_buf); + assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX); - *ep->endpoint_control = reg; + pico_info("Alloced %d bytes at offset 0x%x (0x%p) for ep %d %s\n", + size, + dpram_offset, + ep->hw_data_buf, + tu_edpt_number(ep->ep_addr), + ep_dir_string[tu_edpt_dir(ep->ep_addr)]); + + // Fill in endpoint control register with buffer offset + uint32_t const reg = EP_CTRL_ENABLE_BITS | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; + + *ep->endpoint_control = reg; } static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { const uint8_t num = tu_edpt_number(ep_addr); const tusb_dir_t dir = tu_edpt_dir(ep_addr); + ep->ep_addr = ep_addr; + // For device, IN is a tx transfer and OUT is an rx transfer ep->rx = (dir == TUSB_DIR_OUT); + // Response to a setup packet on EP0 starts with pid of 1 ep->next_pid = num == 0 ? 1u : 0u; - // Add some checks around the max packet size - if (transfer_type == TUSB_XFER_ISOCHRONOUS) - { - if (wMaxPacketSize > USB_MAX_ISO_PACKET_SIZE) - { - panic("Isochronous wMaxPacketSize %d too large", wMaxPacketSize); - } - } - else - { - if (wMaxPacketSize > USB_MAX_PACKET_SIZE) - { - panic("Isochronous wMaxPacketSize %d too large", wMaxPacketSize); - } - } - ep->wMaxPacketSize = wMaxPacketSize; ep->transfer_type = transfer_type; @@ -164,6 +145,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t ep_addr, uint16_t // Now if it hasn't already been done //alloc a buffer and fill in endpoint control register + // TODO device may change configuration (dynamic), should clear and reallocate if(!(ep->configured)) { _hw_endpoint_alloc(ep); @@ -198,10 +180,10 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t b _hw_endpoint_init(ep, ep_addr, wMaxPacketSize, bmAttributes); } -static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool start) +static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) { struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); - _hw_endpoint_xfer(ep, buffer, total_bytes, start); + hw_endpoint_xfer_start(ep, buffer, total_bytes); } static void hw_handle_buff_status(void) @@ -213,19 +195,16 @@ static void hw_handle_buff_status(void) { if (remaining_buffers & bit) { - uint __unused which = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0; - // Should be single buffered - assert(which == 0); // clear this in advance usb_hw_clear->buf_status = bit; // IN transfer for even i, OUT transfer for odd i struct hw_endpoint *ep = hw_endpoint_get_by_num(i >> 1u, !(i & 1u)); // Continue xfer - bool done = _hw_endpoint_xfer_continue(ep); + bool done = hw_endpoint_xfer_continue(ep); if (done) { // Notify - dcd_event_xfer_complete(0, ep->ep_addr, ep->len, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true); hw_endpoint_reset_transfer(ep); } remaining_buffers &= ~bit; @@ -251,7 +230,7 @@ static void ep0_0len_status(void) { // Send 0len complete response on EP0 IN reset_ep0(); - hw_endpoint_xfer(0x80, NULL, 0, true); + hw_endpoint_xfer(0x80, NULL, 0); } static void _hw_endpoint_stall(struct hw_endpoint *ep) @@ -339,10 +318,7 @@ static void dcd_rp2040_irq(void) #if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX // Only run enumeration walk-around if pull up is enabled - if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS ) - { - rp2040_usb_device_enumeration_fix(); - } + if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS ) rp2040_usb_device_enumeration_fix(); #endif } @@ -402,9 +378,9 @@ void dcd_init (uint8_t rhport) // EP0 always exists so init it now // EP0 OUT - hw_endpoint_init(0x0, 64, 0); + hw_endpoint_init(0x0, 64, TUSB_XFER_CONTROL); // EP0 IN - hw_endpoint_init(0x80, 64, 0); + hw_endpoint_init(0x80, 64, TUSB_XFER_CONTROL); // Initializes the USB peripheral for device mode and enables it. // Don't need to enable the pull up here. Force VBUS @@ -470,23 +446,22 @@ void dcd_connect(uint8_t rhport) void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) { - pico_trace("dcd_edpt0_status_complete %d\n", rhport); - assert(rhport == 0); + (void) rhport; - if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && - request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && - request->bRequest == TUSB_REQ_SET_ADDRESS) - { - pico_trace("Set HW address %d\n", assigned_address); - usb_hw->dev_addr_ctrl = (uint8_t) request->wValue; - } + if ( request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS ) + { + pico_trace("Set HW address %d\n", request->wValue); + usb_hw->dev_addr_ctrl = (uint8_t) request->wValue; + } - reset_ep0(); + reset_ep0(); } bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) { - pico_info("dcd_edpt_open %d %02x\n", rhport, desc_edpt->bEndpointAddress); + pico_info("dcd_edpt_open %02x\n", desc_edpt->bEndpointAddress); assert(rhport == 0); hw_endpoint_init(desc_edpt->bEndpointAddress, desc_edpt->wMaxPacketSize.size, desc_edpt->bmAttributes.xfer); return true; @@ -495,21 +470,20 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) { assert(rhport == 0); - // True means start new xfer - hw_endpoint_xfer(ep_addr, buffer, total_bytes, true); + hw_endpoint_xfer(ep_addr, buffer, total_bytes); return true; } void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) { - pico_trace("dcd_edpt_stall %d %02x\n", rhport, ep_addr); + pico_trace("dcd_edpt_stall %02x\n", ep_addr); assert(rhport == 0); hw_endpoint_stall(ep_addr); } void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) { - pico_trace("dcd_edpt_clear_stall %d %02x\n", rhport, ep_addr); + pico_trace("dcd_edpt_clear_stall %02x\n", ep_addr); assert(rhport == 0); hw_endpoint_clear_stall(ep_addr); } @@ -517,9 +491,11 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) { - // usbd.c says: In progress transfers on this EP may be delivered after this call - pico_trace("dcd_edpt_close %d %02x\n", rhport, ep_addr); + (void) rhport; + (void) ep_addr; + // usbd.c says: In progress transfers on this EP may be delivered after this call + pico_trace("dcd_edpt_close %02x\n", ep_addr); } void dcd_int_handler(uint8_t rhport) diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index b575c6afc..40a0805c6 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -2,6 +2,7 @@ * The MIT License (MIT) * * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * Copyright (c) 2021 Ha Thach (tinyusb.org) for Double Buffered * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -52,46 +53,30 @@ static_assert(PICO_USB_HOST_INTERRUPT_ENDPOINTS <= USB_MAX_ENDPOINTS, ""); // Host mode uses one shared endpoint register for non-interrupt endpoint -struct hw_endpoint eps[1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS]; -#define epx (eps[0]) +static struct hw_endpoint ep_pool[1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS]; +#define epx (ep_pool[0]) -#define usb_hw_set hw_set_alias(usb_hw) +#define usb_hw_set hw_set_alias(usb_hw) #define usb_hw_clear hw_clear_alias(usb_hw) -// Used for hcd pipe busy. -// todo still a bit wasteful -// top bit set if valid -uint8_t dev_ep_map[CFG_TUSB_HOST_DEVICE_MAX][1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS][2]; - // Flags we set by default in sie_ctrl (we add other bits on top) -static uint32_t sie_ctrl_base = USB_SIE_CTRL_SOF_EN_BITS | - USB_SIE_CTRL_KEEP_ALIVE_EN_BITS | - USB_SIE_CTRL_PULLDOWN_EN_BITS | - USB_SIE_CTRL_EP0_INT_1BUF_BITS; +enum { + SIE_CTRL_BASE = USB_SIE_CTRL_SOF_EN_BITS | USB_SIE_CTRL_KEEP_ALIVE_EN_BITS | + USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS +}; static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr) { - uint8_t num = tu_edpt_number(ep_addr); - if (num == 0) { - return &epx; - } - uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0; - uint mapping = dev_ep_map[dev_addr-1][num][in]; - pico_trace("Get dev addr %d ep %d = %d\n", dev_addr, ep_addr, mapping); - return mapping >= 128 ? eps + (mapping & 0x7fu) : NULL; -} + uint8_t num = tu_edpt_number(ep_addr); + if ( num == 0 ) return &epx; -static void set_dev_ep(uint8_t dev_addr, uint8_t ep_addr, struct hw_endpoint *ep) -{ - uint8_t num = tu_edpt_number(ep_addr); - uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0; - uint32_t index = ep - eps; - hard_assert(index < TU_ARRAY_SIZE(eps)); - // todo revisit why dev_addr can be 0 here - if (dev_addr) { - dev_ep_map[dev_addr-1][num][in] = 128u | index; - } - pico_trace("Set dev addr %d ep %d = %d\n", dev_addr, ep_addr, index); + for ( uint32_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++ ) + { + struct hw_endpoint *ep = &ep_pool[i]; + if ( ep->configured && (ep->dev_addr == dev_addr) && (ep->ep_addr == ep_addr) ) return ep; + } + + return NULL; } static inline uint8_t dev_speed(void) @@ -111,15 +96,15 @@ static void hw_xfer_complete(struct hw_endpoint *ep, xfer_result_t xfer_result) // Mark transfer as done before we tell the tinyusb stack uint8_t dev_addr = ep->dev_addr; uint8_t ep_addr = ep->ep_addr; - uint total_len = ep->total_len; + uint xferred_len = ep->xferred_len; hw_endpoint_reset_transfer(ep); - hcd_event_xfer_complete(dev_addr, ep_addr, total_len, xfer_result, true); + hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); } static void _handle_buff_status_bit(uint bit, struct hw_endpoint *ep) { usb_hw_clear->buf_status = bit; - bool done = _hw_endpoint_xfer_continue(ep); + bool done = hw_endpoint_xfer_continue(ep); if (done) { hw_xfer_complete(ep, XFER_RESULT_SUCCESS); @@ -137,6 +122,17 @@ static void hw_handle_buff_status(void) { remaining_buffers &= ~bit; struct hw_endpoint *ep = &epx; + + uint32_t ep_ctrl = *ep->endpoint_control; + if (ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS) + { + TU_LOG(3, "Double Buffered: "); + }else + { + TU_LOG(3, "Single Buffered: "); + } + TU_LOG_HEX(3, ep_ctrl); + _handle_buff_status_bit(bit, ep); } @@ -153,7 +149,7 @@ static void hw_handle_buff_status(void) if (remaining_buffers & bit) { remaining_buffers &= ~bit; - _handle_buff_status_bit(bit, &eps[i]); + _handle_buff_status_bit(bit, &ep_pool[i]); } } @@ -165,19 +161,19 @@ static void hw_handle_buff_status(void) static void hw_trans_complete(void) { - struct hw_endpoint *ep = &epx; - assert(ep->active); + struct hw_endpoint *ep = &epx; + assert(ep->active); - if (ep->sent_setup) - { - pico_trace("Sent setup packet\n"); - hw_xfer_complete(ep, XFER_RESULT_SUCCESS); - } - else - { - // Don't care. Will handle this in buff status - return; - } + if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS) + { + pico_trace("Sent setup packet\n"); + hw_xfer_complete(ep, XFER_RESULT_SUCCESS); + } + else + { + // Don't care. Will handle this in buff status + return; + } } static void hcd_rp2040_irq(void) @@ -202,20 +198,22 @@ static void hcd_rp2040_irq(void) usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS; } + if (status & USB_INTS_BUFF_STATUS_BITS) + { + handled |= USB_INTS_BUFF_STATUS_BITS; + TU_LOG(2, "Buffer complete\n"); + // print_bufctrl32(*epx.buffer_control); + hw_handle_buff_status(); + } + if (status & USB_INTS_TRANS_COMPLETE_BITS) { handled |= USB_INTS_TRANS_COMPLETE_BITS; usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS; + TU_LOG(2, "Transfer complete\n"); hw_trans_complete(); } - if (status & USB_INTS_BUFF_STATUS_BITS) - { - handled |= USB_INTS_BUFF_STATUS_BITS; - // print_bufctrl32(*epx.buffer_control); - hw_handle_buff_status(); - } - if (status & USB_INTS_STALL_BITS) { // We have rx'd a stall from the device @@ -234,7 +232,7 @@ static void hcd_rp2040_irq(void) if (status & USB_INTS_ERROR_DATA_SEQ_BITS) { usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS; - // print_bufctrl32(*epx.buffer_control); + print_bufctrl32(*epx.buffer_control); panic("Data Seq Error \n"); } @@ -247,9 +245,9 @@ static void hcd_rp2040_irq(void) static struct hw_endpoint *_next_free_interrupt_ep(void) { struct hw_endpoint *ep = NULL; - for (uint i = 1; i < TU_ARRAY_SIZE(eps); i++) + for (uint i = 1; i < TU_ARRAY_SIZE(ep_pool); i++) { - ep = &eps[i]; + ep = &ep_pool[i]; if (!ep->configured) { // Will be configured by _hw_endpoint_init / _hw_endpoint_allocate @@ -263,6 +261,7 @@ static struct hw_endpoint *_next_free_interrupt_ep(void) static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) { struct hw_endpoint *ep = NULL; + if (transfer_type == TUSB_XFER_INTERRUPT) { ep = _next_free_interrupt_ep(); @@ -270,11 +269,11 @@ static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) assert(ep); ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; - // 0x180 for epx - // 0x1c0 for intep0 - // 0x200 for intep1 + // 0 for epx (double buffered): TODO increase to 1024 for ISO + // 2x64 for intep0 + // 3x64 for intep1 // etc - ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 1)]; + ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 2)]; } else { @@ -283,6 +282,7 @@ static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) ep->endpoint_control = &usbh_dpram->epx_ctrl; ep->hw_data_buf = &usbh_dpram->epx_data[0]; } + return ep; } @@ -303,7 +303,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep->rx = (dir == TUSB_DIR_IN); // Response to a setup packet on EP0 starts with pid of 1 - ep->next_pid = num == 0 ? 1u : 0u; + ep->next_pid = (num == 0 ? 1u : 0u); ep->wMaxPacketSize = wMaxPacketSize; ep->transfer_type = transfer_type; @@ -332,6 +332,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t // preamble uint32_t reg = dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB); // Assert the interrupt endpoint is IN_TO_HOST + // TODO Interrupt can also be OUT assert(dir == TUSB_DIR_IN); if (need_pre(dev_addr)) @@ -345,24 +346,9 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t // If it's an interrupt endpoint we need to set up the buffer control // register - } } -static void hw_endpoint_init(uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) -{ - // Allocated differently based on if it's an interrupt endpoint or not - struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer); - _hw_endpoint_init(ep, - dev_addr, - ep_desc->bEndpointAddress, - ep_desc->wMaxPacketSize.size, - ep_desc->bmAttributes.xfer, - ep_desc->bInterval); - // Map this struct to ep@device address - set_dev_ep(dev_addr, ep_desc->bEndpointAddress, ep); -} - //--------------------------------------------------------------------+ // HCD API //--------------------------------------------------------------------+ @@ -377,11 +363,11 @@ bool hcd_init(uint8_t rhport) irq_set_exclusive_handler(USBCTRL_IRQ, hcd_rp2040_irq); // clear epx and interrupt eps - memset(&eps, 0, sizeof(eps)); + memset(&ep_pool, 0, sizeof(ep_pool)); // Enable in host mode with SOF / Keep alive on usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS | USB_MAIN_CTRL_HOST_NDEVICE_BITS; - usb_hw->sie_ctrl = sie_ctrl_base; + usb_hw->sie_ctrl = SIE_CTRL_BASE; usb_hw->inte = USB_INTE_BUFF_STATUS_BITS | USB_INTE_HOST_CONN_DIS_BITS | USB_INTE_HOST_RESUME_BITS | @@ -409,7 +395,6 @@ bool hcd_port_connect_status(uint8_t rhport) tusb_speed_t hcd_port_speed_get(uint8_t rhport) { - pico_trace("hcd_port_speed_get\n"); assert(rhport == 0); // TODO: Should enumval this register switch (dev_speed()) @@ -420,15 +405,25 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport) return TUSB_SPEED_FULL; default: panic("Invalid speed\n"); + return TUSB_SPEED_INVALID; } } // Close all opened endpoint belong to this device void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { + (void) rhport; + (void) dev_addr; + pico_trace("hcd_device_close %d\n", dev_addr); } +uint32_t hcd_frame_number(uint8_t rhport) +{ + (void) rhport; + return usb_hw->sof_rd; +} + void hcd_int_enable(uint8_t rhport) { assert(rhport == 0); @@ -442,36 +437,70 @@ void hcd_int_disable(uint8_t rhport) irq_set_enabled(USBCTRL_IRQ, false); } +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) +{ + (void) rhport; + + pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress); + + // Allocated differently based on if it's an interrupt endpoint or not + struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer); + + _hw_endpoint_init(ep, + dev_addr, + ep_desc->bEndpointAddress, + ep_desc->wMaxPacketSize.size, + ep_desc->bmAttributes.xfer, + ep_desc->bInterval); + + return true; +} + bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { - pico_info("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen); + (void) rhport; + + pico_trace("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen); + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr); + // Get appropriate ep. Either EPX or interrupt endpoint struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr); assert(ep); - if (ep_addr != ep->ep_addr) + // Control endpoint can change direction 0x00 <-> 0x80 + if ( ep_addr != ep->ep_addr ) { - // Direction has flipped so re init it but with same properties - // TODO treat IN and OUT as invidual endpoints - _hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0); - } + assert(ep_num == 0); - // True indicates this is the start of the transfer - _hw_endpoint_xfer(ep, buffer, buflen, true); + // Direction has flipped on endpoint control so re init it but with same properties + _hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0); + } // If a normal transfer (non-interrupt) then initiate using // sie ctrl registers. Otherwise interrupt ep registers should // already be configured if (ep == &epx) { + hw_endpoint_xfer_start(ep, buffer, buflen); + // That has set up buffer control, endpoint control etc // for host we have to initiate the transfer - usb_hw->dev_addr_ctrl = dev_addr | (tu_edpt_number(ep_addr) << USB_ADDR_ENDP_ENDPOINT_LSB); - uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | sie_ctrl_base; - flags |= ep->rx ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS; + usb_hw->dev_addr_ctrl = dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB); + + uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | SIE_CTRL_BASE | + (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS); // Set pre if we are a low speed device on full speed hub flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0; + usb_hw->sie_ctrl = flags; + }else + { + hw_endpoint_xfer_start(ep, buffer, buflen); } return true; @@ -479,41 +508,33 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) { - pico_info("hcd_setup_send dev_addr %d\n", dev_addr); - + (void) rhport; + // Copy data into setup packet buffer memcpy((void*)&usbh_dpram->setup_packet[0], setup_packet, 8); // Configure EP0 struct with setup info for the trans complete struct hw_endpoint *ep = _hw_endpoint_allocate(0); + // EP0 out _hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0); assert(ep->configured); - ep->total_len = 8; - ep->transfer_size = 8; - ep->active = true; - ep->sent_setup = true; + + ep->remaining_len = 8; + ep->active = true; // Set device address usb_hw->dev_addr_ctrl = dev_addr; + // Set pre if we are a low speed device on full speed hub - uint32_t flags = sie_ctrl_base | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS; - flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0; + uint32_t const flags = SIE_CTRL_BASE | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS | + (need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + usb_hw->sie_ctrl = flags; + return true; } -uint32_t hcd_frame_number(uint8_t rhport) -{ - return usb_hw->sof_rd; -} - -bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) -{ - pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress); - hw_endpoint_init(dev_addr, ep_desc); - return true; -} //bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) //{ @@ -531,6 +552,9 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr) { + (void) dev_addr; + (void) ep_addr; + panic("hcd_clear_stall"); return true; } diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 31381e6c2..dc12e6d82 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -2,6 +2,7 @@ * The MIT License (MIT) * * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * Copyright (c) 2021 Ha Thach (tinyusb.org) for Double Buffered * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -43,42 +44,38 @@ static inline void _hw_endpoint_lock_update(struct hw_endpoint *ep, int delta) { // sense to have worker and IRQ on same core, however I think using critsec is about equivalent. } -#if TUSB_OPT_HOST_ENABLED -static inline void _hw_endpoint_update_last_buf(struct hw_endpoint *ep) -{ - ep->last_buf = (ep->len + ep->transfer_size == ep->total_len); -} -#endif +static void _hw_endpoint_xfer_sync(struct hw_endpoint *ep); +static void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep); + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ void rp2040_usb_init(void) { - // Reset usb controller - reset_block(RESETS_RESET_USBCTRL_BITS); - unreset_block_wait(RESETS_RESET_USBCTRL_BITS); + // Reset usb controller + reset_block(RESETS_RESET_USBCTRL_BITS); + unreset_block_wait(RESETS_RESET_USBCTRL_BITS); - // Clear any previous state just in case - memset(usb_hw, 0, sizeof(*usb_hw)); - memset(usb_dpram, 0, sizeof(*usb_dpram)); + // Clear any previous state just in case + memset(usb_hw, 0, sizeof(*usb_hw)); + memset(usb_dpram, 0, sizeof(*usb_dpram)); - // Mux the controller to the onboard usb phy - usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS; + // Mux the controller to the onboard usb phy + usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS; - // Force VBUS detect so the device thinks it is plugged into a host - // TODO support VBUs detect - usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS; + // Force VBUS detect so the device thinks it is plugged into a host + // TODO support VBUs detect + usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS; } void hw_endpoint_reset_transfer(struct hw_endpoint *ep) { - ep->stalled = false; - ep->active = false; -#if TUSB_OPT_HOST_ENABLED - ep->sent_setup = false; -#endif - ep->total_len = 0; - ep->len = 0; - ep->transfer_size = 0; - ep->user_buf = 0; + ep->stalled = false; + ep->active = false; + ep->remaining_len = 0; + ep->xferred_len = 0; + ep->user_buf = 0; } void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) { @@ -111,215 +108,223 @@ void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_m *ep->buffer_control = value; } -void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep) +// prepare buffer, return buffer control +static uint32_t prepare_ep_buffer(struct hw_endpoint *ep, uint8_t buf_id) { - // Prepare buffer control register value - uint32_t val = ep->transfer_size | USB_BUF_CTRL_AVAIL; + uint16_t const buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize); + ep->remaining_len -= buflen; - if (!ep->rx) + uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL; + + // PID + buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID; + ep->next_pid ^= 1u; + + if ( !ep->rx ) + { + // Copy data from user buffer to hw buffer + memcpy(ep->hw_data_buf + buf_id*64, ep->user_buf, buflen); + ep->user_buf += buflen; + + // Mark as full + buf_ctrl |= USB_BUF_CTRL_FULL; + } + + // Is this the last buffer? Only really matters for host mode. Will trigger + // the trans complete irq but also stop it polling. We only really care about + // trans complete for setup packets being sent + if (ep->remaining_len == 0) + { + buf_ctrl |= USB_BUF_CTRL_LAST; + } + + if (buf_id) buf_ctrl = buf_ctrl << 16; + + return buf_ctrl; +} + +// Prepare buffer control register value +static void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep) +{ + uint32_t ep_ctrl = *ep->endpoint_control; + + // always compute and start with buffer 0 + uint32_t buf_ctrl = prepare_ep_buffer(ep, 0) | USB_BUF_CTRL_SEL; + + // For now: skip double buffered for Device mode, OUT endpoint since + // host could send < 64 bytes and cause short packet on buffer0 + // NOTE this could happen to Host mode IN endpoint + bool const force_single = !(usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) && !tu_edpt_dir(ep->ep_addr); + + if(ep->remaining_len && !force_single) + { + // Use buffer 1 (double buffered) if there is still data + // TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt) + + buf_ctrl |= prepare_ep_buffer(ep, 1); + + // Set endpoint control double buffered bit if needed + ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER; + ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER; + }else + { + // Single buffered since 1 is enough + ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER); + ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; + } + + *ep->endpoint_control = ep_ctrl; + + TU_LOG(3, "Prepare Buffer Control:\r\n"); + print_bufctrl32(buf_ctrl); + + // Finally, write to buffer_control which will trigger the transfer + // the next time the controller polls this dpram address + _hw_endpoint_buffer_control_set_value32(ep, buf_ctrl); +} + +void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len) +{ + _hw_endpoint_lock_update(ep, 1); + + if ( ep->active ) + { + // TODO: Is this acceptable for interrupt packets? + TU_LOG(1, "WARN: starting new transfer on already active ep %d %s\n", tu_edpt_number(ep->ep_addr), + ep_dir_string[tu_edpt_dir(ep->ep_addr)]); + + hw_endpoint_reset_transfer(ep); + } + + // Fill in info now that we're kicking off the hw + ep->remaining_len = total_len; + ep->xferred_len = 0; + ep->active = true; + ep->user_buf = buffer; + + _hw_endpoint_start_next_buffer(ep); + _hw_endpoint_lock_update(ep, -1); +} + +// sync endpoint buffer and return transferred bytes +static uint16_t sync_ep_buffer(struct hw_endpoint *ep, uint8_t buf_id) +{ + uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep); + if (buf_id) buf_ctrl = buf_ctrl >> 16; + + uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK; + + if ( !ep->rx ) + { + // We are continuing a transfer here. If we are TX, we have successfully + // sent some data can increase the length we have sent + assert(!(buf_ctrl & USB_BUF_CTRL_FULL)); + + ep->xferred_len += xferred_bytes; + }else + { + // If we have received some data, so can increase the length + // we have received AFTER we have copied it to the user buffer at the appropriate offset + assert(buf_ctrl & USB_BUF_CTRL_FULL); + + memcpy(ep->user_buf, ep->hw_data_buf + buf_id*64, xferred_bytes); + ep->xferred_len += xferred_bytes; + ep->user_buf += xferred_bytes; + } + + // Short packet + if (xferred_bytes < ep->wMaxPacketSize) + { + pico_trace("Short rx transfer on buffer %d with %u bytes\n", buf_id, xferred_bytes); + // Reduce total length as this is last packet + ep->remaining_len = 0; + } + + return xferred_bytes; +} + +static void _hw_endpoint_xfer_sync (struct hw_endpoint *ep) +{ + // Update hw endpoint struct with info from hardware + // after a buff status interrupt + + uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep); + TU_LOG(3, "_hw_endpoint_xfer_sync:\r\n"); + print_bufctrl32(buf_ctrl); + + // always sync buffer 0 + uint16_t buf0_bytes = sync_ep_buffer(ep, 0); + + // sync buffer 1 if double buffered + if ( (*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS ) + { + if (buf0_bytes == ep->wMaxPacketSize) { - // Copy data from user buffer to hw buffer - memcpy(ep->hw_data_buf, &ep->user_buf[ep->len], ep->transfer_size); - // Mark as full - val |= USB_BUF_CTRL_FULL; - } - - // PID - val |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID; - -#if TUSB_OPT_DEVICE_ENABLED - ep->next_pid ^= 1u; - -#else - // For Host (also device but since we dictate the endpoint size, following scenario does not occur) - // Next PID depends on the number of packet in case wMaxPacketSize < 64 (e.g Interrupt Endpoint 8, or 12) - // Special case with control status stage where PID is always DATA1 - if ( ep->transfer_size == 0 ) - { - // ZLP also toggle data - ep->next_pid ^= 1u; + // sync buffer 1 if not short packet + sync_ep_buffer(ep, 1); }else { - uint32_t packet_count = 1 + ((ep->transfer_size - 1) / ep->wMaxPacketSize); + // short packet on buffer 0 + // TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example + // At this time (currently trigger per 2 buffer), the buffer1 is probably filled with data from + // the next transfer (not current one). For now we disable double buffered for device OUT + // NOTE this could happen to Host IN +#if 0 + uint8_t const ep_num = tu_edpt_number(ep->ep_addr); + uint8_t const dir = (uint8_t) tu_edpt_dir(ep->ep_addr); + uint8_t const ep_id = 2*ep_num + (dir ? 0 : 1); - if ( packet_count & 0x01 ) - { - ep->next_pid ^= 1u; - } - } + // abort queued transfer on buffer 1 + usb_hw->abort |= TU_BIT(ep_id); + + while ( !(usb_hw->abort_done & TU_BIT(ep_id)) ) {} + + uint32_t ep_ctrl = *ep->endpoint_control; + ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER); + ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; + + _hw_endpoint_buffer_control_set_value32(ep, 0); + + usb_hw->abort &= ~TU_BIT(ep_id); + + TU_LOG(3, "----SHORT PACKET buffer0 on EP %02X:\r\n", ep->ep_addr); + print_bufctrl32(buf_ctrl); #endif - - -#if TUSB_OPT_HOST_ENABLED - // Is this the last buffer? Only really matters for host mode. Will trigger - // the trans complete irq but also stop it polling. We only really care about - // trans complete for setup packets being sent - if (ep->last_buf) - { - pico_trace("Last buf (%d bytes left)\n", ep->transfer_size); - val |= USB_BUF_CTRL_LAST; - } -#endif - - // Finally, write to buffer_control which will trigger the transfer - // the next time the controller polls this dpram address - _hw_endpoint_buffer_control_set_value32(ep, val); - pico_trace("buffer control (0x%p) <- 0x%x\n", ep->buffer_control, val); - //print_bufctrl16(val); -} - - -void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len) -{ - _hw_endpoint_lock_update(ep, 1); - pico_trace("Start transfer of total len %d on ep %d %s\n", total_len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); - if (ep->active) - { - // TODO: Is this acceptable for interrupt packets? - pico_warn("WARN: starting new transfer on already active ep %d %s\n", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); - - hw_endpoint_reset_transfer(ep); - } - - // Fill in info now that we're kicking off the hw - ep->total_len = total_len; - ep->len = 0; - - // Limit by packet size but not less 64 (i.e low speed 8 bytes EP0) - ep->transfer_size = tu_min16(total_len, tu_max16(64, ep->wMaxPacketSize)); - - ep->active = true; - ep->user_buf = buffer; -#if TUSB_OPT_HOST_ENABLED - // Recalculate if this is the last buffer - _hw_endpoint_update_last_buf(ep); - ep->buf_sel = 0; -#endif - - _hw_endpoint_start_next_buffer(ep); - _hw_endpoint_lock_update(ep, -1); -} - -void _hw_endpoint_xfer_sync(struct hw_endpoint *ep) -{ - // Update hw endpoint struct with info from hardware - // after a buff status interrupt - - uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep); - -#if TUSB_OPT_HOST_ENABLED - // RP2040-E4 - // tag::host_buf_sel_fix[] - // TODO need changes to support double buffering - if (ep->buf_sel == 1) - { - // Host can erroneously write status to top half of buf_ctrl register - buf_ctrl = buf_ctrl >> 16; - - // update buf1 -> buf0 to prevent panic with "already available" - *ep->buffer_control = buf_ctrl; - } - // Flip buf sel for host - ep->buf_sel ^= 1u; - // end::host_buf_sel_fix[] -#endif - - // Get tranferred bytes after adjusted buf sel - uint16_t const transferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK; - - // We are continuing a transfer here. If we are TX, we have successfullly - // sent some data can increase the length we have sent - if (!ep->rx) - { - assert(!(buf_ctrl & USB_BUF_CTRL_FULL)); - pico_trace("tx %d bytes (buf_ctrl 0x%08x)\n", transferred_bytes, buf_ctrl); - ep->len += transferred_bytes; - } - else - { - // If we are OUT we have recieved some data, so can increase the length - // we have recieved AFTER we have copied it to the user buffer at the appropriate - // offset - pico_trace("rx %d bytes (buf_ctrl 0x%08x)\n", transferred_bytes, buf_ctrl); - assert(buf_ctrl & USB_BUF_CTRL_FULL); - memcpy(&ep->user_buf[ep->len], ep->hw_data_buf, transferred_bytes); - ep->len += transferred_bytes; - } - - // Sometimes the host will send less data than we expect... - // If this is a short out transfer update the total length of the transfer - // to be the current length - if ((ep->rx) && (transferred_bytes < ep->wMaxPacketSize)) - { - pico_trace("Short rx transfer\n"); - // Reduce total length as this is last packet - ep->total_len = ep->len; } + } } // Returns true if transfer is complete -bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep) +bool hw_endpoint_xfer_continue(struct hw_endpoint *ep) { - _hw_endpoint_lock_update(ep, 1); - // Part way through a transfer - if (!ep->active) - { - panic("Can't continue xfer on inactive ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string); - } + _hw_endpoint_lock_update(ep, 1); + // Part way through a transfer + if (!ep->active) + { + panic("Can't continue xfer on inactive ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string); + } - // Update EP struct from hardware state - _hw_endpoint_xfer_sync(ep); - - // Now we have synced our state with the hardware. Is there more data to transfer? - // Limit by packet size but not less 64 (i.e low speed 8 bytes EP0) - uint16_t remaining_bytes = ep->total_len - ep->len; - ep->transfer_size = tu_min16(remaining_bytes, tu_max16(64, ep->wMaxPacketSize)); -#if TUSB_OPT_HOST_ENABLED - _hw_endpoint_update_last_buf(ep); -#endif - - // Can happen because of programmer error so check for it - if (ep->len > ep->total_len) - { - panic("Transferred more data than expected"); - } - - // If we are done then notify tinyusb - if (ep->len == ep->total_len) - { - pico_trace("Completed transfer of %d bytes on ep %d %s\n", - ep->len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); - // Notify caller we are done so it can notify the tinyusb stack - _hw_endpoint_lock_update(ep, -1); - return true; - } - else - { - _hw_endpoint_start_next_buffer(ep); - } + // Update EP struct from hardware state + _hw_endpoint_xfer_sync(ep); + // Now we have synced our state with the hardware. Is there more data to transfer? + // If we are done then notify tinyusb + if (ep->remaining_len == 0) + { + pico_trace("Completed transfer of %d bytes on ep %d %s\n", + ep->xferred_len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); + // Notify caller we are done so it can notify the tinyusb stack _hw_endpoint_lock_update(ep, -1); - // More work to do - return false; -} + return true; + } + else + { + _hw_endpoint_start_next_buffer(ep); + } -void _hw_endpoint_xfer(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len, bool start) -{ - // Trace - pico_trace("hw_endpoint_xfer ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); - pico_trace(" total_len %d, start=%d\n", total_len, start); - - assert(ep->configured); - - - if (start) - { - _hw_endpoint_xfer_start(ep, buffer, total_len); - } - else - { - _hw_endpoint_xfer_continue(ep); - } + _hw_endpoint_lock_update(ep, -1); + // More work to do + return false; } #endif diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 33f3ecd41..514152cd9 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -17,23 +17,8 @@ #endif -#if false && !defined(NDEBUG) -#define pico_trace(format,args...) printf(format, ## args) -#else -#define pico_trace(format,...) ((void)0) -#endif - -#if false && !defined(NDEBUG) -#define pico_info(format,args...) printf(format, ## args) -#else -#define pico_info(format,...) ((void)0) -#endif - -#if false && !defined(NDEBUG) -#define pico_warn(format,args...) printf(format, ## args) -#else -#define pico_warn(format,...) ((void)0) -#endif +#define pico_info(...) TU_LOG(2, __VA_ARGS__) +#define pico_trace(...) TU_LOG(3, __VA_ARGS__) // Hardware information per endpoint struct hw_endpoint @@ -50,6 +35,7 @@ struct hw_endpoint // Endpoint control register io_rw_32 *endpoint_control; + // Buffer control register io_rw_32 *buffer_control; @@ -61,27 +47,22 @@ struct hw_endpoint // Current transfer information bool active; - uint16_t total_len; - uint16_t len; - // Amount of data with the hardware - uint16_t transfer_size; + uint16_t remaining_len; + uint16_t xferred_len; + // User buffer in main memory uint8_t *user_buf; // Data needed from EP descriptor uint16_t wMaxPacketSize; + // Interrupt, bulk, etc uint8_t transfer_type; #if TUSB_OPT_HOST_ENABLED - // Only needed for host mode - bool last_buf; - // RP2040-E4: HOST BUG. Host will incorrect write status to top half of buffer - // control register when doing transfers > 1 packet - uint8_t buf_sel; // Only needed for host uint8_t dev_addr; - bool sent_setup; + // If interrupt endpoint uint8_t interrupt_num; #endif @@ -89,12 +70,10 @@ struct hw_endpoint void rp2040_usb_init(void); +void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len); +bool hw_endpoint_xfer_continue(struct hw_endpoint *ep); void hw_endpoint_reset_transfer(struct hw_endpoint *ep); -void _hw_endpoint_xfer(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len, bool start); -void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep); -void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len); -void _hw_endpoint_xfer_sync(struct hw_endpoint *ep); -bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep); + void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask); static inline uint32_t _hw_endpoint_buffer_control_get_value32(struct hw_endpoint *ep) { return *ep->buffer_control; @@ -134,14 +113,15 @@ typedef union TU_ATTR_PACKED TU_VERIFY_STATIC(sizeof(rp2040_buffer_control_t) == 2, "size is not correct"); -static inline void print_bufctrl16(uint32_t __unused u16) +#if CFG_TUSB_DEBUG >= 3 +static inline void print_bufctrl16(uint32_t u16) { - rp2040_buffer_control_t __unused bufctrl = { + rp2040_buffer_control_t bufctrl = { .u16 = u16 }; - TU_LOG(2, "len = %u, available = %u, stall = %u, reset = %u, toggle = %u, last = %u, full = %u\r\n", - bufctrl.xfer_len, bufctrl.available, bufctrl.stall, bufctrl.reset_bufsel, bufctrl.data_toggle, bufctrl.last_buf, bufctrl.full); + TU_LOG(3, "len = %u, available = %u, full = %u, last = %u, stall = %u, reset = %u, toggle = %u\r\n", + bufctrl.xfer_len, bufctrl.available, bufctrl.full, bufctrl.last_buf, bufctrl.stall, bufctrl.reset_bufsel, bufctrl.data_toggle); } static inline void print_bufctrl32(uint32_t u32) @@ -149,12 +129,19 @@ static inline void print_bufctrl32(uint32_t u32) uint16_t u16; u16 = u32 >> 16; - TU_LOG(2, "Buffer Control 1 0x%x: ", u16); + TU_LOG(3, " Buffer Control 1 0x%x: ", u16); print_bufctrl16(u16); u16 = u32 & 0x0000ffff; - TU_LOG(2, "Buffer Control 0 0x%x: ", u16); + TU_LOG(3, " Buffer Control 0 0x%x: ", u16); print_bufctrl16(u16); } +#else + +#define print_bufctrl16(u16) +#define print_bufctrl32(u32) + +#endif + #endif diff --git a/src/portable/renesas/usba/dcd_usba.c b/src/portable/renesas/usba/dcd_usba.c index 06ea1a3f9..338d5b0e6 100644 --- a/src/portable/renesas/usba/dcd_usba.c +++ b/src/portable/renesas/usba/dcd_usba.c @@ -26,8 +26,8 @@ #include "tusb_option.h" -#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_RX63X ) - +#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_RX63X || \ + CFG_TUSB_MCU == OPT_MCU_RX65X) #include "device/dcd.h" #include "iodefine.h" @@ -63,7 +63,10 @@ #define USB_IS0_CTSQ_SETUP (1u) #define USB_IS0_DVSQ_DEF (1u<<4) #define USB_IS0_DVSQ_ADDR (2u<<4) -#define USB_IS0_DVSQ_SUSP (4u<<4) +#define USB_IS0_DVSQ_SUSP0 (4u<<4) +#define USB_IS0_DVSQ_SUSP1 (5u<<4) +#define USB_IS0_DVSQ_SUSP2 (6u<<4) +#define USB_IS0_DVSQ_SUSP3 (7u<<4) #define USB_PIPECTR_PID_NAK (0u) #define USB_PIPECTR_PID_BUF (1u) @@ -125,6 +128,7 @@ typedef struct { pipe_state_t pipe[9]; uint8_t ep[2][16]; /* a lookup table for a pipe index from an endpoint address */ + uint8_t suspended; } dcd_data_t; //--------------------------------------------------------------------+ @@ -517,7 +521,8 @@ void dcd_init(uint8_t rhport) /* Setup default control pipe */ USB0.DCPMAXP.BIT.MXPS = 64; - USB0.INTENB0.WORD = USB_IS0_VBINT | USB_IS0_BRDY | USB_IS0_BEMP | USB_IS0_DVST | USB_IS0_CTRT; + USB0.INTENB0.WORD = USB_IS0_VBINT | USB_IS0_BRDY | USB_IS0_BEMP | + USB_IS0_DVST | USB_IS0_CTRT | USB_IS0_SOFR | USB_IS0_RESM; USB0.BEMPENB.WORD = 1; USB0.BRDYENB.WORD = 1; @@ -547,7 +552,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) void dcd_remote_wakeup(uint8_t rhport) { (void)rhport; - /* TODO */ + USB0.DVSTCTR0.BIT.WKUP = 1; } void dcd_connect(uint8_t rhport) @@ -689,6 +694,22 @@ void dcd_int_handler(uint8_t rhport) dcd_disconnect(rhport); } } + if (is0 & USB_IS0_RESM) { + dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); + _dcd.suspended = 0; + } + if (is0 & USB_IS0_SOFR) { + if (_dcd.suspended) { + /* When USB host resumes caused by `dcd_remote_wakeup()`, + * RESM interrupt does not rise. + * Therefore we need to manually send resume event. + * Of course, when USB host resumes on its own, + * RESM interrupt rise properly, then this statements are ignored. */ + dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); + _dcd.suspended = 0; + } + dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); + } if (is0 & USB_IS0_DVST) { switch (is0 & USB_IS0_DVSQ) { case USB_IS0_DVSQ_DEF: @@ -697,6 +718,12 @@ void dcd_int_handler(uint8_t rhport) case USB_IS0_DVSQ_ADDR: process_set_address(rhport); break; + case USB_IS0_DVSQ_SUSP0: + case USB_IS0_DVSQ_SUSP1: + case USB_IS0_DVSQ_SUSP2: + case USB_IS0_DVSQ_SUSP3: + dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); + _dcd.suspended = 1; default: break; } diff --git a/src/tusb_option.h b/src/tusb_option.h index 9b0ea4358..13d11fdfe 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -64,7 +64,6 @@ #define OPT_MCU_SAML21 206 ///< MicroChip SAML21 #define OPT_MCU_SAMX7X 207 ///< MicroChip SAME70, S70, V70, V71 family - // STM32 #define OPT_MCU_STM32F0 300 ///< ST STM32F0 #define OPT_MCU_STM32F1 301 ///< ST STM32F1 @@ -115,6 +114,7 @@ // Renesas RX #define OPT_MCU_RX63X 1400 ///< Renesas RX63N/631 +#define OPT_MCU_RX65X 1401 ///< Renesas RX65N/RX651 // Mind Motion #define OPT_MCU_MM32F327X 1500 ///< Mind Motion MM32F327 @@ -280,8 +280,8 @@ #error there is no benefit enable hub with max device is 1. Please disable hub or increase CFG_TUSB_HOST_DEVICE_MAX #endif - #ifndef CFG_TUH_ENUMERATION_BUFSZIE - #define CFG_TUH_ENUMERATION_BUFSZIE 256 + #ifndef CFG_TUH_ENUMERATION_BUFSIZE + #define CFG_TUH_ENUMERATION_BUFSIZE 256 #endif //------------- CLASS -------------// diff --git a/test/test/test_fifo.c b/test/test/test_fifo.c index f9bfc5f03..0a5f4d3b9 100644 --- a/test/test/test_fifo.c +++ b/test/test/test_fifo.c @@ -292,3 +292,27 @@ void test_full(void) // write info } + +void test_rd_idx_wrap() +{ + tu_fifo_t ff10; + uint8_t buf[10]; + uint8_t dst[10]; + + tu_fifo_config(&ff10, buf, 10, 1, 1); + + uint16_t n; + + ff10.wr_idx = 6; + ff10.rd_idx = 15; + + n = tu_fifo_read_n(&ff10, dst, 4); + TEST_ASSERT_EQUAL(n, 4); + TEST_ASSERT_EQUAL(ff10.rd_idx, 0); + n = tu_fifo_read_n(&ff10, dst, 4); + TEST_ASSERT_EQUAL(n, 4); + TEST_ASSERT_EQUAL(ff10.rd_idx, 4); + n = tu_fifo_read_n(&ff10, dst, 4); + TEST_ASSERT_EQUAL(n, 2); + TEST_ASSERT_EQUAL(ff10.rd_idx, 6); +} \ No newline at end of file