diff --git a/.circleci/config.yml b/.circleci/config.yml index 7b36bed7c..4d541a595 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,92 +1,39 @@ version: 2.1 -commands: - setup-toolchain: - parameters: - toolchain: - type: string - toolchain_url: - type: string - steps: -# - run: -# name: Make toolchain cache key -# command: echo "<< parameters.toolchain >>-<< parameters.toolchain_url>>" > toolchain_key -# - restore_cache: -# name: Restore Toolchain Cache -# key: deps-{{ checksum "toolchain_key" }} -# paths: -# - ~/cache/<< parameters.toolchain >> - - run: - name: Install Toolchain - command: | - # Only download if folder does not exist (not cached) - if [ ! -d ~/cache/<< parameters.toolchain >> ]; then - mkdir -p ~/cache/<< parameters.toolchain >> - wget << parameters.toolchain_url>> -O toolchain.tar.gz - tar -C ~/cache/<< parameters.toolchain >> -xaf toolchain.tar.gz - fi -# - save_cache: -# name: Save Toolchain Cache -# key: deps-{{ checksum "toolchain_key" }} -# paths: -# - ~/cache/<< parameters.toolchain >> - - run: - name: Setup build environment - command: | - echo "export PATH=$PATH:`echo ~/cache/<< parameters.toolchain >>/*/bin`" >> $BASH_ENV - # Install Ninja - NINJA_URL=https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux.zip - wget $NINJA_URL -O ninja-linux.zip - unzip ninja-linux.zip -d ~/bin - - get-deps: - parameters: - family: - type: string - steps: - - run: - name: Get Dependencies - command: | - python tools/get_deps.py << parameters.family >> +setup: true +orbs: + continuation: circleci/continuation@1 jobs: - arm-clang: - parameters: - family: - type: string - build-system: - type: string - + set-matrix: + executor: continuation/default docker: - image: cimg/base:current - resource_class: medium - environment: - TOOLCHAIN_URL: https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-17.0.1/LLVMEmbeddedToolchainForArm-17.0.1-Linux-x86_64.tar.xz + resource_class: small steps: - checkout - - setup-toolchain: - toolchain: clang - toolchain_url: $TOOLCHAIN_URL - - get-deps: - family: << parameters.family >> - run: - name: Build + name: Set matrix command: | - # Only build one board per family for non PRs i.e commit to master - ONE_PER_FAMILY="" - if [ -z "$CIRCLE_PULL_REQUEST" ]; then - ONE_PER_FAMILY="--one-per-family" - fi - python tools/build.py $ONE_PER_FAMILY -s << parameters.build-system >> --toolchain clang << parameters.family >> + MATRIX_JSON=$(python .github/workflows/ci_set_matrix.py) + echo "MATRIX_JSON=$MATRIX_JSON" + + TOOLCHAIN_LIST=("arm-clang" "arm-gcc") + for toolchain in "${TOOLCHAIN_LIST[@]}"; do + FAMILY=$(echo $MATRIX_JSON | jq -r ".\"$toolchain\".family") + echo "${toolchain}_FAMILY=$FAMILY" + echo " - build:" >> .circleci/config2.yml + echo " matrix:" >> .circleci/config2.yml + echo " parameters:" >> .circleci/config2.yml + echo " toolchain: ['$toolchain']" >> .circleci/config2.yml + echo " build-system: ['cmake']" >> .circleci/config2.yml + echo " family: $FAMILY" >> .circleci/config2.yml + done + + - continuation/continue: + configuration_path: .circleci/config2.yml workflows: - build: + set-matrix: jobs: - - arm-clang: - matrix: - parameters: - build-system: - - cmake - #family: ['stm32f1'] - #family: ['stm32f1', 'stm32f2'] - family: ['imxrt', 'kinetis_k kinetis_kl kinetis_k32l2', 'lpc11 lpc13 lpc15', 'lpc17 lpc18 lpc40 lpc43', 'lpc51 lpc54 lpc55', 'nrf', 'samd11 samd21 saml2x', 'samd5x_e5x samg', 'stm32f0 stm32f1 stm32f2 stm32f3', 'stm32f4', 'stm32f7', 'stm32g0 stm32g4 stm32h5', 'stm32h7', 'stm32l4 stm32u5 stm32wb'] + - set-matrix diff --git a/.circleci/config2.yml b/.circleci/config2.yml new file mode 100644 index 000000000..ea7fccaff --- /dev/null +++ b/.circleci/config2.yml @@ -0,0 +1,98 @@ +version: 2.1 + +commands: + setup-toolchain: + parameters: + toolchain: + type: string + steps: + - run: + name: Install Toolchain + command: | + TOOLCHAIN_JSON='{ + "arm-clang": "https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-17.0.1/LLVMEmbeddedToolchainForArm-17.0.1-Linux-x86_64.tar.xz", + "arm-gcc": "https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v12.3.1-1.1/xpack-arm-none-eabi-gcc-12.3.1-1.1-linux-x64.tar.gz" + }' + toolchain_url=$(echo $TOOLCHAIN_JSON | jq -r '.["<< parameters.toolchain >>"]') + echo "toolchain_url=$toolchain_url" + + # download and extract toolchain + mkdir -p ~/cache/<< parameters.toolchain >> + wget $toolchain_url -O toolchain.tar.gz + tar -C ~/cache/<< parameters.toolchain >> -xaf toolchain.tar.gz + + # Add toolchain to PATH + echo "export PATH=$PATH:`echo ~/cache/<< parameters.toolchain >>/*/bin`" >> $BASH_ENV + + get-deps: + parameters: + family: + type: string + steps: + - run: + name: Get Dependencies + command: | + python tools/get_deps.py << parameters.family >> + + # Install Pico SDK + if [ << parameters.family >> == "rp2040" ]; then + git clone --depth 1 https://github.com/raspberrypi/pico-sdk.git ~/pico-sdk + echo "export PICO_SDK_PATH=~/pico-sdk" >> $BASH_ENV + fi + +jobs: + build: + parameters: + build-system: + type: string + toolchain: + type: string + family: + type: string + + docker: + - image: cimg/base:current + resource_class: medium+ + steps: + - checkout + - when: + condition: << parameters.build-system >> == 'cmake' + steps: + - run: + name: Install Ninja + command: | + # Install Ninja + NINJA_URL=https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux.zip + wget $NINJA_URL -O ninja-linux.zip + unzip ninja-linux.zip -d ~/bin + - setup-toolchain: + toolchain: << parameters.toolchain >> + - get-deps: + family: << parameters.family >> + - run: + name: Build + command: | + # Only build one board per family for non PRs i.e commit to master + ONE_PER_FAMILY="" + if [ -z "$CIRCLE_PULL_REQUEST" ]; then + ONE_PER_FAMILY="--one-per-family" + fi + + # Toolchain option default is gcc + if [ "<< parameters.toolchain >>" == "arm-clang" ]; then + TOOLCHAIN_OPTION="--toolchain clang" + elif [ "<< parameters.toolchain >>" == "arm-gcc" ]; then + TOOLCHAIN_OPTION="--toolchain gcc" + fi + + python tools/build.py $ONE_PER_FAMILY -s << parameters.build-system >> $TOOLCHAIN_OPTION << parameters.family >> + +workflows: + build: + jobs: +# - build: +# matrix: +# parameters: +# toolchain: ['arm-clang'] +# build-system: ['cmake'] +# family: ['imxrt'] diff --git a/.github/actions/setup_toolchain/action.yml b/.github/actions/setup_toolchain/action.yml index 19fe28b0c..d173d6903 100644 --- a/.github/actions/setup_toolchain/action.yml +++ b/.github/actions/setup_toolchain/action.yml @@ -4,8 +4,8 @@ inputs: toolchain: description: 'Toolchain name' required: true - toolchain_url: - description: 'Toolchain URL or version' + toolchain_version: + description: 'Toolchain version' required: false outputs: @@ -27,7 +27,28 @@ runs: uses: ./.github/actions/setup_toolchain/espressif with: toolchain: ${{ inputs.toolchain }} - toolchain_url: ${{ inputs.toolchain_url }} + toolchain_version: ${{ inputs.toolchain_version }} + + - name: Get Toolchain URL + if: >- + inputs.toolchain != 'arm-gcc' && + inputs.toolchain != 'arm-iar' && + inputs.toolchain != 'esp-idf' + id: set-toolchain-url + run: | + TOOLCHAIN_JSON='{ + "aarch64-gcc": "https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz", + "arm-clang": "https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-17.0.1/LLVMEmbeddedToolchainForArm-17.0.1-Linux-x86_64.tar.xz", + "arm-iar": "", + "arm-gcc": "", + "msp430-gcc": "http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/9_2_0_0/export/msp430-gcc-9.2.0.50_linux64.tar.bz2", + "riscv-gcc": "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz", + "rx-gcc": "http://gcc-renesas.com/downloads/get.php?f=rx/8.3.0.202004-gnurx/gcc-8.3.0.202004-GNURX-ELF.run" + }' + TOOLCHAIN_URL=$(echo $TOOLCHAIN_JSON | jq -r '.["${{ inputs.toolchain }}"]') + echo "toolchain_url=$TOOLCHAIN_URL" + echo "toolchain_url=$TOOLCHAIN_URL" >> $GITHUB_OUTPUT + shell: bash - name: Download Toolchain if: >- @@ -37,7 +58,7 @@ runs: uses: ./.github/actions/setup_toolchain/download with: toolchain: ${{ inputs.toolchain }} - toolchain_url: ${{ inputs.toolchain_url }} + toolchain_url: ${{ steps.set-toolchain-url.outputs.toolchain_url }} - name: Set toolchain option id: set-toolchain-option diff --git a/.github/actions/setup_toolchain/espressif/action.yml b/.github/actions/setup_toolchain/espressif/action.yml index 46da02911..3129329dd 100644 --- a/.github/actions/setup_toolchain/espressif/action.yml +++ b/.github/actions/setup_toolchain/espressif/action.yml @@ -4,7 +4,7 @@ inputs: toolchain: description: 'Toolchain name' required: true - toolchain_url: + toolchain_version: description: 'Toolchain URL or version' required: true @@ -22,14 +22,14 @@ runs: id: cache-toolchain-espressif with: path: ${{ env.DOCKER_ESP_IDF }} - key: ${{ inputs.toolchain }}-${{ inputs.toolchain_url }} + key: ${{ inputs.toolchain }}-${{ inputs.toolchain_version }} - name: Pull and Save Docker Image if: steps.cache-toolchain-espressif.outputs.cache-hit != 'true' run: | - docker pull espressif/idf:${{ inputs.toolchain_url }} + docker pull espressif/idf:${{ inputs.toolchain_version }} mkdir -p $(dirname $DOCKER_ESP_IDF) - docker save -o $DOCKER_ESP_IDF espressif/idf:${{ inputs.toolchain_url }} + docker save -o $DOCKER_ESP_IDF espressif/idf:${{ inputs.toolchain_version }} du -sh $DOCKER_ESP_IDF shell: bash diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 712c7dd43..fde9400a7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,18 +58,30 @@ jobs: fail-fast: false matrix: toolchain: - # - 'arm-clang' is built by circle-ci + # - 'arm-clang' is built by circle-ci in PR - 'aarch64-gcc' - - 'arm-gcc' - 'msp430-gcc' - 'riscv-gcc' with: build-system: 'cmake' toolchain: ${{ matrix.toolchain }} - toolchain_url: ${{ fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain].toolchain_url }} build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain].family) }} one-per-family: ${{ github.event_name != 'pull_request' }} + # --------------------------------------- + # Build CMake arm-gcc + # only build with push, for PR: all board is built by circle-ci + # --------------------------------------- + cmake-arm-gcc: + if: github.event_name == 'push' + needs: set-matrix + uses: ./.github/workflows/build_util.yml + with: + build-system: 'cmake' + toolchain: 'arm-gcc' + build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)['arm-gcc'].family) }} + one-per-family: true + # --------------------------------------- # Build Make # --------------------------------------- @@ -81,7 +93,7 @@ jobs: fail-fast: false matrix: toolchain: - # 'arm-clang' is built by circle-ci + # 'arm-clang' would be built by circle-ci - 'aarch64-gcc' - 'arm-gcc' - 'msp430-gcc' @@ -90,7 +102,6 @@ jobs: with: build-system: 'make' toolchain: ${{ matrix.toolchain }} - toolchain_url: ${{ fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain].toolchain_url }} build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain].family) }} one-per-family: ${{ github.event_name != 'pull_request' }} @@ -120,14 +131,12 @@ jobs: fail-fast: false matrix: board: - # ESP32-S2 - 'espressif_kaluga_1' - # ESP32-S3 skip since devkitm is also compiled in hil-test workflow - #- 'espressif_s3_devkitm' + - 'espressif_s3_devkitm' with: build-system: 'cmake' toolchain: 'esp-idf' - toolchain_url: 'v5.1.1' + toolchain_version: 'v5.1.1' build-args: '["-b${{ matrix.board }}"]' # --------------------------------------- diff --git a/.github/workflows/build_util.yml b/.github/workflows/build_util.yml index 49a9feabd..e983f06d1 100644 --- a/.github/workflows/build_util.yml +++ b/.github/workflows/build_util.yml @@ -9,7 +9,7 @@ on: toolchain: required: true type: string - toolchain_url: + toolchain_version: required: false type: string build-args: @@ -40,7 +40,7 @@ jobs: uses: ./.github/actions/setup_toolchain with: toolchain: ${{ inputs.toolchain }} - toolchain_url: ${{ inputs.toolchain_url }} + toolchain_version: ${{ inputs.toolchain_version }} - name: Get Dependencies uses: ./.github/actions/get_deps @@ -58,11 +58,10 @@ jobs: shell: bash - name: Build - if: inputs.toolchain != 'esp-idf' run: | - python tools/build.py -s ${{ inputs.build-system }} ${{ steps.setup-toolchain.outputs.build_option }} ${{ steps.set-one-per-family.outputs.build_option }} ${{ matrix.arg }} - - - name: Build using ESP-IDF docker - if: inputs.toolchain == 'esp-idf' - run: | - docker run --rm -v $PWD:/project -w /project espressif/idf:${{ inputs.toolchain_url }} python3 tools/build.py ${{ matrix.arg }} + if [ "${{ inputs.toolchain }}" == "esp-idf" ]; then + docker run --rm -v $PWD:/project -w /project espressif/idf:${{ inputs.toolchain_version }} python3 tools/build.py ${{ matrix.arg }} + else + python tools/build.py -s ${{ inputs.build-system }} ${{ steps.setup-toolchain.outputs.build_option }} ${{ steps.set-one-per-family.outputs.build_option }} ${{ matrix.arg }} + fi + shell: bash diff --git a/.github/workflows/ci_set_matrix.py b/.github/workflows/ci_set_matrix.py index 7dc9a2fa1..a56bd4214 100644 --- a/.github/workflows/ci_set_matrix.py +++ b/.github/workflows/ci_set_matrix.py @@ -1,21 +1,22 @@ import json # toolchain, url -toolchain_list = { - "aarch64-gcc": "https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz", - "arm-clang": "https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-17.0.1/LLVMEmbeddedToolchainForArm-17.0.1-Linux-x86_64.tar.xz", - "arm-iar": "", - "arm-gcc": "", - "msp430-gcc": "http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/9_2_0_0/export/msp430-gcc-9.2.0.50_linux64.tar.bz2", - "riscv-gcc": "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz", - "rx-gcc": "http://gcc-renesas.com/downloads/get.php?f=rx/8.3.0.202004-gnurx/gcc-8.3.0.202004-GNURX-ELF.run", -} +toolchain_list = [ + "aarch64-gcc", + "arm-clang", + "arm-iar", + "arm-gcc", + "msp430-gcc", + "riscv-gcc", + "rx-gcc" +] # family: [supported toolchain] family_list = { "broadcom_32bit": ["arm-gcc"], "broadcom_64bit": ["aarch64-gcc"], - "ch32v20x ch32v307 fomu gd32vf103": ["riscv-gcc"], + "ch32v10x ch32v20x ch32v307 fomu gd32vf103": ["riscv-gcc"], + "da1469x": ["arm-gcc"], "imxrt": ["arm-gcc", "arm-clang"], "kinetis_k kinetis_kl kinetis_k32l2": ["arm-gcc", "arm-clang"], "lpc11 lpc13 lpc15": ["arm-gcc", "arm-clang"], @@ -36,14 +37,14 @@ family_list = { "stm32f7": ["arm-gcc", "arm-clang", "arm-iar"], "stm32g0 stm32g4 stm32h5": ["arm-gcc", "arm-clang", "arm-iar"], "stm32h7": ["arm-gcc", "arm-clang", "arm-iar"], - "stm32l4 stm32u5 stm32wb": ["arm-gcc", "arm-clang", "arm-iar"], + "stm32l0 stm32l4 stm32u5 stm32wb": ["arm-gcc", "arm-clang", "arm-iar"], "xmc4000": ["arm-gcc"], } def set_matrix_json(): matrix = {} - for toolchain in toolchain_list.keys(): + for toolchain in toolchain_list: filtered_families = [family for family, supported_toolchain in family_list.items() if toolchain in supported_toolchain] @@ -54,7 +55,7 @@ def set_matrix_json(): hfp_boards = [f"-b{board['name']}" for board in hfp_data['boards']] filtered_families = filtered_families + hfp_boards - matrix[toolchain] = {"family": filtered_families, "toolchain_url": toolchain_list[toolchain]} + matrix[toolchain] = {"family": filtered_families} print(json.dumps(matrix)) diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 622d261a0..d7f1fc066 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -26,7 +26,7 @@ jobs: with: oss-fuzz-project-name: 'tinyusb' language: c++ - fuzz-seconds: 600 + fuzz-seconds: 400 - name: Upload Crash uses: actions/upload-artifact@v4 diff --git a/.github/workflows/hil_test.yml b/.github/workflows/hil_test.yml index 39a9060a2..d02f13e05 100644 --- a/.github/workflows/hil_test.yml +++ b/.github/workflows/hil_test.yml @@ -17,6 +17,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + HIL_JSON: test/hil/rpi.json + jobs: # --------------------------------------- # Build Non Espressif @@ -36,16 +39,21 @@ jobs: sudo apt install -y jq # Non-Espresif boards - BOARDS_LIST=$(jq -r '.boards[] | select(.flasher != "esptool") | "-b " + .name' test/hil/pi4.json | tr '\n' ' ') + BOARDS_LIST=$(jq -r '.boards[] | select(.flasher != "esptool") | "-b " + .name' ${{ env.HIL_JSON }} | tr '\n' ' ') echo "BOARDS_LIST=$BOARDS_LIST" echo "BOARDS_LIST=$BOARDS_LIST" >> $GITHUB_ENV echo "BOARDS_LIST=$BOARDS_LIST" >> $GITHUB_OUTPUT - - name: Setup Toolchain + - name: Setup arm-gcc toolchain uses: ./.github/actions/setup_toolchain with: toolchain: 'arm-gcc' + - name: Setup risv-gcc toolchain + uses: ./.github/actions/setup_toolchain + with: + toolchain: 'riscv-gcc' + - name: Get Dependencies uses: ./.github/actions/get_deps with: @@ -57,71 +65,22 @@ jobs: - name: Upload Artifacts for Hardware Testing uses: actions/upload-artifact@v4 with: - name: hil_pi4 + name: hil_rpi path: | cmake-build/cmake-build-*/*/*/*.elf cmake-build/cmake-build-*/*/*/*.bin - # --------------------------------------- - # Build Espressif - # --------------------------------------- - build-esp: - runs-on: ubuntu-latest - outputs: - BOARDS_LIST: ${{ steps.parse_hil_json.outputs.BOARDS_LIST }} - steps: - - name: Checkout TinyUSB - uses: actions/checkout@v4 - - - name: Parse HIL json - id: parse_hil_json - run: | - sudo apt install -y jq - # Espressif boards - BOARDS_LIST=$(jq -r '.boards[] | select(.flasher == "esptool") | "-b " + .name' test/hil/pi4.json | tr '\n' ' ') - echo "BOARDS_LIST=$BOARDS_LIST" - echo "BOARDS_LIST=$BOARDS_LIST" >> $GITHUB_ENV - echo "BOARDS_LIST=$BOARDS_LIST" >> $GITHUB_OUTPUT - - - name: Setup ESP-IDF - if: env.BOARDS_LIST != '' - uses: ./.github/actions/setup_toolchain - with: - toolchain: 'esp-idf' - toolchain_url: 'v5.1.1' - - - name: Get Dependencies - uses: ./.github/actions/get_deps - with: - arg: ${{ env.BOARDS_LIST }} - - - name: Build Espressif - if: env.BOARDS_LIST != '' - run: docker run --rm -v $PWD:/project -w /project espressif/idf:v5.1.1 python3 tools/build.py $BOARDS_LIST - - - name: Upload Artifacts for Hardware Testing - uses: actions/upload-artifact@v4 - with: - name: hil_pi4_esp - path: | - cmake-build/cmake-build-*/*/*/*.bin - cmake-build/cmake-build-*/*/*/bootloader/bootloader.bin - cmake-build/cmake-build-*/*/*/partition_table/partition-table.bin - cmake-build/cmake-build-*/*/*/config.env - cmake-build/cmake-build-*/*/*/flash_args - # --------------------------------------- # Hardware in the loop (HIL) - # Current self-hosted instance is running on an RPI4. For attached hardware checkout test/hil/pi4.json + # self-hosted running on an RPI. For attached hardware checkout test/hil/rpi.json # --------------------------------------- - hil-pi4: + hil-rpi: if: github.repository_owner == 'hathach' needs: - build - - build-esp - runs-on: [self-hosted, rp2040, nrf52840, esp32s3, hardware-in-the-loop] + runs-on: [self-hosted, ARM64, rpi, hardware-in-the-loop] env: - BOARDS_LIST: "${{ needs.build.outputs.BOARDS_LIST }} ${{ needs.build-esp.outputs.BOARDS_LIST }}" + BOARDS_LIST: "${{ needs.build-esp.outputs.BOARDS_LIST }} ${{ needs.build.outputs.BOARDS_LIST }}" steps: - name: Clean workspace run: | @@ -129,12 +88,12 @@ jobs: rm -rf "${{ github.workspace }}" mkdir -p "${{ github.workspace }}" - # USB bus on rpi4 is not stable, reset it before testing - - name: Reset USB bus - run: | - # lsusb -t - # reset VIA Labs 2.0 hub - sudo usbreset 001/002 + # USB bus on rpi is not stable, reset it before testing +# - name: Reset USB bus +# run: | +# echo "1-2" | sudo tee /sys/bus/usb/drivers/usb/unbind +# sleep 5 +# echo "1-2" | sudo tee /sys/bus/usb/drivers/usb/bind - name: Checkout TinyUSB uses: actions/checkout@v4 @@ -144,14 +103,8 @@ jobs: - name: Download Artifacts uses: actions/download-artifact@v4 with: - name: hil_pi4 - path: cmake-build - - - name: Download Artifacts - uses: actions/download-artifact@v4 - with: - name: hil_pi4_esp path: cmake-build + merge-multiple: true - name: Test on actual hardware run: | @@ -159,4 +112,4 @@ jobs: echo "::group::{cmake-build contents}" tree cmake-build echo "::endgroup::" - python3 test/hil/hil_test.py $BOARDS_LIST pi4.json + python3 test/hil/hil_test.py $BOARDS_LIST ${{ env.HIL_JSON }} diff --git a/.gitignore b/.gitignore index 7a37d65dc..f2150a26f 100644 --- a/.gitignore +++ b/.gitignore @@ -31,62 +31,4 @@ cov-int __pycache__ cmake-build-* sdkconfig - -# submodules -hw/mcu/allwinner -hw/mcu/bridgetek/ft9xx/ft90x-sdk -hw/mcu/broadcom -hw/mcu/gd/nuclei-sdk -hw/mcu/infineon/mtb-xmclib-cat3 -hw/mcu/microchip -hw/mcu/mindmotion/mm32sdk -hw/mcu/nordic/nrfx -hw/mcu/nuvoton -hw/mcu/nxp/lpcopen -hw/mcu/nxp/mcux-sdk -hw/mcu/nxp/nxp_sdk -hw/mcu/raspberry_pi/Pico-PIO-USB -hw/mcu/renesas/rx -hw/mcu/silabs/cmsis-dfp-efm32gg12b -hw/mcu/sony/cxd56/spresense-exported-sdk -hw/mcu/st/cmsis_device_f0 -hw/mcu/st/cmsis_device_f1 -hw/mcu/st/cmsis_device_f2 -hw/mcu/st/cmsis_device_f3 -hw/mcu/st/cmsis_device_f4 -hw/mcu/st/cmsis_device_f7 -hw/mcu/st/cmsis_device_g0 -hw/mcu/st/cmsis_device_g4 -hw/mcu/st/cmsis_device_h5 -hw/mcu/st/cmsis_device_h7 -hw/mcu/st/cmsis_device_l0 -hw/mcu/st/cmsis_device_l1 -hw/mcu/st/cmsis_device_l4 -hw/mcu/st/cmsis_device_l5 -hw/mcu/st/cmsis_device_u5 -hw/mcu/st/cmsis_device_wb -hw/mcu/st/stm32f0xx_hal_driver -hw/mcu/st/stm32f1xx_hal_driver -hw/mcu/st/stm32f2xx_hal_driver -hw/mcu/st/stm32f3xx_hal_driver -hw/mcu/st/stm32f4xx_hal_driver -hw/mcu/st/stm32f7xx_hal_driver -hw/mcu/st/stm32g0xx_hal_driver -hw/mcu/st/stm32g4xx_hal_driver -hw/mcu/st/stm32h5xx_hal_driver -hw/mcu/st/stm32h7xx_hal_driver -hw/mcu/st/stm32l0xx_hal_driver -hw/mcu/st/stm32l1xx_hal_driver -hw/mcu/st/stm32l4xx_hal_driver -hw/mcu/st/stm32l5xx_hal_driver -hw/mcu/st/stm32u5xx_hal_driver -hw/mcu/st/stm32wbxx_hal_driver -hw/mcu/ti -hw/mcu/wch/ch32v20x -hw/mcu/wch/ch32v307 -hw/mcu/wch/ch32f20x -lib/CMSIS_5 -lib/FreeRTOS-Kernel -lib/lwip -lib/sct_neopixel -tools/uf2 +.PVS-Studio diff --git a/.idea/cmake.xml b/.idea/cmake.xml index 5568609dd..729309ebb 100644 --- a/.idea/cmake.xml +++ b/.idea/cmake.xml @@ -3,138 +3,143 @@ - - - - + + + + + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - + + + + + + + + - - - - - + + + + + - - + + - - - - - + + + + + - + - - - - - - - - + + + + + + + + + - + - - - - + + + + + + \ No newline at end of file diff --git a/README.rst b/README.rst index b5f9fc149..1ae8c5375 100644 --- a/README.rst +++ b/README.rst @@ -151,8 +151,9 @@ Following CPUs are supported, check out `Supported Devices`_ for comprehensive l +--------------+------------------------------------------------------------+ | Sony | CXD56 | +--------------+------------------------------------------------------------+ -| ST STM32 | F0, F1, F2, F3, F4, F7, H5, H7, G0, G4, L0, L1, L4, L4+, | -| | U5, WB | +| ST STM32 | F0, F1, F2, F3, F4, F7, G0, G4, H5, H7, | +| | | +| | L0, L1, L4, L4+, L5, U5, WB | +--------------+------------------------------------------------------------+ | TI | MSP430, MSP432E4, TM4C123 | +--------------+------------------------------------------------------------+ diff --git a/docs/reference/supported.rst b/docs/reference/supported.rst index 3ca203e88..edb6b69ca 100644 --- a/docs/reference/supported.rst +++ b/docs/reference/supported.rst @@ -104,21 +104,33 @@ Supported MCUs | +-----------------------+--------+------+-----------+-------------------+--------------+ | | F7 | ✔ | | ✔ | dwc2 | | | +-----------------------+--------+------+-----------+-------------------+--------------+ -| | H7 | ✔ | | ✔ | dwc2 | | +| | G0 | ✔ | | ✖ | stm32_fsdev | | | +-----------------------+--------+------+-----------+-------------------+--------------+ | | G4 | ✔ | ✖ | ✖ | stm32_fsdev | | | +-----------------------+--------+------+-----------+-------------------+--------------+ -| | L0, L1 | ✔ | ✖ | ✖ | stm32_fsdev | | +| | H5 | ✔ | | ✖ | stm32_fsdev | | +| +-----------------------+--------+------+-----------+-------------------+--------------+ +| | H7 | ✔ | | ✔ | dwc2 | | +| +-----------------------+--------+------+-----------+-------------------+--------------+ +| | L0 | ✔ | ✖ | ✖ | stm32_fsdev | | +| +-----------------------+--------+------+-----------+-------------------+--------------+ +| | L1 | ✔ | ✖ | ✖ | stm32_fsdev | | | +----+------------------+--------+------+-----------+-------------------+--------------+ | | L4 | 4x2, 4x3 | ✔ | ✖ | ✖ | stm32_fsdev | | | | +------------------+--------+------+-----------+-------------------+--------------+ -| | | 4x5, 4x6 | ✔ | | | dwc2 | | +| | | 4x5, 4x6 | ✔ | | ✖ | dwc2 | | | +----+------------------+--------+------+-----------+-------------------+--------------+ -| | L4+ | ✔ | | | dwc2 | | +| | L4+ | ✔ | | ✖ | dwc2 | | | +-----------------------+--------+------+-----------+-------------------+--------------+ -| | U5 | ✔ | | ✔ | dwc2 | | -| +-----------------------+--------+------+-----------+-------------------+--------------+ -| | WBx5 | ✔ | | | stm32_fsdev | | +| | L5 | ✔ | ✖ | ✖ | stm32_fsdev | | +| +----+------------------+--------+------+-----------+-------------------+--------------+ +| | U5 | 535, 545 | ✔ | | ✖ | stm32_fsdev | | +| | +------------------+--------+------+-----------+-------------------+--------------+ +| | | 575, 585 | ✔ | | ✖ | dwc2 | | +| | +------------------+--------+------+-----------+-------------------+--------------+ +| | | 59x,5Ax,5Fx,5Gx | ✔ | | ✔ | dwc2 | | +| +----+------------------+--------+------+-----------+-------------------+--------------+ +| | WBx5 | ✔ | ✖ | ✖ | stm32_fsdev | | +--------------+-----------------------+--------+------+-----------+-------------------+--------------+ | TI | MSP430 | ✔ | ✖ | ✖ | msp430x5xx | | | +-----------------------+--------+------+-----------+-------------------+--------------+ @@ -129,7 +141,9 @@ Supported MCUs | ValentyUSB | eptri | ✔ | ✖ | ✖ | eptri | | +--------------+-----------------------+--------+------+-----------+-------------------+--------------+ | WCH | CH32F20x | ✔ | | ✔ | ch32f205 | | +| +-----------------------+--------+------+-----------+-------------------+--------------+ | | CH32V20x | ✔ | | ✖ | ch32v20x | | +| +-----------------------+--------+------+-----------+-------------------+--------------+ | | CH32V307 | ✔ | | ✔ | ch32v307 | | +--------------+-----------------------+--------+------+-----------+-------------------+--------------+ @@ -137,11 +151,12 @@ Supported MCUs Table Legend ------------ -= =================== -✔ Supported -⚠ WIP/partial support -✖ Not supported -= =================== +========= ========================= +✔ Supported +⚠ Partial support +✖ Not supported by hardware +\[empty\] Unknown +========= ========================= Supported Boards ================ diff --git a/examples/build_system/cmake/cpu/cortex-m33-nodsp.cmake b/examples/build_system/cmake/cpu/cortex-m33-nodsp.cmake new file mode 100644 index 000000000..b3cd743fd --- /dev/null +++ b/examples/build_system/cmake/cpu/cortex-m33-nodsp.cmake @@ -0,0 +1,25 @@ +if (TOOLCHAIN STREQUAL "gcc") + set(TOOLCHAIN_COMMON_FLAGS + -mthumb + -mcpu=cortex-m33+nodsp + -mfloat-abi=hard + -mfpu=fpv5-sp-d16 + ) + set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "") + +elseif (TOOLCHAIN STREQUAL "clang") + set(TOOLCHAIN_COMMON_FLAGS + --target=arm-none-eabi + -mcpu=cortex-m33+nodsp + -mfpu=fpv5-sp-d16 + ) + set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "") + +elseif (TOOLCHAIN STREQUAL "iar") + set(TOOLCHAIN_COMMON_FLAGS + --cpu cortex-m33+nodsp + --fpu VFPv5-SP + ) + set(FREERTOS_PORT IAR_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "") + +endif () diff --git a/examples/build_system/make/make.mk b/examples/build_system/make/make.mk index a78f4130d..1500a51e0 100644 --- a/examples/build_system/make/make.mk +++ b/examples/build_system/make/make.mk @@ -109,6 +109,10 @@ INC += \ BOARD_UPPER = $(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,$(subst z,Z,$(subst -,_,$(BOARD)))))))))))))))))))))))))))) CFLAGS += -DBOARD_$(BOARD_UPPER) +ifdef CFLAGS_CLI + CFLAGS += $(CFLAGS_CLI) +endif + # use max3421 as host controller ifeq (${MAX3421_HOST},1) SRC_C += src/portable/analog/max3421/hcd_max3421.c diff --git a/examples/build_system/make/rules.mk b/examples/build_system/make/rules.mk index 102c6db0c..f322dbae6 100644 --- a/examples/build_system/make/rules.mk +++ b/examples/build_system/make/rules.mk @@ -145,6 +145,12 @@ OPENOCD_WCH_OPTION ?= flash-openocd-wch: $(BUILD)/$(PROJECT).elf $(OPENOCD_WCH) $(OPENOCD_WCH_OPTION) -c init -c halt -c "flash write_image $<" -c reset -c exit +# --------------- wlink-rs ----------------- +# flash with https://github.com/ch32-rs/wlink +WLINK_RS ?= wlink +flash-wlink-rs: $(BUILD)/$(PROJECT).elf + $(WLINK_RS) flash $< + # --------------- dfu-util ----------------- DFU_UTIL_OPTION ?= -a 0 flash-dfu-util: $(BUILD)/$(PROJECT).bin diff --git a/examples/device/audio_4_channel_mic_freertos/skip.txt b/examples/device/audio_4_channel_mic_freertos/skip.txt index 4769af009..012ca4e74 100644 --- a/examples/device/audio_4_channel_mic_freertos/skip.txt +++ b/examples/device/audio_4_channel_mic_freertos/skip.txt @@ -1,3 +1,4 @@ +mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 mcu:CXD56 diff --git a/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h index 6cc7a6577..869500ad2 100644 --- a/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ b/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h @@ -67,6 +67,7 @@ #define configENABLE_FPU 1 #define configENABLE_TRUSTZONE 0 #define configMINIMAL_SECURE_STACK_SIZE ( 1024 ) +#define configRUN_FREERTOS_SECURE_ONLY 1 #define configUSE_PREEMPTION 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 diff --git a/examples/device/audio_test_freertos/skip.txt b/examples/device/audio_test_freertos/skip.txt index 6aaa27661..c13667f56 100644 --- a/examples/device/audio_test_freertos/skip.txt +++ b/examples/device/audio_test_freertos/skip.txt @@ -1,3 +1,4 @@ +mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 mcu:CXD56 diff --git a/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h index 6cc7a6577..869500ad2 100644 --- a/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ b/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h @@ -67,6 +67,7 @@ #define configENABLE_FPU 1 #define configENABLE_TRUSTZONE 0 #define configMINIMAL_SECURE_STACK_SIZE ( 1024 ) +#define configRUN_FREERTOS_SECURE_ONLY 1 #define configUSE_PREEMPTION 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 diff --git a/examples/device/cdc_dual_ports/src/main.c b/examples/device/cdc_dual_ports/src/main.c index 1167a5d50..ef12186f2 100644 --- a/examples/device/cdc_dual_ports/src/main.c +++ b/examples/device/cdc_dual_ports/src/main.c @@ -119,6 +119,26 @@ static void cdc_task(void) { } } +// Invoked when cdc when line state changed e.g connected/disconnected +// Use to reset to DFU when disconnect with 1200 bps +void tud_cdc_line_state_cb(uint8_t instance, bool dtr, bool rts) { + (void)rts; + + // DTR = false is counted as disconnected + if (!dtr) { + // touch1200 only with first CDC instance (Serial) + if (instance == 0) { + cdc_line_coding_t coding; + tud_cdc_get_line_coding(&coding); + if (coding.bit_rate == 1200) { + if (board_reset_to_bootloader) { + board_reset_to_bootloader(); + } + } + } + } +} + //--------------------------------------------------------------------+ // BLINKING TASK //--------------------------------------------------------------------+ diff --git a/examples/device/cdc_msc/skip.txt b/examples/device/cdc_msc/skip.txt index 833fd072c..b6252e405 100644 --- a/examples/device/cdc_msc/skip.txt +++ b/examples/device/cdc_msc/skip.txt @@ -1,2 +1,3 @@ mcu:SAMD11 family:espressif +board:ch32v203g_r0_1v0 diff --git a/examples/device/cdc_msc_freertos/skip.txt b/examples/device/cdc_msc_freertos/skip.txt index 457ddb760..75a79aff4 100644 --- a/examples/device/cdc_msc_freertos/skip.txt +++ b/examples/device/cdc_msc_freertos/skip.txt @@ -1,3 +1,4 @@ +mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 mcu:CXD56 diff --git a/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h index 6cc7a6577..869500ad2 100644 --- a/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ b/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h @@ -67,6 +67,7 @@ #define configENABLE_FPU 1 #define configENABLE_TRUSTZONE 0 #define configMINIMAL_SECURE_STACK_SIZE ( 1024 ) +#define configRUN_FREERTOS_SECURE_ONLY 1 #define configUSE_PREEMPTION 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 diff --git a/examples/device/cdc_msc_freertos/src/msc_disk.c b/examples/device/cdc_msc_freertos/src/msc_disk.c index c8a04bb74..d2f8628f1 100644 --- a/examples/device/cdc_msc_freertos/src/msc_disk.c +++ b/examples/device/cdc_msc_freertos/src/msc_disk.c @@ -28,6 +28,9 @@ #if CFG_TUD_MSC +// whether host does safe-eject +static bool ejected = false; + // Some MCU doesn't have enough 8KB SRAM to store the whole disk // We will use Flash as read-only disk with board that has // CFG_EXAMPLE_MSC_READONLY defined @@ -137,7 +140,14 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) { (void) lun; - return true; // RAM disk is always ready + // RAM disk is ready until ejected + if (ejected) { + // Additional Sense 3A-00 is NOT_FOUND + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); + return false; + } + + return true; } // Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size @@ -166,6 +176,7 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo }else { // unload disk storage + ejected = true; } } @@ -187,6 +198,17 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buff return (int32_t) bufsize; } +bool tud_msc_is_writable_cb (uint8_t lun) +{ + (void) lun; + +#ifdef CFG_EXAMPLE_MSC_READONLY + return false; +#else + return true; +#endif +} + // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and return number of written bytes int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c index c57d98a1a..70b0949a9 100644 --- a/examples/device/cdc_uac2/src/uac2_app.c +++ b/examples/device/cdc_uac2/src/uac2_app.c @@ -141,7 +141,7 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req 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) + else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { if (request->bRequest == AUDIO_CS_REQ_RANGE) { diff --git a/examples/device/dynamic_configuration/skip.txt b/examples/device/dynamic_configuration/skip.txt index 833fd072c..b6252e405 100644 --- a/examples/device/dynamic_configuration/skip.txt +++ b/examples/device/dynamic_configuration/skip.txt @@ -1,2 +1,3 @@ mcu:SAMD11 family:espressif +board:ch32v203g_r0_1v0 diff --git a/examples/device/hid_composite_freertos/skip.txt b/examples/device/hid_composite_freertos/skip.txt index 6aaa27661..c13667f56 100644 --- a/examples/device/hid_composite_freertos/skip.txt +++ b/examples/device/hid_composite_freertos/skip.txt @@ -1,3 +1,4 @@ +mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 mcu:CXD56 diff --git a/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h index 6cc7a6577..869500ad2 100644 --- a/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ b/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h @@ -67,6 +67,7 @@ #define configENABLE_FPU 1 #define configENABLE_TRUSTZONE 0 #define configMINIMAL_SECURE_STACK_SIZE ( 1024 ) +#define configRUN_FREERTOS_SECURE_ONLY 1 #define configUSE_PREEMPTION 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 diff --git a/examples/device/net_lwip_webserver/CMakeLists.txt b/examples/device/net_lwip_webserver/CMakeLists.txt index c39fd32c5..13923b583 100644 --- a/examples/device/net_lwip_webserver/CMakeLists.txt +++ b/examples/device/net_lwip_webserver/CMakeLists.txt @@ -5,7 +5,14 @@ include(${CMAKE_CURRENT_LIST_DIR}/../../../hw/bsp/family_support.cmake) # gets PROJECT name for the example (e.g. -) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) +# Prefer the tinyusb lwip set(LWIP ${TOP}/lib/lwip) + +# If we can't find one from tinyusb then check cmake var before giving up +if (NOT EXISTS ${LWIP}/src) + set(LWIP ${TINYUSB_LWIP_PATH}) +endif() + if (NOT EXISTS ${LWIP}/src) family_example_missing_dependency(${PROJECT} "lib/lwip") return() diff --git a/examples/device/net_lwip_webserver/skip.txt b/examples/device/net_lwip_webserver/skip.txt index 51af58667..85c450e82 100644 --- a/examples/device/net_lwip_webserver/skip.txt +++ b/examples/device/net_lwip_webserver/skip.txt @@ -1,3 +1,4 @@ +mcu:CH32V103 mcu:CH32V20X mcu:LPC11UXX mcu:LPC13XX diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c index 0ea6e7025..c285211b8 100644 --- a/examples/device/uac2_headset/src/main.c +++ b/examples/device/uac2_headset/src/main.c @@ -229,7 +229,7 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req 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) + else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { if (request->bRequest == AUDIO_CS_REQ_RANGE) { diff --git a/examples/device/uac2_speaker_fb/CMakeLists.txt b/examples/device/uac2_speaker_fb/CMakeLists.txt new file mode 100644 index 000000000..e92a57148 --- /dev/null +++ b/examples/device/uac2_speaker_fb/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.17) + +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} C CXX ASM) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +# Espressif has its own cmake build system +if(FAMILY STREQUAL "espressif") + return() +endif() + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example without RTOS. +# See the corresponding function in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT} noos) diff --git a/examples/device/uac2_speaker_fb/Makefile b/examples/device/uac2_speaker_fb/Makefile new file mode 100644 index 000000000..7fa475da5 --- /dev/null +++ b/examples/device/uac2_speaker_fb/Makefile @@ -0,0 +1,11 @@ +include ../../build_system/make/make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +EXAMPLE_SOURCE += $(wildcard src/*.c) +SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) + +include ../../build_system/make/rules.mk diff --git a/examples/device/uac2_speaker_fb/skip.txt b/examples/device/uac2_speaker_fb/skip.txt new file mode 100644 index 000000000..234f1ebed --- /dev/null +++ b/examples/device/uac2_speaker_fb/skip.txt @@ -0,0 +1,8 @@ +mcu:LPC11UXX +mcu:LPC13XX +mcu:NUC121 +mcu:SAMD11 +mcu:SAME5X +mcu:SAMG +board:stm32l052dap52 +family:broadcom_64bit diff --git a/examples/device/uac2_speaker_fb/src/audio_debug.py b/examples/device/uac2_speaker_fb/src/audio_debug.py new file mode 100644 index 000000000..336c46c01 --- /dev/null +++ b/examples/device/uac2_speaker_fb/src/audio_debug.py @@ -0,0 +1,69 @@ +# Install python3 HID package https://pypi.org/project/hid/ +# Install python3 matplotlib package https://pypi.org/project/matplotlib/ + +from ctypes import * +try: + import hid + import matplotlib.pyplot as plt + import matplotlib.animation as animation +except: + print("Missing import, please try 'pip install hid matplotlib' or consult your OS's python package manager.") + +# Example must be compiled with CFG_AUDIO_DEBUG=1 +VID = 0xcafe +PID = 0x4014 + +CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX = 2 + +class audio_debug_info_t (Structure): + _fields_ = [("sample_rate", c_uint32), + ("alt_settings", c_uint8), + ("mute", (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1) * c_int8), + ("volume", (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1) * c_int16), + ("fifo_size", c_uint16), + ("fifo_count", c_uint16), + ("fifo_count_avg", c_uint16) + ] + +dev = hid.Device(VID, PID) + +if dev: + # Create figure for plotting + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + fifo_avg = [] + fifo_cnt = [] + # This function is called periodically from FuncAnimation + def animate(i): + info = None + for i in range(30): + try: + str_in = dev.read(64, 50) + info = audio_debug_info_t.from_buffer_copy(str_in) + + global fifo_avg + global fifo_cnt + fifo_avg.append(info.fifo_count_avg) + fifo_cnt.append(info.fifo_count) + except: + exit(1) + # Limit to 1000 items + fifo_avg = fifo_avg[-1000:] + fifo_cnt = fifo_cnt[-1000:] + + if info is not None: + # Draw x and y lists + ax.clear() + ax.plot(fifo_cnt, label='FIFO count') + ax.plot(fifo_avg, label='FIFO average') + ax.legend() + ax.set_ylim(bottom=0, top=info.fifo_size) + + # Format plot + plt.title('FIFO information') + plt.grid() + + print(f'Sample rate:{info.sample_rate} | Alt settings:{info.alt_settings} | Volume:{info.volume[:]}') + + ani = animation.FuncAnimation(fig, animate, interval=10) + plt.show() diff --git a/examples/device/uac2_speaker_fb/src/common_types.h b/examples/device/uac2_speaker_fb/src/common_types.h new file mode 100644 index 000000000..174e26671 --- /dev/null +++ b/examples/device/uac2_speaker_fb/src/common_types.h @@ -0,0 +1,52 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023 HiFiPhile + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _COMMON_TYPES_H_ +#define _COMMON_TYPES_H_ + +enum +{ + ITF_NUM_AUDIO_CONTROL = 0, + ITF_NUM_AUDIO_STREAMING, +#if CFG_AUDIO_DEBUG + ITF_NUM_DEBUG, +#endif + ITF_NUM_TOTAL +}; + +#if CFG_AUDIO_DEBUG +typedef struct + { + uint32_t sample_rate; + uint8_t alt_settings; + int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; + int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; + uint16_t fifo_size; + uint16_t fifo_count; + uint16_t fifo_count_avg; + } audio_debug_info_t; +#endif + +#endif diff --git a/examples/device/uac2_speaker_fb/src/main.c b/examples/device/uac2_speaker_fb/src/main.c new file mode 100644 index 000000000..0a8529a61 --- /dev/null +++ b/examples/device/uac2_speaker_fb/src/main.c @@ -0,0 +1,505 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Jerzy Kasenberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include +#include + +#include "bsp/board_api.h" +#include "tusb.h" +#include "usb_descriptors.h" +#include "common_types.h" + +#ifdef CFG_QUIRK_OS_GUESSING +#include "quirk_os_guessing.h" +#endif + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTOTYPES +//--------------------------------------------------------------------+ + +// List of supported sample rates +#if defined(__RX__) + const uint32_t sample_rates[] = {44100, 48000}; +#else + const uint32_t sample_rates[] = {44100, 48000, 88200, 96000}; +#endif + +uint32_t current_sample_rate = 44100; + +#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates) + +/* Blink pattern + * - 25 ms : streaming data + * - 250 ms : device not mounted + * - 1000 ms : device mounted + * - 2500 ms : device is suspended + */ +enum +{ + BLINK_STREAMING = 25, + BLINK_NOT_MOUNTED = 250, + BLINK_MOUNTED = 1000, + BLINK_SUSPENDED = 2500, +}; + +enum +{ + VOLUME_CTRL_0_DB = 0, + VOLUME_CTRL_10_DB = 2560, + VOLUME_CTRL_20_DB = 5120, + VOLUME_CTRL_30_DB = 7680, + VOLUME_CTRL_40_DB = 10240, + VOLUME_CTRL_50_DB = 12800, + VOLUME_CTRL_60_DB = 15360, + VOLUME_CTRL_70_DB = 17920, + VOLUME_CTRL_80_DB = 20480, + VOLUME_CTRL_90_DB = 23040, + VOLUME_CTRL_100_DB = 25600, + VOLUME_CTRL_SILENCE = 0x8000, +}; + +static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; + +// Audio controls +// Current states +int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 +int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 + +// Buffer for speaker data +uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ/2]; + +void led_blinking_task(void); +void audio_task(void); + +#if CFG_AUDIO_DEBUG +void audio_debug_task(void); +uint8_t current_alt_settings; +uint16_t fifo_count; +uint32_t fifo_count_avg; +#endif + +/*------------- MAIN -------------*/ +int main(void) +{ + board_init(); + + // init device stack on configured roothub port + tud_init(BOARD_TUD_RHPORT); + + if (board_init_after_tusb) { + board_init_after_tusb(); + } + + TU_LOG1("Speaker running\r\n"); + + while (1) + { + tud_task(); // TinyUSB device task + led_blinking_task(); +#if CFG_AUDIO_DEBUG + audio_debug_task(); +#endif + audio_task(); + } +} + +//--------------------------------------------------------------------+ +// Device callbacks +//--------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) +{ + blink_interval_ms = BLINK_MOUNTED; +} + +// Invoked when device is unmounted +void tud_umount_cb(void) +{ + blink_interval_ms = BLINK_NOT_MOUNTED; +} + +// Invoked when usb bus is suspended +// remote_wakeup_en : if host allow us to perform remote wakeup +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +void tud_suspend_cb(bool remote_wakeup_en) +{ + (void)remote_wakeup_en; + blink_interval_ms = BLINK_SUSPENDED; +} + +// Invoked when usb bus is resumed +void tud_resume_cb(void) +{ + blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; +} + +//--------------------------------------------------------------------+ +// Application Callback API Implementations +//--------------------------------------------------------------------+ + +// Helper for clock get requests +static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) +{ + TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); + + if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) + { + if (request->bRequest == AUDIO_CS_REQ_CUR) + { + TU_LOG1("Clock get current freq %lu\r\n", current_sample_rate); + + audio_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) }; + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf)); + } + else if (request->bRequest == AUDIO_CS_REQ_RANGE) + { + audio_control_range_4_n_t(N_SAMPLE_RATES) rangef = + { + .wNumSubRanges = tu_htole16(N_SAMPLE_RATES) + }; + TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES); + for(uint8_t i = 0; i < N_SAMPLE_RATES; i++) + { + rangef.subrange[i].bMin = (int32_t) sample_rates[i]; + rangef.subrange[i].bMax = (int32_t) sample_rates[i]; + rangef.subrange[i].bRes = 0; + TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes); + } + + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &rangef, sizeof(rangef)); + } + } + else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID && + request->bRequest == AUDIO_CS_REQ_CUR) + { + audio_control_cur_1_t cur_valid = { .bCur = 1 }; + TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur); + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_valid, sizeof(cur_valid)); + } + TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", + request->bEntityID, request->bControlSelector, request->bRequest); + return false; +} + +// Helper for clock set requests +static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) +{ + (void)rhport; + + TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); + TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); + + if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) + { + TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t)); + + current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur; + + TU_LOG1("Clock set current freq: %ld\r\n", current_sample_rate); + + return true; + } + else + { + TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", + request->bEntityID, request->bControlSelector, request->bRequest); + return false; + } +} + +// Helper for feature unit get requests +static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) +{ + TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT); + + if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) + { + audio_control_cur_1_t mute1 = { .bCur = mute[request->bChannelNumber] }; + TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur); + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &mute1, sizeof(mute1)); + } + else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) + { + if (request->bRequest == AUDIO_CS_REQ_RANGE) + { + audio_control_range_2_n_t(1) range_vol = { + .wNumSubRanges = tu_htole16(1), + .subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) } + }; + TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber, + range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256); + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &range_vol, sizeof(range_vol)); + } + else if (request->bRequest == AUDIO_CS_REQ_CUR) + { + audio_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[request->bChannelNumber]) }; + TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256); + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_vol, sizeof(cur_vol)); + } + } + TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", + request->bEntityID, request->bControlSelector, request->bRequest); + + return false; +} + +// Helper for feature unit set requests +static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) +{ + (void)rhport; + + TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT); + TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); + + if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) + { + TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t)); + + mute[request->bChannelNumber] = ((audio_control_cur_1_t const *)buf)->bCur; + + TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]); + + return true; + } + else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) + { + TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t)); + + volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur; + + TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256); + + return true; + } + else + { + TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", + request->bEntityID, request->bControlSelector, request->bRequest); + return false; + } +} + +// Invoked when audio class specific get request received for an entity +bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) +{ + audio_control_request_t const *request = (audio_control_request_t const *)p_request; + + if (request->bEntityID == UAC2_ENTITY_CLOCK) + return tud_audio_clock_get_request(rhport, request); + if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT) + return tud_audio_feature_unit_get_request(rhport, request); + else + { + TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n", + request->bEntityID, request->bControlSelector, request->bRequest); + } + return false; +} + +// Invoked when audio class specific set request received for an entity +bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) +{ + audio_control_request_t const *request = (audio_control_request_t const *)p_request; + + if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT) + return tud_audio_feature_unit_set_request(rhport, request, buf); + if (request->bEntityID == UAC2_ENTITY_CLOCK) + return tud_audio_clock_set_request(rhport, request, buf); + TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n", + request->bEntityID, request->bControlSelector, request->bRequest); + + return false; +} + +bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) +{ + (void)rhport; + + uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); + uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); + + if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0) + blink_interval_ms = BLINK_MOUNTED; + + return true; +} + +bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) +{ + (void)rhport; + uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); + uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); + + TU_LOG2("Set interface %d alt %d\r\n", itf, alt); + if (ITF_NUM_AUDIO_STREAMING == itf && alt != 0) + blink_interval_ms = BLINK_STREAMING; + +#if CFG_AUDIO_DEBUG + current_alt_settings = alt; +#endif + + return true; +} + +void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param) +{ + (void)func_id; + (void)alt_itf; + // Set feedback method to fifo counting + feedback_param->method = AUDIO_FEEDBACK_METHOD_FIFO_COUNT; + feedback_param->sample_freq = current_sample_rate; +} + +#if CFG_AUDIO_DEBUG +bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) +{ + (void)rhport; + (void)n_bytes_received; + (void)func_id; + (void)ep_out; + (void)cur_alt_setting; + + fifo_count = tud_audio_available(); + // Same averaging method used in UAC2 class + fifo_count_avg = (uint32_t)(((uint64_t)fifo_count_avg * 63 + ((uint32_t)fifo_count << 16)) >> 6); + + return true; +} +#endif + +#if CFG_QUIRK_OS_GUESSING +bool tud_audio_feedback_format_correction_cb(uint8_t func_id) +{ + (void)func_id; + if(tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) { + return true; + } else { + return false; + } +} +#endif +//--------------------------------------------------------------------+ +// AUDIO Task +//--------------------------------------------------------------------+ + +void audio_task(void) +{ + // Replace audio_task() with your I2S transmit callback. + // Here we simulate a callback called every 1ms. + static uint32_t start_ms = 0; + uint32_t curr_ms = board_millis(); + if ( start_ms == curr_ms ) return; // not enough time + start_ms = curr_ms; + + uint16_t length = current_sample_rate/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX; + + if (current_sample_rate == 44100 && (curr_ms % 10 == 0)) + { + // Take one more sample every 10 cycles, to have a average reading speed of 44.1 + // This correction is not needed in real world cases + length += CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX; + } else + if (current_sample_rate == 88200 && (curr_ms % 5 == 0)) + { + // Take one more sample every 5 cycles, to have a average reading speed of 88.2 + // This correction is not needed in real world cases + length += CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX; + } + + tud_audio_read(i2s_dummy_buffer, length); +} + +//--------------------------------------------------------------------+ +// BLINKING TASK +//--------------------------------------------------------------------+ +void led_blinking_task(void) +{ + static uint32_t start_ms = 0; + static bool led_state = false; + + // Blink every interval ms + if (board_millis() - start_ms < blink_interval_ms) return; + start_ms += blink_interval_ms; + + board_led_write(led_state); + led_state = 1 - led_state; +} + +#if CFG_AUDIO_DEBUG +//--------------------------------------------------------------------+ +// HID interface for audio debug +//--------------------------------------------------------------------+ +// Every 1ms, we will sent 1 debug information report +void audio_debug_task(void) +{ + static uint32_t start_ms = 0; + uint32_t curr_ms = board_millis(); + if ( start_ms == curr_ms ) return; // not enough time + start_ms = curr_ms; + + audio_debug_info_t debug_info; + debug_info.sample_rate = current_sample_rate; + debug_info.alt_settings = current_alt_settings; + debug_info.fifo_size = CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ; + debug_info.fifo_count = fifo_count; + debug_info.fifo_count_avg = fifo_count_avg >> 16; + for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) + { + debug_info.mute[i] = mute[i]; + debug_info.volume[i] = volume[i]; + } + + if(tud_hid_ready()) + tud_hid_report(0, &debug_info, sizeof(debug_info)); +} + +// Invoked when received GET_REPORT control request +// Unused here +uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) +{ + // TODO not Implemented + (void) itf; + (void) report_id; + (void) report_type; + (void) buffer; + (void) reqlen; + + return 0; +} + +// Invoked when received SET_REPORT control request or +// Unused here +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) +{ + // This example doesn't use multiple report and report ID + (void) itf; + (void) report_id; + (void) report_type; + (void) buffer; + (void) bufsize; +} + +#endif diff --git a/examples/device/uac2_speaker_fb/src/quirk_os_guessing.c b/examples/device/uac2_speaker_fb/src/quirk_os_guessing.c new file mode 100644 index 000000000..965bbd6cf --- /dev/null +++ b/examples/device/uac2_speaker_fb/src/quirk_os_guessing.c @@ -0,0 +1,90 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 HiFiPhile + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "quirk_os_guessing.h" + +static tusb_desc_type_t desc_req_buf[2]; +static int desc_req_idx = 0; + +// Place at the start of tud_descriptor_device_cb() +void quirk_os_guessing_desc_device_cb() { + desc_req_idx = 0; +} + +// Place at the start of tud_descriptor_configuration_cb() +void quirk_os_guessing_desc_configuration_cb() { + // Skip redundant request + if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_CONFIGURATION)) { + desc_req_buf[desc_req_idx++] = TUSB_DESC_CONFIGURATION; + } +} + +// Place at the start of tud_descriptor_bos_cb() +void quirk_os_guessing_desc_bos_cb() { + // Skip redundant request + if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_BOS)) { + desc_req_buf[desc_req_idx++] = TUSB_DESC_BOS; + } +} + +// Place at the start of tud_descriptor_string_cb() +void quirk_os_guessing_desc_string_cb() { + // Skip redundant request + if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_STRING)) { + desc_req_buf[desc_req_idx++] = TUSB_DESC_STRING; + } +} + +// Each OS request descriptors differently: +// Windows 10 - 11 +// Device Desc +// Config Desc +// BOS Desc +// String Desc +// Linux 3.16 - 6.8 +// Device Desc +// BOS Desc +// Config Desc +// String Desc +// OS X Ventura - Sonoma +// Device Desc +// String Desc +// Config Desc || BOS Desc +// BOS Desc || Config Desc +quirk_os_guessing_t quirk_os_guessing_get(void) { + if (desc_req_idx < 2) { + return QUIRK_OS_GUESSING_UNKNOWN; + } + + if (desc_req_buf[0] == TUSB_DESC_BOS && desc_req_buf[1] == TUSB_DESC_CONFIGURATION) { + return QUIRK_OS_GUESSING_LINUX; + } else if (desc_req_buf[0] == TUSB_DESC_CONFIGURATION && desc_req_buf[1] == TUSB_DESC_BOS) { + return QUIRK_OS_GUESSING_WINDOWS; + } else if (desc_req_buf[0] == TUSB_DESC_STRING && (desc_req_buf[1] == TUSB_DESC_BOS || desc_req_buf[1] == TUSB_DESC_CONFIGURATION)) { + return QUIRK_OS_GUESSING_OSX; + } + + return QUIRK_OS_GUESSING_UNKNOWN; +} diff --git a/examples/device/uac2_speaker_fb/src/quirk_os_guessing.h b/examples/device/uac2_speaker_fb/src/quirk_os_guessing.h new file mode 100644 index 000000000..1120355c9 --- /dev/null +++ b/examples/device/uac2_speaker_fb/src/quirk_os_guessing.h @@ -0,0 +1,75 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 HiFiPhile + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _QUIRK_OS_GUESSING_H_ +#define _QUIRK_OS_GUESSING_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "tusb.h" + +//================================== !!! WARNING !!! ==================================== +// This quirk operate out of USB specification in order to workaround specific issues. +// It may not work on your platform. +//======================================================================================= +// +// Prerequisites: +// - Set USB version to at least 2.01 in Device Descriptor +// - Has a valid BOS Descriptor, refer to webusb_serial example +// +// Attention: +// Windows detection result comes out after Configuration Descriptor request, +// meaning it will be too late to do descriptor adjustment. It's advised to make +// Windows as default configuration and adjust to other OS accordingly. + +typedef enum { + QUIRK_OS_GUESSING_UNKNOWN, + QUIRK_OS_GUESSING_LINUX, + QUIRK_OS_GUESSING_OSX, + QUIRK_OS_GUESSING_WINDOWS, +} quirk_os_guessing_t; + +// Get Host OS type +quirk_os_guessing_t quirk_os_guessing_get(void); + +// Place at the start of tud_descriptor_device_cb() +void quirk_os_guessing_desc_device_cb(void); + +// Place at the start of tud_descriptor_configuration_cb() +void quirk_os_guessing_desc_configuration_cb(void); + +// Place at the start of tud_descriptor_bos_cb() +void quirk_os_guessing_desc_bos_cb(void); + +// Place at the start of tud_descriptor_string_cb() +void quirk_os_guessing_desc_string_cb(void); + +#ifdef __cplusplus + } +#endif + +#endif /* _QUIRK_OS_GUESSING_H_ */ diff --git a/examples/device/uac2_speaker_fb/src/tusb_config.h b/examples/device/uac2_speaker_fb/src/tusb_config.h new file mode 100644 index 000000000..fd4925c7f --- /dev/null +++ b/examples/device/uac2_speaker_fb/src/tusb_config.h @@ -0,0 +1,168 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Ha Thach (tinyusb.org) + * Copyright (c) 2020 Jerzy Kasenberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "usb_descriptors.h" + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// Common Configuration +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +// It's recommended to disable debug unless for control requests debugging, +// as the extra time needed will impact data stream ! +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +/* (Needed for Full-Speed only) + * Enable host OS guessing to workaround UAC2 compatibility issues between Windows and OS X + * The default configuration only support Windows and Linux, enable this option for OS X + * support. Otherwise if you don't need Windows support you can make OS X's configuration as + * default. + */ +#define CFG_QUIRK_OS_GUESSING 1 + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +// Expose audio class debug information via HID interface +#ifndef CFG_AUDIO_DEBUG +#define CFG_AUDIO_DEBUG 1 +#endif + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +#define CFG_TUD_HID_EP_BUFSIZE 64 + +//------------- CLASS -------------// +#define CFG_TUD_AUDIO 1 + +#if CFG_AUDIO_DEBUG +#define CFG_TUD_HID 1 +#else +#define CFG_TUD_HID 0 +#endif + +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +//-------------------------------------------------------------------- +// AUDIO CLASS DRIVER CONFIGURATION +//-------------------------------------------------------------------- + +#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN + +// Enable if Full-Speed on OSX, also set feedback EP size to 3 +#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0 + +// Audio format type I specifications +#if defined(__RX__) +#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 48000 +#else +#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 96000 +#endif + +#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2 + +// 16bit in 16bit slots +#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX 2 +#define CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX 16 + +// 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_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX) +#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device + +// Enable feedback EP +#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 1 + +// 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 + +// Size of control request buffer +#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/examples/device/uac2_speaker_fb/src/usb_descriptors.c b/examples/device/uac2_speaker_fb/src/usb_descriptors.c new file mode 100644 index 000000000..31c5e116d --- /dev/null +++ b/examples/device/uac2_speaker_fb/src/usb_descriptors.c @@ -0,0 +1,295 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023 HiFiPhile + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "bsp/board_api.h" +#include "tusb.h" +#include "usb_descriptors.h" +#include "common_types.h" + +#ifdef CFG_QUIRK_OS_GUESSING +#include "quirk_os_guessing.h" +#endif + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) +#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) ) + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0201, + + // Use Interface Association Descriptor (IAD) for Audio + // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0xCafe, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ +#if CFG_QUIRK_OS_GUESSING + quirk_os_guessing_desc_device_cb(); +#endif + return (uint8_t const *)&desc_device; +} + +#if CFG_AUDIO_DEBUG +//--------------------------------------------------------------------+ +// HID Report Descriptor +//--------------------------------------------------------------------+ + +uint8_t const desc_hid_report[] = +{ + HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\ + HID_USAGE ( 0x01 ),\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\ + HID_USAGE ( 0x02 ),\ + HID_LOGICAL_MIN ( 0x00 ),\ + HID_LOGICAL_MAX_N ( 0xff, 2 ),\ + HID_REPORT_SIZE ( 8 ),\ + HID_REPORT_COUNT( sizeof(audio_debug_info_t) ),\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ + HID_COLLECTION_END +}; + +// Invoked when received GET HID REPORT DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) +{ + (void) itf; + return desc_hid_report; +} +#endif + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +#if CFG_AUDIO_DEBUG + #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN + TUD_HID_DESC_LEN) +#else + #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN) +#endif + +#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX + // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number + // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... + #define EPNUM_AUDIO_FB 0x03 + #define EPNUM_AUDIO_OUT 0x03 + #define EPNUM_DEBUG 0x04 + +#elif CFG_TUSB_MCU == OPT_MCU_NRF5X + // ISO endpoints for NRF5x are fixed to 0x08 (0x88) + #define EPNUM_AUDIO_FB 0x08 + #define EPNUM_AUDIO_OUT 0x08 + #define EPNUM_DEBUG 0x01 + +#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X + // SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_AUDIO_FB 0x01 + #define EPNUM_AUDIO_OUT 0x02 + #define EPNUM_DEBUG 0x03 + +#elif CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X + // FT9XX doesn't support a same endpoint number with different direction IN and OUT + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_AUDIO_FB 0x01 + #define EPNUM_AUDIO_OUT 0x02 + #define EPNUM_DEBUG 0x03 + +#else + #define EPNUM_AUDIO_FB 0x01 + #define EPNUM_AUDIO_OUT 0x01 + #define EPNUM_DEBUG 0x02 +#endif + +uint8_t const desc_configuration_default[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, byte per sample, bit per sample, EP Out, EP size, EP feedback, feedback EP size, + TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR(0, 4, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX, EPNUM_AUDIO_OUT, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX, EPNUM_AUDIO_FB | 0x80, 4), + +#if CFG_AUDIO_DEBUG + // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval + TUD_HID_DESCRIPTOR(ITF_NUM_DEBUG, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_DEBUG | 0x80, CFG_TUD_HID_EP_BUFSIZE, 7) +#endif +}; + +#if CFG_QUIRK_OS_GUESSING +// OS X needs 3 bytes feedback endpoint on FS +uint8_t const desc_configuration_osx_fs[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, byte per sample, bit per sample, EP Out, EP size, EP feedback, feedback EP size, + TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR(0, 4, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX, EPNUM_AUDIO_OUT, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX, EPNUM_AUDIO_FB | 0x80, 3), + +#if CFG_AUDIO_DEBUG + // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval + TUD_HID_DESCRIPTOR(ITF_NUM_DEBUG, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_DEBUG | 0x80, CFG_TUD_HID_EP_BUFSIZE, 7) +#endif +}; +#endif + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void)index; // for multiple configurations + +#if CFG_QUIRK_OS_GUESSING + quirk_os_guessing_desc_configuration_cb(); + if(tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) { + return desc_configuration_osx_fs; + } +#endif + return desc_configuration_default; +} + +//--------------------------------------------------------------------+ +// BOS Descriptor, required for OS guessing quirk +//--------------------------------------------------------------------+ + +#define TUD_BOS_USB20_EXT_DESC_LEN 7 + +#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_USB20_EXT_DESC_LEN) + +// BOS Descriptor is required for webUSB +uint8_t const desc_bos[] = +{ + // total length, number of device caps + TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1), + + // USB 2.0 Extension Descriptor + 0x07, TUSB_DESC_DEVICE_CAPABILITY, DEVICE_CAPABILITY_USB20_EXTENSION, 0x00, 0x00, 0x00,0x00 +}; + +uint8_t const * tud_descriptor_bos_cb(void) +{ +#if CFG_QUIRK_OS_GUESSING + quirk_os_guessing_desc_bos_cb(); +#endif + return desc_bos; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// String Descriptor Index +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, +}; + +// array of pointer to string descriptors +char const *string_desc_arr[] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Speaker", // 2: Product + NULL, // 3: Serials will use unique ID if possible + "UAC2 Speaker", // 4: Audio Interface +}; + +static uint16_t _desc_str[32 + 1]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void) langid; + size_t chr_count; + +#if CFG_QUIRK_OS_GUESSING + quirk_os_guessing_desc_string_cb(); +#endif + + switch ( index ) { + case STRID_LANGID: + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + break; + + case STRID_SERIAL: + chr_count = board_usb_get_serial(_desc_str + 1, 32); + break; + + default: + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL; + + const char *str = string_desc_arr[index]; + + // Cap at max char + chr_count = strlen(str); + size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type + if ( chr_count > max_count ) chr_count = max_count; + + // Convert ASCII string into UTF-16 + for ( size_t i = 0; i < chr_count; i++ ) { + _desc_str[1 + i] = str[i]; + } + break; + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/examples/device/uac2_speaker_fb/src/usb_descriptors.h b/examples/device/uac2_speaker_fb/src/usb_descriptors.h new file mode 100644 index 000000000..9511bf797 --- /dev/null +++ b/examples/device/uac2_speaker_fb/src/usb_descriptors.h @@ -0,0 +1,82 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023 HiFiPhile + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _USB_DESCRIPTORS_H_ +#define _USB_DESCRIPTORS_H_ + +// Defined in TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR +#define UAC2_ENTITY_CLOCK 0x04 +#define UAC2_ENTITY_INPUT_TERMINAL 0x01 +#define UAC2_ENTITY_FEATURE_UNIT 0x02 +#define UAC2_ENTITY_OUTPUT_TERMINAL 0x03 + +#define TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\ + + TUD_AUDIO_DESC_STD_AC_LEN\ + + TUD_AUDIO_DESC_CS_AC_LEN\ + + TUD_AUDIO_DESC_CLK_SRC_LEN\ + + TUD_AUDIO_DESC_INPUT_TERM_LEN\ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ + + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + 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\ + + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN) + +#define TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epoutsize, _epfb, _epfbsize) \ + /* Standard Interface Association Descriptor (IAD) */\ + TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\ + /* Standard AC Interface Descriptor(4.7.1) */\ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */\ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\ + /* Clock Source Descriptor(4.7.2.1) */\ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK, /*_ctrl*/ (AUDIO_CTRL_RW << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\ + /* Input Terminal Descriptor(4.7.2.4) */\ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x02, /*_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) */\ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ + /* Feature Unit Descriptor(4.7.2.8) */\ + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_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),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 1 - alternate interface for data streaming */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x02, /*_stridx*/ 0x00),\ + /* Class-Specific AS Interface Descriptor(4.9.2) */\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_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),\ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epoutsize, /*_interval*/ 0x01),\ + /* 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 Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_epsize*/ _epfbsize, /*_interval*/ TUD_OPT_HIGH_SPEED ? 4 : 1)\ + +#endif diff --git a/examples/device/video_capture/skip.txt b/examples/device/video_capture/skip.txt index 714cabeec..cb0c7d2e6 100644 --- a/examples/device/video_capture/skip.txt +++ b/examples/device/video_capture/skip.txt @@ -1,3 +1,4 @@ +mcu:CH32V103 mcu:CH32V20X mcu:MSP430x5xx mcu:NUC121 diff --git a/examples/device/video_capture_2ch/skip.txt b/examples/device/video_capture_2ch/skip.txt index 1786297f9..af3b0de04 100644 --- a/examples/device/video_capture_2ch/skip.txt +++ b/examples/device/video_capture_2ch/skip.txt @@ -2,6 +2,7 @@ mcu:MSP430x5xx mcu:NUC121 mcu:SAMD11 mcu:GD32VF103 +mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 mcu:STM32L0 diff --git a/examples/dual/CMakeLists.txt b/examples/dual/CMakeLists.txt index 15081cf26..f11074e93 100644 --- a/examples/dual/CMakeLists.txt +++ b/examples/dual/CMakeLists.txt @@ -10,4 +10,5 @@ if (FAMILY STREQUAL "rp2040" AND NOT TARGET tinyusb_pico_pio_usb) else () # family_add_subdirectory will filter what to actually add based on selected FAMILY family_add_subdirectory(host_hid_to_device_cdc) + family_add_subdirectory(host_info_to_device_cdc) endif () diff --git a/examples/dual/host_hid_to_device_cdc/src/main.c b/examples/dual/host_hid_to_device_cdc/src/main.c index 96a2beff5..0b4165e38 100644 --- a/examples/dual/host_hid_to_device_cdc/src/main.c +++ b/examples/dual/host_hid_to_device_cdc/src/main.c @@ -55,14 +55,14 @@ const uint8_t colemak[128] = { }; #endif -static uint8_t const keycode2ascii[128][2] = { HID_KEYCODE_TO_ASCII }; +static uint8_t const keycode2ascii[128][2] = {HID_KEYCODE_TO_ASCII}; /* Blink pattern * - 250 ms : device not mounted * - 1000 ms : device mounted * - 2500 ms : device is suspended */ -enum { +enum { BLINK_NOT_MOUNTED = 250, BLINK_MOUNTED = 1000, BLINK_SUSPENDED = 2500, @@ -73,8 +73,7 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; void led_blinking_task(void); /*------------- MAIN -------------*/ -int main(void) -{ +int main(void) { board_init(); printf("TinyUSB Host HID <-> Device CDC Example\r\n"); @@ -87,8 +86,7 @@ int main(void) board_init_after_tusb(); } - while (1) - { + while (1) { tud_task(); // tinyusb device task tuh_task(); // tinyusb host task led_blinking_task(); @@ -102,35 +100,30 @@ int main(void) //--------------------------------------------------------------------+ // Invoked when device is mounted -void tud_mount_cb(void) -{ +void tud_mount_cb(void) { blink_interval_ms = BLINK_MOUNTED; } // Invoked when device is unmounted -void tud_umount_cb(void) -{ +void tud_umount_cb(void) { blink_interval_ms = BLINK_NOT_MOUNTED; } // Invoked when usb bus is suspended // remote_wakeup_en : if host allow us to perform remote wakeup // Within 7ms, device must draw an average of current less than 2.5 mA from bus -void tud_suspend_cb(bool remote_wakeup_en) -{ +void tud_suspend_cb(bool remote_wakeup_en) { (void) remote_wakeup_en; blink_interval_ms = BLINK_SUSPENDED; } // Invoked when usb bus is resumed -void tud_resume_cb(void) -{ +void tud_resume_cb(void) { blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; } // Invoked when CDC interface received data from host -void tud_cdc_rx_cb(uint8_t itf) -{ +void tud_cdc_rx_cb(uint8_t itf) { (void) itf; char buf[64]; @@ -149,38 +142,36 @@ void tud_cdc_rx_cb(uint8_t itf) // can be used to parse common/simple enough descriptor. // Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped // therefore report_desc = NULL, desc_len = 0 -void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) -{ - (void)desc_report; - (void)desc_len; +void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) { + (void) desc_report; + (void) desc_len; // Interface protocol (hid_interface_protocol_enum_t) - const char* protocol_str[] = { "None", "Keyboard", "Mouse" }; + const char* protocol_str[] = {"None", "Keyboard", "Mouse"}; uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); uint16_t vid, pid; tuh_vid_pid_get(dev_addr, &vid, &pid); char tempbuf[256]; - int count = sprintf(tempbuf, "[%04x:%04x][%u] HID Interface%u, Protocol = %s\r\n", vid, pid, dev_addr, instance, protocol_str[itf_protocol]); + int count = sprintf( + tempbuf, "[%04x:%04x][%u] HID Interface%u, Protocol = %s\r\n", vid, pid, dev_addr, instance, + protocol_str[itf_protocol]); tud_cdc_write(tempbuf, (uint32_t) count); tud_cdc_write_flush(); // Receive report from boot keyboard & mouse only // tuh_hid_report_received_cb() will be invoked when report is available - if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD || itf_protocol == HID_ITF_PROTOCOL_MOUSE) - { - if ( !tuh_hid_receive_report(dev_addr, instance) ) - { + if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD || itf_protocol == HID_ITF_PROTOCOL_MOUSE) { + if (!tuh_hid_receive_report(dev_addr, instance)) { tud_cdc_write_str("Error: cannot request report\r\n"); } } } // Invoked when device with hid interface is un-mounted -void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) -{ +void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) { char tempbuf[256]; int count = sprintf(tempbuf, "[%u] HID Interface%u is unmounted\r\n", dev_addr, instance); tud_cdc_write(tempbuf, (uint32_t) count); @@ -188,11 +179,9 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) } // look up new key in previous keys -static inline bool find_key_in_report(hid_keyboard_report_t const *report, uint8_t keycode) -{ - for(uint8_t i=0; i<6; i++) - { - if (report->keycode[i] == keycode) return true; +static inline bool find_key_in_report(hid_keyboard_report_t const* report, uint8_t keycode) { + for (uint8_t i = 0; i < 6; i++) { + if (report->keycode[i] == keycode) return true; } return false; @@ -200,22 +189,17 @@ static inline bool find_key_in_report(hid_keyboard_report_t const *report, uint8 // convert hid keycode to ascii and print via usb device CDC (ignore non-printable) -static void process_kbd_report(uint8_t dev_addr, hid_keyboard_report_t const *report) -{ +static void process_kbd_report(uint8_t dev_addr, hid_keyboard_report_t const* report) { (void) dev_addr; - static hid_keyboard_report_t prev_report = { 0, 0, {0} }; // previous report to check key released + static hid_keyboard_report_t prev_report = {0, 0, {0}}; // previous report to check key released bool flush = false; - for(uint8_t i=0; i<6; i++) - { + for (uint8_t i = 0; i < 6; i++) { uint8_t keycode = report->keycode[i]; - if ( keycode ) - { - if ( find_key_in_report(&prev_report, keycode) ) - { + if (keycode) { + if (find_key_in_report(&prev_report, keycode)) { // exist in previous report means the current key is holding - }else - { + } else { // not existed in previous report means the current key is pressed // remap the key code for Colemak layout @@ -227,8 +211,7 @@ static void process_kbd_report(uint8_t dev_addr, hid_keyboard_report_t const *re bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT); uint8_t ch = keycode2ascii[keycode][is_shift ? 1 : 0]; - if (ch) - { + if (ch) { if (ch == '\n') tud_cdc_write("\r", 1); tud_cdc_write(&ch, 1); flush = true; @@ -244,13 +227,12 @@ static void process_kbd_report(uint8_t dev_addr, hid_keyboard_report_t const *re } // send mouse report to usb device CDC -static void process_mouse_report(uint8_t dev_addr, hid_mouse_report_t const * report) -{ +static void process_mouse_report(uint8_t dev_addr, hid_mouse_report_t const* report) { //------------- button state -------------// //uint8_t button_changed_mask = report->buttons ^ prev_report.buttons; - char l = report->buttons & MOUSE_BUTTON_LEFT ? 'L' : '-'; + char l = report->buttons & MOUSE_BUTTON_LEFT ? 'L' : '-'; char m = report->buttons & MOUSE_BUTTON_MIDDLE ? 'M' : '-'; - char r = report->buttons & MOUSE_BUTTON_RIGHT ? 'R' : '-'; + char r = report->buttons & MOUSE_BUTTON_RIGHT ? 'R' : '-'; char tempbuf[32]; int count = sprintf(tempbuf, "[%u] %c%c%c %d %d %d\r\n", dev_addr, l, m, r, report->x, report->y, report->wheel); @@ -260,27 +242,25 @@ static void process_mouse_report(uint8_t dev_addr, hid_mouse_report_t const * re } // 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 tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) { (void) len; uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); - switch(itf_protocol) - { + switch (itf_protocol) { case HID_ITF_PROTOCOL_KEYBOARD: - process_kbd_report(dev_addr, (hid_keyboard_report_t const*) report ); - break; + process_kbd_report(dev_addr, (hid_keyboard_report_t const*) report); + break; case HID_ITF_PROTOCOL_MOUSE: - process_mouse_report(dev_addr, (hid_mouse_report_t const*) report ); - break; + process_mouse_report(dev_addr, (hid_mouse_report_t const*) report); + break; - default: break; + default: + break; } // continue to request to receive report - if ( !tuh_hid_receive_report(dev_addr, instance) ) - { + if (!tuh_hid_receive_report(dev_addr, instance)) { tud_cdc_write_str("Error: cannot request report\r\n"); } } @@ -288,13 +268,12 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons //--------------------------------------------------------------------+ // Blinking Task //--------------------------------------------------------------------+ -void led_blinking_task(void) -{ +void led_blinking_task(void) { static uint32_t start_ms = 0; static bool led_state = false; // Blink every interval ms - if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time + if (board_millis() - start_ms < blink_interval_ms) return; // not enough time start_ms += blink_interval_ms; board_led_write(led_state); diff --git a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h index 8133ed418..2843e0b83 100644 --- a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h +++ b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h @@ -23,8 +23,8 @@ * */ -#ifndef _TUSB_CONFIG_H_ -#define _TUSB_CONFIG_H_ +#ifndef TUSB_CONFIG_H_ +#define TUSB_CONFIG_H_ #ifdef __cplusplus extern "C" { @@ -144,4 +144,4 @@ } #endif -#endif /* _TUSB_CONFIG_H_ */ +#endif diff --git a/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c b/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c index 293620042..9d57737fb 100644 --- a/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c +++ b/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c @@ -42,44 +42,41 @@ //--------------------------------------------------------------------+ // Device Descriptors //--------------------------------------------------------------------+ -tusb_desc_device_t const desc_device = -{ - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = USB_BCD, +tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, - // Use Interface Association Descriptor (IAD) for CDC - // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) - .bDeviceClass = TUSB_CLASS_MISC, - .bDeviceSubClass = MISC_SUBCLASS_COMMON, - .bDeviceProtocol = MISC_PROTOCOL_IAD, + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .idVendor = USB_VID, - .idProduct = USB_PID, - .bcdDevice = 0x0100, + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, - .bNumConfigurations = 0x01 + .bNumConfigurations = 0x01 }; // Invoked when received GET DEVICE DESCRIPTOR // Application return pointer to descriptor -uint8_t const * tud_descriptor_device_cb(void) -{ - return (uint8_t const *) &desc_device; +uint8_t const* tud_descriptor_device_cb(void) { + return (uint8_t const*) &desc_device; } //--------------------------------------------------------------------+ // Configuration Descriptor //--------------------------------------------------------------------+ -enum -{ +enum { ITF_NUM_CDC = 0, ITF_NUM_CDC_DATA, ITF_NUM_TOTAL @@ -92,7 +89,7 @@ enum #define EPNUM_CDC_OUT 0x02 #define EPNUM_CDC_IN 0x82 -#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X +#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X // SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_CDC_NOTIF 0x81 @@ -109,7 +106,7 @@ enum #define EPNUM_CDC_IN 0x81 #elif CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X - // FT9XX doesn't support a same endpoint number with different direction IN and OUT +// FT9XX doesn't support a same endpoint number with different direction IN and OUT // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_CDC_NOTIF 0x81 #define EPNUM_CDC_OUT 0x02 @@ -125,21 +122,19 @@ enum #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) // full speed configuration -uint8_t const desc_fs_configuration[] = -{ - // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), +uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), - // Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), }; #if TUD_OPT_HIGH_SPEED // Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration // high speed configuration -uint8_t const desc_hs_configuration[] = -{ +uint8_t const desc_hs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), @@ -151,8 +146,7 @@ uint8_t const desc_hs_configuration[] = uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; // device qualifier is mostly similar to device descriptor since we don't change configuration based on speed -tusb_desc_device_qualifier_t const desc_device_qualifier = -{ +tusb_desc_device_qualifier_t const desc_device_qualifier = { .bLength = sizeof(tusb_desc_device_qualifier_t), .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, .bcdUSB = USB_BCD, @@ -170,16 +164,14 @@ tusb_desc_device_qualifier_t const desc_device_qualifier = // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. // device_qualifier descriptor describes information about a high-speed capable device that would // change if the device were operating at the other speed. If not highspeed capable stall this request. -uint8_t const* tud_descriptor_device_qualifier_cb(void) -{ +uint8_t const* tud_descriptor_device_qualifier_cb(void) { return (uint8_t const*) &desc_device_qualifier; } // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa -uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) -{ +uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) { (void) index; // for multiple configurations // if link speed is high return fullspeed config, and vice versa @@ -199,8 +191,7 @@ uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) // Invoked when received GET CONFIGURATION DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete -uint8_t const * tud_descriptor_configuration_cb(uint8_t index) -{ +uint8_t const* tud_descriptor_configuration_cb(uint8_t index) { (void) index; // for multiple configurations #if TUD_OPT_HIGH_SPEED @@ -224,24 +215,23 @@ enum { }; // array of pointer to string descriptors -char const *string_desc_arr[] = -{ - (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) - "TinyUSB", // 1: Manufacturer - "TinyUSB Device", // 2: Product - NULL, // 3: Serials will use unique ID if possible - "TinyUSB CDC", // 4: CDC Interface +char const* string_desc_arr[] = { + (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + NULL, // 3: Serials will use unique ID if possible + "TinyUSB CDC", // 4: CDC Interface }; static uint16_t _desc_str[32 + 1]; // Invoked when received GET STRING DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete -uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { (void) langid; size_t chr_count; - switch ( index ) { + switch (index) { case STRID_LANGID: memcpy(&_desc_str[1], string_desc_arr[0], 2); chr_count = 1; @@ -255,17 +245,17 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors - if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL; + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL; - const char *str = string_desc_arr[index]; + const char* str = string_desc_arr[index]; // Cap at max char chr_count = strlen(str); size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type - if ( chr_count > max_count ) chr_count = max_count; + if (chr_count > max_count) chr_count = max_count; // Convert ASCII string into UTF-16 - for ( size_t i = 0; i < chr_count; i++ ) { + for (size_t i = 0; i < chr_count; i++) { _desc_str[1 + i] = str[i]; } break; diff --git a/examples/dual/host_info_to_device_cdc/CMakeLists.txt b/examples/dual/host_info_to_device_cdc/CMakeLists.txt new file mode 100644 index 000000000..a6557c2d0 --- /dev/null +++ b/examples/dual/host_info_to_device_cdc/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.17) + +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} C CXX ASM) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example without RTOS. +# See the corresponding function in hw/bsp/FAMILY/family.cmake for details. +family_configure_dual_usb_example(${PROJECT} noos) + +# due to warnings from Pico-PIO-USB +target_compile_options(${PROJECT} PUBLIC + -Wno-error=shadow + -Wno-error=cast-align + -Wno-error=cast-qual + -Wno-error=redundant-decls + -Wno-error=sign-conversion + -Wno-error=conversion + -Wno-error=sign-compare + -Wno-error=unused-function + ) diff --git a/examples/dual/host_info_to_device_cdc/Makefile b/examples/dual/host_info_to_device_cdc/Makefile new file mode 100644 index 000000000..0ede79c17 --- /dev/null +++ b/examples/dual/host_info_to_device_cdc/Makefile @@ -0,0 +1,17 @@ +include ../../build_system/make/make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +EXAMPLE_SOURCE += $(wildcard src/*.c) +SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) + +CFLAGS += -Wno-error=cast-align -Wno-error=null-dereference + +SRC_C += \ + src/host/hub.c \ + src/host/usbh.c + +include ../../build_system/make/rules.mk diff --git a/examples/dual/host_info_to_device_cdc/only.txt b/examples/dual/host_info_to_device_cdc/only.txt new file mode 100644 index 000000000..cfc87eb4e --- /dev/null +++ b/examples/dual/host_info_to_device_cdc/only.txt @@ -0,0 +1,6 @@ +board:mimxrt1060_evk +board:mimxrt1064_evk +board:mcb1800 +mcu:RP2040 +mcu:ra6m5 +mcu:MAX3421 diff --git a/examples/dual/host_info_to_device_cdc/src/main.c b/examples/dual/host_info_to_device_cdc/src/main.c new file mode 100644 index 000000000..5be531098 --- /dev/null +++ b/examples/dual/host_info_to_device_cdc/src/main.c @@ -0,0 +1,285 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 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. + * + */ + +/* Host example will get device descriptors of attached devices and print it out via device cdc as follows: + * Device 1: ID 046d:c52f + Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 0200 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x046d + idProduct 0xc52f + bcdDevice 2200 + iManufacturer 1 Logitech + iProduct 2 USB Receiver + iSerialNumber 0 + bNumConfigurations 1 + * + */ + +#include +#include +#include + +#include "bsp/board_api.h" +#include "tusb.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ +// Language ID: English +#define LANGUAGE_ID 0x0409 + +/* Blink pattern + * - 250 ms : device not mounted + * - 1000 ms : device mounted + * - 2500 ms : device is suspended + */ +enum { + BLINK_NOT_MOUNTED = 250, + BLINK_MOUNTED = 1000, + BLINK_SUSPENDED = 2500, +}; + +static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; + +static bool is_print[CFG_TUH_DEVICE_MAX+1] = { 0 }; + +static void print_utf16(uint16_t *temp_buf, size_t buf_len); +void led_blinking_task(void); +void cdc_task(void); + +/*------------- MAIN -------------*/ +int main(void) { + board_init(); + + printf("TinyUSB Host Information -> Device CDC Example\r\n"); + + // init device and host stack on configured roothub port + tud_init(BOARD_TUD_RHPORT); + tuh_init(BOARD_TUH_RHPORT); + + if (board_init_after_tusb) { + board_init_after_tusb(); + } + + while (1) { + tud_task(); // tinyusb device task + tuh_task(); // tinyusb host task + cdc_task(); + led_blinking_task(); + } + + return 0; +} + +//--------------------------------------------------------------------+ +// Device CDC +//--------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) { + blink_interval_ms = BLINK_MOUNTED; +} + +// Invoked when device is unmounted +void tud_umount_cb(void) { + blink_interval_ms = BLINK_NOT_MOUNTED; +} + +// Invoked when usb bus is suspended +// remote_wakeup_en : if host allow us to perform remote wakeup +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +void tud_suspend_cb(bool remote_wakeup_en) { + (void) remote_wakeup_en; + blink_interval_ms = BLINK_SUSPENDED; +} + +// Invoked when usb bus is resumed +void tud_resume_cb(void) { + blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; +} + +#if 1 +#define cdc_printf(...) \ + do { \ + char _tempbuf[256]; \ + int count = sprintf(_tempbuf, __VA_ARGS__); \ + tud_cdc_write(_tempbuf, (uint32_t) count); \ + tud_cdc_write_flush(); \ + tud_task(); \ + } while(0) +#endif + +//#define cdc_printf printf + +void print_device_info(uint8_t daddr) { + tusb_desc_device_t desc_device; + uint8_t xfer_result = tuh_descriptor_get_device_sync(daddr, &desc_device, 18); + if (XFER_RESULT_SUCCESS != xfer_result) { + tud_cdc_write_str("Failed to get device descriptor\r\n"); + return; + } + + cdc_printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct); + cdc_printf("Device Descriptor:\r\n"); + cdc_printf(" bLength %u\r\n" , desc_device.bLength); + cdc_printf(" bDescriptorType %u\r\n" , desc_device.bDescriptorType); + cdc_printf(" bcdUSB %04x\r\n" , desc_device.bcdUSB); + cdc_printf(" bDeviceClass %u\r\n" , desc_device.bDeviceClass); + cdc_printf(" bDeviceSubClass %u\r\n" , desc_device.bDeviceSubClass); + cdc_printf(" bDeviceProtocol %u\r\n" , desc_device.bDeviceProtocol); + cdc_printf(" bMaxPacketSize0 %u\r\n" , desc_device.bMaxPacketSize0); + cdc_printf(" idVendor 0x%04x\r\n" , desc_device.idVendor); + cdc_printf(" idProduct 0x%04x\r\n" , desc_device.idProduct); + cdc_printf(" bcdDevice %04x\r\n" , desc_device.bcdDevice); + + // Get String descriptor using Sync API + uint16_t buf[128]; + + cdc_printf(" iManufacturer %u " , desc_device.iManufacturer); + xfer_result = tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf)); + if (XFER_RESULT_SUCCESS == xfer_result ) { + print_utf16(buf, TU_ARRAY_SIZE(buf)); + } + tud_cdc_write_str("\r\n"); + + cdc_printf(" iProduct %u " , desc_device.iProduct); + xfer_result = tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf)); + if (XFER_RESULT_SUCCESS == xfer_result) { + print_utf16(buf, TU_ARRAY_SIZE(buf)); + } + tud_cdc_write_str("\r\n"); + + cdc_printf(" iSerialNumber %u " , desc_device.iSerialNumber); + xfer_result = tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf)); + if (XFER_RESULT_SUCCESS == xfer_result) { + print_utf16(buf, TU_ARRAY_SIZE(buf)); + } + tud_cdc_write_str("\r\n"); + + cdc_printf(" bNumConfigurations %u\r\n" , desc_device.bNumConfigurations); +} + +void cdc_task(void) { + if (tud_cdc_connected()) { + for (uint8_t daddr = 1; daddr <= CFG_TUH_DEVICE_MAX; daddr++) { + if (tuh_mounted(daddr)) { + if (is_print[daddr]) { + is_print[daddr] = false; + print_device_info(daddr); + tud_cdc_write_flush(); + } + } + } + } +} + +//--------------------------------------------------------------------+ +// Host Get device information +//--------------------------------------------------------------------+ +void tuh_mount_cb(uint8_t daddr) { + printf("mounted device %u\r\n", daddr); + is_print[daddr] = true; +} + +void tuh_umount_cb(uint8_t daddr) { + printf("unmounted device %u\r\n", daddr); + is_print[daddr] = false; +} + +//--------------------------------------------------------------------+ +// Blinking Task +//--------------------------------------------------------------------+ +void led_blinking_task(void) { + static uint32_t start_ms = 0; + static bool led_state = false; + + // Blink every interval ms + if (board_millis() - start_ms < blink_interval_ms) return; // not enough time + start_ms += blink_interval_ms; + + board_led_write(led_state); + led_state = 1 - led_state; // toggle +} + +//--------------------------------------------------------------------+ +// String Descriptor Helper +//--------------------------------------------------------------------+ + +static void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) { + // TODO: Check for runover. + (void)utf8_len; + // Get the UTF-16 length out of the data itself. + + for (size_t i = 0; i < utf16_len; i++) { + uint16_t chr = utf16[i]; + if (chr < 0x80) { + *utf8++ = chr & 0xffu; + } else if (chr < 0x800) { + *utf8++ = (uint8_t)(0xC0 | (chr >> 6 & 0x1F)); + *utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F)); + } else { + // TODO: Verify surrogate. + *utf8++ = (uint8_t)(0xE0 | (chr >> 12 & 0x0F)); + *utf8++ = (uint8_t)(0x80 | (chr >> 6 & 0x3F)); + *utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F)); + } + // TODO: Handle UTF-16 code points that take two entries. + } +} + +// Count how many bytes a utf-16-le encoded string will take in utf-8. +static int _count_utf8_bytes(const uint16_t *buf, size_t len) { + size_t total_bytes = 0; + for (size_t i = 0; i < len; i++) { + uint16_t chr = buf[i]; + if (chr < 0x80) { + total_bytes += 1; + } else if (chr < 0x800) { + total_bytes += 2; + } else { + total_bytes += 3; + } + // TODO: Handle UTF-16 code points that take two entries. + } + return (int) total_bytes; +} + +static void print_utf16(uint16_t *temp_buf, size_t buf_len) { + if ((temp_buf[0] & 0xff) == 0) return; // empty + size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t); + size_t utf8_len = (size_t) _count_utf8_bytes(temp_buf + 1, utf16_len); + _convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, sizeof(uint16_t) * buf_len); + ((uint8_t*) temp_buf)[utf8_len] = '\0'; + + tud_cdc_write(temp_buf, utf8_len); + tud_cdc_write_flush(); + tud_task(); +} diff --git a/examples/dual/host_info_to_device_cdc/src/tusb_config.h b/examples/dual/host_info_to_device_cdc/src/tusb_config.h new file mode 100644 index 000000000..bb47fbf4a --- /dev/null +++ b/examples/dual/host_info_to_device_cdc/src/tusb_config.h @@ -0,0 +1,143 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 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. + * + */ + +#ifndef TUSB_CONFIG_H_ +#define TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +// RHPort number used for host can be defined by board.mk, default to port 1 +#ifndef BOARD_TUH_RHPORT +#define BOARD_TUH_RHPORT 1 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUH_MAX_SPEED +#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack, Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_ENABLED 1 +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +// Enable Host stack, Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUH_ENABLED 1 +#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED + +#if CFG_TUSB_MCU == OPT_MCU_RP2040 +// Use pico-pio-usb as host controller for raspberry rp2040 +#define CFG_TUH_RPI_PIO_USB 1 +#endif + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUD_MEM_SECTION +#define CFG_TUD_MEM_SECTION +#endif + +#ifndef CFG_TUD_MEM_ALIGN +#define CFG_TUD_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_CDC 1 + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +//-------------------------------------------------------------------- +// HOST CONFIGURATION +//-------------------------------------------------------------------- + +// Size of buffer to hold descriptors and other data used for enumeration +#define CFG_TUH_ENUMERATION_BUFSIZE 256 + +#ifndef CFG_TUH_MEM_SECTION +#define CFG_TUH_MEM_SECTION +#endif + +#ifndef CFG_TUH_MEM_ALIGN +#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +#define CFG_TUH_HUB 1 +// max device support (excluding hub device) +#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/examples/dual/host_info_to_device_cdc/src/usb_descriptors.c b/examples/dual/host_info_to_device_cdc/src/usb_descriptors.c new file mode 100644 index 000000000..9d57737fb --- /dev/null +++ b/examples/dual/host_info_to_device_cdc/src/usb_descriptors.c @@ -0,0 +1,268 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 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. + * + */ + +#include "bsp/board_api.h" +#include "tusb.h" + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) +#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) + +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const* tud_descriptor_device_cb(void) { + return (uint8_t const*) &desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum { + ITF_NUM_CDC = 0, + ITF_NUM_CDC_DATA, + ITF_NUM_TOTAL +}; + +#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX + // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number + // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ... + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x82 + +#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X + // SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x83 + +#elif CFG_TUSB_MCU == OPT_MCU_CXD56 + // CXD56 doesn't support a same endpoint number with different direction IN and OUT + // e.g EP1 OUT & EP1 IN cannot exist together + // CXD56 USB driver has fixed endpoint type (bulk/interrupt/iso) and direction (IN/OUT) by its number + // 0 control (IN/OUT), 1 Bulk (IN), 2 Bulk (OUT), 3 In (IN), 4 Bulk (IN), 5 Bulk (OUT), 6 In (IN) + #define EPNUM_CDC_NOTIF 0x83 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x81 + +#elif CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X +// FT9XX doesn't support a same endpoint number with different direction IN and OUT + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x83 + +#else + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x82 + +#endif + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) + +// full speed configuration +uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), +}; + +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration + +// high speed configuration +uint8_t const desc_hs_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512), +}; + +// other speed configuration +uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00 +}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. +// device_qualifier descriptor describes information about a high-speed capable device that would +// change if the device were operating at the other speed. If not highspeed capable stall this request. +uint8_t const* tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const*) &desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa +uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void) index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration, + CONFIG_TOTAL_LEN); + + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; +} + +#endif // highspeed + + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const* tud_descriptor_configuration_cb(uint8_t index) { + (void) index; // for multiple configurations + +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// String Descriptor Index +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, +}; + +// array of pointer to string descriptors +char const* string_desc_arr[] = { + (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + NULL, // 3: Serials will use unique ID if possible + "TinyUSB CDC", // 4: CDC Interface +}; + +static uint16_t _desc_str[32 + 1]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void) langid; + size_t chr_count; + + switch (index) { + case STRID_LANGID: + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + break; + + case STRID_SERIAL: + chr_count = board_usb_get_serial(_desc_str + 1, 32); + break; + + default: + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL; + + const char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = strlen(str); + size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type + if (chr_count > max_count) chr_count = max_count; + + // Convert ASCII string into UTF-16 + for (size_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + break; + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/examples/host/CMakeLists.txt b/examples/host/CMakeLists.txt index e6a2ece14..793f6ab08 100644 --- a/examples/host/CMakeLists.txt +++ b/examples/host/CMakeLists.txt @@ -9,5 +9,6 @@ family_initialize_project(tinyusb_host_examples ${CMAKE_CURRENT_LIST_DIR}) family_add_subdirectory(bare_api) family_add_subdirectory(cdc_msc_hid) family_add_subdirectory(cdc_msc_hid_freertos) +family_add_subdirectory(device_info) family_add_subdirectory(hid_controller) family_add_subdirectory(msc_file_explorer) diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c index 670e58498..ae574c078 100644 --- a/examples/host/bare_api/src/main.c +++ b/examples/host/bare_api/src/main.c @@ -23,10 +23,6 @@ * */ -/* This example current worked and tested with following controller - * - Sony DualShock 4 [CUH-ZCT2x] VID = 0x054c, PID = 0x09cc - */ - #include #include diff --git a/examples/host/bare_api/src/tusb_config.h b/examples/host/bare_api/src/tusb_config.h index 432446e94..fab233be0 100644 --- a/examples/host/bare_api/src/tusb_config.h +++ b/examples/host/bare_api/src/tusb_config.h @@ -23,8 +23,8 @@ * */ -#ifndef _TUSB_CONFIG_H_ -#define _TUSB_CONFIG_H_ +#ifndef TUSB_CONFIG_H_ +#define TUSB_CONFIG_H_ #ifdef __cplusplus extern "C" { @@ -118,4 +118,4 @@ } #endif -#endif /* _TUSB_CONFIG_H_ */ +#endif diff --git a/examples/host/cdc_msc_hid/src/hid_app.c b/examples/host/cdc_msc_hid/src/hid_app.c index f0d42a08f..6bb3a2072 100644 --- a/examples/host/cdc_msc_hid/src/hid_app.c +++ b/examples/host/cdc_msc_hid/src/hid_app.c @@ -235,6 +235,7 @@ 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) dev_addr; + (void) len; uint8_t const rpt_count = hid_info[instance].report_count; tuh_hid_report_info_t* rpt_info_arr = hid_info[instance].report_info; diff --git a/examples/host/device_info/CMakeLists.txt b/examples/host/device_info/CMakeLists.txt new file mode 100644 index 000000000..76182d6fa --- /dev/null +++ b/examples/host/device_info/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.17) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +# gets PROJECT name for the example +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT} C CXX ASM) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +# Espressif has its own cmake build system +if(FAMILY STREQUAL "espressif") + return() +endif() + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example without RTOS. +# See the corresponding function in hw/bsp/FAMILY/family.cmake for details. +family_configure_host_example(${PROJECT} noos) diff --git a/examples/host/device_info/Makefile b/examples/host/device_info/Makefile new file mode 100644 index 000000000..0235e08c3 --- /dev/null +++ b/examples/host/device_info/Makefile @@ -0,0 +1,13 @@ +include ../../build_system/make/make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +EXAMPLE_SOURCE += \ + src/main.c + +SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) + +include ../../build_system/make/rules.mk diff --git a/examples/host/device_info/only.txt b/examples/host/device_info/only.txt new file mode 100644 index 000000000..fee10f9e2 --- /dev/null +++ b/examples/host/device_info/only.txt @@ -0,0 +1,14 @@ +mcu:KINETIS_KL +mcu:LPC175X_6X +mcu:LPC177X_8X +mcu:LPC18XX +mcu:LPC40XX +mcu:LPC43XX +mcu:MIMXRT1XXX +mcu:MIMXRT10XX +mcu:MIMXRT11XX +mcu:RP2040 +mcu:MSP432E4 +mcu:RX65X +mcu:RAXXX +mcu:MAX3421 diff --git a/examples/host/device_info/src/main.c b/examples/host/device_info/src/main.c new file mode 100644 index 000000000..18beea663 --- /dev/null +++ b/examples/host/device_info/src/main.c @@ -0,0 +1,210 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 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. + * + */ + +/* Host example will get device descriptors of attached devices and print it out via uart/rtt (logger) as follows: + * Device 1: ID 046d:c52f + Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 0200 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x046d + idProduct 0xc52f + bcdDevice 2200 + iManufacturer 1 Logitech + iProduct 2 USB Receiver + iSerialNumber 0 + bNumConfigurations 1 + * + */ + +#include +#include +#include + +#include "bsp/board_api.h" +#include "tusb.h" + +// English +#define LANGUAGE_ID 0x0409 + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ +void led_blinking_task(void); +static void print_utf16(uint16_t* temp_buf, size_t buf_len); + +/*------------- MAIN -------------*/ +int main(void) { + board_init(); + + printf("TinyUSB Device Info Example\r\n"); + + // init host stack on configured roothub port + tuh_init(BOARD_TUH_RHPORT); + + if (board_init_after_tusb) { + board_init_after_tusb(); + } + + while (1) { + // tinyusb host task + tuh_task(); + led_blinking_task(); + } + + return 0; +} + +/*------------- TinyUSB Callbacks -------------*/ + +// Invoked when device is mounted (configured) +void tuh_mount_cb(uint8_t daddr) { + // Get Device Descriptor + tusb_desc_device_t desc_device; + uint8_t xfer_result = tuh_descriptor_get_device_sync(daddr, &desc_device, 18); + if (XFER_RESULT_SUCCESS != xfer_result) { + printf("Failed to get device descriptor\r\n"); + return; + } + + uint16_t buf[256]; + + printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct); + printf("Device Descriptor:\r\n"); + printf(" bLength %u\r\n", desc_device.bLength); + printf(" bDescriptorType %u\r\n", desc_device.bDescriptorType); + printf(" bcdUSB %04x\r\n", desc_device.bcdUSB); + printf(" bDeviceClass %u\r\n", desc_device.bDeviceClass); + printf(" bDeviceSubClass %u\r\n", desc_device.bDeviceSubClass); + printf(" bDeviceProtocol %u\r\n", desc_device.bDeviceProtocol); + printf(" bMaxPacketSize0 %u\r\n", desc_device.bMaxPacketSize0); + printf(" idVendor 0x%04x\r\n", desc_device.idVendor); + printf(" idProduct 0x%04x\r\n", desc_device.idProduct); + printf(" bcdDevice %04x\r\n", desc_device.bcdDevice); + + // Get String descriptor using Sync API + + printf(" iManufacturer %u ", desc_device.iManufacturer); + xfer_result = tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf)); + if (XFER_RESULT_SUCCESS == xfer_result) { + print_utf16(buf, TU_ARRAY_SIZE(buf)); + } + printf("\r\n"); + + printf(" iProduct %u ", desc_device.iProduct); + xfer_result = tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf)); + if (XFER_RESULT_SUCCESS == xfer_result) { + print_utf16(buf, TU_ARRAY_SIZE(buf)); + } + printf("\r\n"); + + printf(" iSerialNumber %u ", desc_device.iSerialNumber); + xfer_result = tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf)); + if (XFER_RESULT_SUCCESS == xfer_result) { + print_utf16(buf, TU_ARRAY_SIZE(buf)); + } + printf("\r\n"); + + printf(" bNumConfigurations %u\r\n", desc_device.bNumConfigurations); +} + +/// Invoked when device is unmounted (bus reset/unplugged) +void tuh_umount_cb(uint8_t daddr) { + printf("Device removed, address = %d\r\n", daddr); +} + +//--------------------------------------------------------------------+ +// Blinking Task +//--------------------------------------------------------------------+ +void led_blinking_task(void) { + const uint32_t interval_ms = 1000; + static uint32_t start_ms = 0; + + static bool led_state = false; + + // Blink every interval ms + if (board_millis() - start_ms < interval_ms) return; // not enough time + start_ms += interval_ms; + + board_led_write(led_state); + led_state = 1 - led_state; // toggle +} + +//--------------------------------------------------------------------+ +// String Descriptor Helper +//--------------------------------------------------------------------+ + +static void _convert_utf16le_to_utf8(const uint16_t* utf16, size_t utf16_len, uint8_t* utf8, size_t utf8_len) { + // TODO: Check for runover. + (void) utf8_len; + // Get the UTF-16 length out of the data itself. + + for (size_t i = 0; i < utf16_len; i++) { + uint16_t chr = utf16[i]; + if (chr < 0x80) { + *utf8++ = chr & 0xffu; + } else if (chr < 0x800) { + *utf8++ = (uint8_t) (0xC0 | (chr >> 6 & 0x1F)); + *utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F)); + } else { + // TODO: Verify surrogate. + *utf8++ = (uint8_t) (0xE0 | (chr >> 12 & 0x0F)); + *utf8++ = (uint8_t) (0x80 | (chr >> 6 & 0x3F)); + *utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F)); + } + // TODO: Handle UTF-16 code points that take two entries. + } +} + +// Count how many bytes a utf-16-le encoded string will take in utf-8. +static int _count_utf8_bytes(const uint16_t* buf, size_t len) { + size_t total_bytes = 0; + for (size_t i = 0; i < len; i++) { + uint16_t chr = buf[i]; + if (chr < 0x80) { + total_bytes += 1; + } else if (chr < 0x800) { + total_bytes += 2; + } else { + total_bytes += 3; + } + // TODO: Handle UTF-16 code points that take two entries. + } + return (int) total_bytes; +} + +static void print_utf16(uint16_t* temp_buf, size_t buf_len) { + if ((temp_buf[0] & 0xff) == 0) return; // empty + size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t); + size_t utf8_len = (size_t) _count_utf8_bytes(temp_buf + 1, utf16_len); + _convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t*) temp_buf, sizeof(uint16_t) * buf_len); + ((uint8_t*) temp_buf)[utf8_len] = '\0'; + + printf("%s", (char*) temp_buf); +} diff --git a/examples/host/device_info/src/tusb_config.h b/examples/host/device_info/src/tusb_config.h new file mode 100644 index 000000000..2e9731eaf --- /dev/null +++ b/examples/host/device_info/src/tusb_config.h @@ -0,0 +1,115 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 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. + * + */ + +#ifndef TUSB_CONFIG_H_ +#define TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// Common Configuration +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUH_MEM_SECTION +#define CFG_TUH_MEM_SECTION +#endif + +#ifndef CFG_TUH_MEM_ALIGN +#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// Host Configuration +//-------------------------------------------------------------------- + +// Enable Host stack +#define CFG_TUH_ENABLED 1 + +#if CFG_TUSB_MCU == OPT_MCU_RP2040 + // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller + // #define CFG_TUH_MAX3421 1 // use max3421 as host controller + + // host roothub port is 1 if using either pio-usb or max3421 + #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421) + #define BOARD_TUH_RHPORT 1 + #endif +#endif + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED + +//------------------------- Board Specific -------------------------- + +// RHPort number used for host can be defined by board.mk, default to port 0 +#ifndef BOARD_TUH_RHPORT +#define BOARD_TUH_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUH_MAX_SPEED +#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// Driver Configuration +//-------------------------------------------------------------------- + +// Size of buffer to hold descriptors and other data used for enumeration +#define CFG_TUH_ENUMERATION_BUFSIZE 256 + +// only hub class is enabled +#define CFG_TUH_HUB 1 + +// max device support (excluding hub device) +// 1 hub typically has 4 ports +#define CFG_TUH_DEVICE_MAX (3*CFG_TUH_HUB + 1) + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/examples/host/hid_controller/src/hid_app.c b/examples/host/hid_controller/src/hid_app.c index bff830ca2..6ffe0b6d4 100644 --- a/examples/host/hid_controller/src/hid_app.c +++ b/examples/host/hid_controller/src/hid_app.c @@ -253,6 +253,7 @@ bool diff_report(sony_ds4_report_t const* rpt1, sony_ds4_report_t const* rpt2) void process_sony_ds4(uint8_t const* report, uint16_t len) { + (void)len; const char* dpad_str[] = { "N", "NE", "E", "SE", "S", "SW", "W", "NW", "none" }; // previous report used to compare for changes diff --git a/hw/bsp/board_api.h b/hw/bsp/board_api.h index eee9ed9c5..a458a3fdc 100644 --- a/hw/bsp/board_api.h +++ b/hw/bsp/board_api.h @@ -24,8 +24,8 @@ * This file is part of the TinyUSB stack. */ -#ifndef _BOARD_API_H_ -#define _BOARD_API_H_ +#ifndef BOARD_API_H_ +#define BOARD_API_H_ #ifdef __cplusplus extern "C" { @@ -72,6 +72,9 @@ void board_init(void); // Init board after tinyusb is initialized void board_init_after_tusb(void) TU_ATTR_WEAK; +// Jump to bootloader +void board_reset_to_bootloader(void) TU_ATTR_WEAK; + // Turn LED on or off void board_led_write(bool state); diff --git a/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.cmake b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.cmake new file mode 100644 index 000000000..f6e47ba30 --- /dev/null +++ b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.cmake @@ -0,0 +1,8 @@ +set(LD_FLASH_SIZE 64K) +set(LD_RAM_SIZE 20K) + +function(update_board TARGET) + target_compile_definitions(${TARGET} PUBLIC + CFG_EXAMPLE_MSC_DUAL_READONLY + ) +endfunction() diff --git a/hw/bsp/ch32v20x/boards/ch32v203_r0_1v0/board.h b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.h similarity index 53% rename from hw/bsp/ch32v20x/boards/ch32v203_r0_1v0/board.h rename to hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.h index 3ed2aef04..3b1187c3a 100644 --- a/hw/bsp/ch32v20x/boards/ch32v203_r0_1v0/board.h +++ b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.h @@ -6,9 +6,13 @@ extern "C" { #endif #define LED_PORT GPIOA -#define LED_PIN GPIO_Pin_15 +#define LED_PIN GPIO_Pin_10 #define LED_STATE_ON 0 +#define BUTTON_PORT GPIOA +#define BUTTON_PIN GPIO_Pin_1 +#define BUTTON_STATE_ACTIVE 0 + #ifdef __cplusplus } #endif diff --git a/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.mk b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.mk new file mode 100644 index 000000000..e594f42a7 --- /dev/null +++ b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.mk @@ -0,0 +1,5 @@ +CFLAGS += -DCFG_EXAMPLE_MSC_DUAL_READONLY + +LDFLAGS += \ + -Wl,--defsym=__FLASH_SIZE=64K \ + -Wl,--defsym=__RAM_SIZE=20K \ diff --git a/hw/bsp/ch32v10x/ch32v10x_conf.h b/hw/bsp/ch32v10x/ch32v10x_conf.h new file mode 100644 index 000000000..939c0fbde --- /dev/null +++ b/hw/bsp/ch32v10x/ch32v10x_conf.h @@ -0,0 +1,37 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : ch32v10x_conf.h + * Author : WCH + * Version : V1.0.0 + * Date : 2020/04/30 + * Description : Library configuration file. +********************************************************************************* +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* Attention: This software (modified or not) and binary are used for +* microcontroller manufactured by Nanjing Qinheng Microelectronics. +*******************************************************************************/ +#ifndef __CH32V10x_CONF_H +#define __CH32V10x_CONF_H + +#include "ch32v10x_adc.h" +#include "ch32v10x_bkp.h" +#include "ch32v10x_crc.h" +#include "ch32v10x_dbgmcu.h" +#include "ch32v10x_dma.h" +#include "ch32v10x_exti.h" +#include "ch32v10x_flash.h" +#include "ch32v10x_gpio.h" +#include "ch32v10x_i2c.h" +#include "ch32v10x_iwdg.h" +#include "ch32v10x_pwr.h" +#include "ch32v10x_rcc.h" +#include "ch32v10x_rtc.h" +#include "ch32v10x_spi.h" +#include "ch32v10x_tim.h" +#include "ch32v10x_usart.h" +#include "ch32v10x_wwdg.h" +#include "ch32v10x_usb.h" +#include "ch32v10x_usb_host.h" +#include "ch32v10x_it.h" +#include "ch32v10x_misc.h" + +#endif /* __CH32V10x_CONF_H */ diff --git a/hw/bsp/ch32v10x/ch32v10x_it.h b/hw/bsp/ch32v10x/ch32v10x_it.h new file mode 100644 index 000000000..13afc2412 --- /dev/null +++ b/hw/bsp/ch32v10x/ch32v10x_it.h @@ -0,0 +1,15 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : ch32v10x_it.h + * Author : WCH + * Version : V1.0.0 + * Date : 2022/08/20 + * Description : This file contains the headers of the interrupt handlers. +********************************************************************************* +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* Attention: This software (modified or not) and binary are used for +* microcontroller manufactured by Nanjing Qinheng Microelectronics. +*******************************************************************************/ +#ifndef __CH32V10x_IT_H +#define __CH32V10x_IT_H + +#endif /* __CH32V10x_IT_H */ diff --git a/hw/bsp/ch32v10x/family.c b/hw/bsp/ch32v10x/family.c new file mode 100644 index 000000000..15f754e11 --- /dev/null +++ b/hw/bsp/ch32v10x/family.c @@ -0,0 +1,148 @@ +#include + +// https://github.com/openwch/ch32v307/pull/90 +// https://github.com/openwch/ch32v20x/pull/12 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + +#include "ch32v10x.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#include "bsp/board_api.h" +#include "board.h" + +__attribute__((interrupt)) __attribute__((used)) +void USBHD_IRQHandler(void) { + #if CFG_TUD_WCH_USBIP_USBFS + tud_int_handler(0); + #endif +} + +__attribute__((interrupt)) __attribute__((used)) +void USBWakeUp_IRQHandler(void) { + #if CFG_TUD_WCH_USBIP_USBFS + tud_int_handler(0); + #endif +} + +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; + +__attribute__((interrupt)) __attribute__((used)) +void SysTick_Handler(void) { + SysTick->CNTL0 = SysTick->CNTL1 = SysTick->CNTL2 = SysTick->CNTL3 = 0; + SysTick->CNTH0 = SysTick->CNTH1 = SysTick->CNTH2 = SysTick->CNTH3 = 0; + system_ticks++; +} + +uint32_t SysTick_Config(uint32_t ticks) { + NVIC_EnableIRQ(SysTicK_IRQn); + SysTick->CTLR = 0; + SysTick->CNTL0 = SysTick->CNTL1 = SysTick->CNTL2 = SysTick->CNTL3 = 0; + SysTick->CNTH0 = SysTick->CNTH1 = SysTick->CNTH2 = SysTick->CNTH3 = 0; + + SysTick->CMPLR0 = (u8)(ticks & 0xFF); + SysTick->CMPLR1 = (u8)(ticks >> 8); + SysTick->CMPLR2 = (u8)(ticks >> 16); + SysTick->CMPLR3 = (u8)(ticks >> 24); + + SysTick->CMPHR0 = SysTick->CMPHR1 = SysTick->CMPHR2 = SysTick->CMPHR3 = 0; + SysTick->CTLR = 1; + return 0; +} + +uint32_t board_millis(void) { + return system_ticks; +} +#endif + +void board_init(void) { + __disable_irq(); + +#if CFG_TUSB_OS == OPT_OS_NONE + SysTick_Config(SystemCoreClock / 1000); +#endif + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); + + EXTEN->EXTEN_CTR |= EXTEN_USBFS_IO_EN; + uint8_t usb_div; + switch (SystemCoreClock) { + case 48000000: usb_div = RCC_USBCLKSource_PLLCLK_Div1; break; + case 72000000: usb_div = RCC_USBCLKSource_PLLCLK_1Div5; break; + default: TU_ASSERT(0,); break; + } + RCC_USBCLKConfig(usb_div); + RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBFS, ENABLE); + + #ifdef LED_PIN + GPIO_InitTypeDef led_init = { + .GPIO_Pin = LED_PIN, + .GPIO_Mode = GPIO_Mode_Out_OD, + .GPIO_Speed = GPIO_Speed_50MHz, + }; + GPIO_Init(LED_PORT, &led_init); + #endif + + #ifdef BUTTON_PIN + GPIO_InitTypeDef button_init = { + .GPIO_Pin = BUTTON_PIN, + .GPIO_Mode = GPIO_Mode_IPU, + .GPIO_Speed = GPIO_Speed_50MHz, + }; + GPIO_Init(BUTTON_PORT, &button_init); + #endif + + // UART TX is PA9 + RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); + GPIO_InitTypeDef usart_init = { + .GPIO_Pin = GPIO_Pin_9, + .GPIO_Speed = GPIO_Speed_50MHz, + .GPIO_Mode = GPIO_Mode_AF_PP, + }; + GPIO_Init(GPIOA, &usart_init); + + USART_InitTypeDef usart = { + .USART_BaudRate = 115200, + .USART_WordLength = USART_WordLength_8b, + .USART_StopBits = USART_StopBits_1, + .USART_Parity = USART_Parity_No, + .USART_Mode = USART_Mode_Tx, + .USART_HardwareFlowControl = USART_HardwareFlowControl_None, + }; + USART_Init(USART1, &usart); + USART_Cmd(USART1, ENABLE); + + __enable_irq(); + + board_led_write(true); +} + +void board_led_write(bool state) { + GPIO_WriteBit(LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON)); +} + +uint32_t board_button_read(void) { + return BUTTON_STATE_ACTIVE == GPIO_ReadInputDataBit(BUTTON_PORT, BUTTON_PIN); +} + +int board_uart_read(uint8_t *buf, int len) { + (void) buf; + (void) len; + return 0; +} + +int board_uart_write(void const *buf, int len) { + const char *bufc = (const char *) buf; + for (int i = 0; i < len; i++) { + while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); + USART_SendData(USART1, *bufc++); + } + + return len; +} diff --git a/hw/bsp/ch32v10x/family.cmake b/hw/bsp/ch32v10x/family.cmake new file mode 100644 index 000000000..c0af0ef44 --- /dev/null +++ b/hw/bsp/ch32v10x/family.cmake @@ -0,0 +1,117 @@ +include_guard() + +#set(UF2_FAMILY_ID 0x699b62ec) +set(CH32_FAMILY ch32v10x) +set(SDK_DIR ${TOP}/hw/mcu/wch/ch32v103) +set(SDK_SRC_DIR ${SDK_DIR}/EVT/EXAM/SRC) + +# include board specific +include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake) + +# toolchain set up +set(CMAKE_SYSTEM_PROCESSOR rv32imac-ilp32 CACHE INTERNAL "System Processor") +set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/riscv_${TOOLCHAIN}.cmake) + +set(FAMILY_MCUS CH32V103 CACHE INTERNAL "") +set(OPENOCD_OPTION "-f ${CMAKE_CURRENT_LIST_DIR}/wch-riscv.cfg") + +#------------------------------------ +# BOARD_TARGET +#------------------------------------ +# only need to be built ONCE for all examples +function(add_board_target BOARD_TARGET) + if (TARGET ${BOARD_TARGET}) + return() + endif() + + if (NOT DEFINED LD_FILE_GNU) + set(LD_FILE_GNU ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/linker/${CH32_FAMILY}.ld) + endif () + set(LD_FILE_Clang ${LD_FILE_GNU}) + + if (NOT DEFINED STARTUP_FILE_GNU) + set(STARTUP_FILE_GNU ${SDK_SRC_DIR}/Startup/startup_${CH32_FAMILY}.S) + endif () + set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU}) + + add_library(${BOARD_TARGET} STATIC + ${SDK_SRC_DIR}/Core/core_riscv.c + ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_gpio.c + ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_misc.c + ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_rcc.c + ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_usart.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/system_${CH32_FAMILY}.c + ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}} + ) + target_include_directories(${BOARD_TARGET} PUBLIC + ${SDK_SRC_DIR}/Core + ${SDK_SRC_DIR}/Peripheral/inc + ${CMAKE_CURRENT_FUNCTION_LIST_DIR} + ) + target_compile_definitions(${BOARD_TARGET} PUBLIC + ) + + update_board(${BOARD_TARGET}) + + if (CMAKE_C_COMPILER_ID STREQUAL "GNU") + target_compile_options(${BOARD_TARGET} PUBLIC + -mcmodel=medany + ) + target_link_options(${BOARD_TARGET} PUBLIC + "LINKER:--script=${LD_FILE_GNU}" + -Wl,--defsym=__FLASH_SIZE=${LD_FLASH_SIZE} + -Wl,--defsym=__RAM_SIZE=${LD_RAM_SIZE} + -nostartfiles + --specs=nosys.specs --specs=nano.specs + ) + elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang") + message(FATAL_ERROR "Clang is not supported for MSP432E4") + elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR") + target_link_options(${BOARD_TARGET} PUBLIC + "LINKER:--config=${LD_FILE_IAR}" + ) + endif () +endfunction() + + +#------------------------------------ +# Functions +#------------------------------------ +function(family_configure_example TARGET RTOS) + family_configure_common(${TARGET} ${RTOS}) + + # Board target + add_board_target(board_${BOARD}) + + #---------- Port Specific ---------- + # These files are built for each example since it depends on example's tusb_config.h + target_sources(${TARGET} PUBLIC + # BSP + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c + ) + target_include_directories(${TARGET} PUBLIC + # family, hw, board + ${CMAKE_CURRENT_FUNCTION_LIST_DIR} + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../ + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD} + ) + + # Add TinyUSB target and port source + family_add_tinyusb(${TARGET} OPT_MCU_CH32V103 ${RTOS}) + + target_sources(${TARGET}-tinyusb PUBLIC + ${TOP}/src/portable/wch/dcd_ch32_usbfs.c + ) + target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) + + # Link dependencies + target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb) + + # Flashing + family_add_bin_hex(${TARGET}) + family_flash_openocd_wch(${TARGET}) + + #family_add_uf2(${TARGET} ${UF2_FAMILY_ID}) + #family_flash_uf2(${TARGET} ${UF2_FAMILY_ID}) +endfunction() diff --git a/hw/bsp/ch32v10x/family.mk b/hw/bsp/ch32v10x/family.mk new file mode 100644 index 000000000..d96d5012e --- /dev/null +++ b/hw/bsp/ch32v10x/family.mk @@ -0,0 +1,53 @@ +# https://www.embecosm.com/resources/tool-chain-downloads/#riscv-stable +#CROSS_COMPILE ?= riscv32-unknown-elf- + +# Toolchain from https://nucleisys.com/download.php +#CROSS_COMPILE ?= riscv-nuclei-elf- + +# Toolchain from https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack +CROSS_COMPILE ?= riscv-none-elf- + +CH32_FAMILY = ch32v10x +SDK_DIR = hw/mcu/wch/ch32v103 +SDK_SRC_DIR = $(SDK_DIR)/EVT/EXAM/SRC + +include $(TOP)/$(BOARD_PATH)/board.mk +CPU_CORE ?= rv32imac-ilp32 + +# Port0 use FSDev, Port1 use USBFS +PORT ?= 0 + +CFLAGS += \ + -mcmodel=medany \ + -ffat-lto-objects \ + -flto \ + -DCFG_TUSB_MCU=OPT_MCU_CH32V103 + +# https://github.com/openwch/ch32v20x/pull/12 +CFLAGS += -Wno-error=strict-prototypes + +LDFLAGS_GCC += \ + -nostdlib -nostartfiles \ + --specs=nosys.specs --specs=nano.specs \ + +LD_FILE = $(FAMILY_PATH)/linker/${CH32_FAMILY}.ld + +SRC_C += \ + src/portable/wch/dcd_ch32_usbfs.c \ + $(SDK_SRC_DIR)/Core/core_riscv.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_gpio.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_misc.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_rcc.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_usart.c \ + +SRC_S += $(SDK_SRC_DIR)/Startup/startup_${CH32_FAMILY}.S + +INC += \ + $(TOP)/$(BOARD_PATH) \ + $(TOP)/$(SDK_SRC_DIR)/Core \ + $(TOP)/$(SDK_SRC_DIR)/Peripheral/inc \ + +FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/RISC-V + +OPENOCD_WCH_OPTION=-f $(TOP)/$(FAMILY_PATH)/wch-riscv.cfg +flash: flash-openocd-wch diff --git a/hw/bsp/ch32v10x/linker/ch32v10x.ld b/hw/bsp/ch32v10x/linker/ch32v10x.ld new file mode 100644 index 000000000..cd5c8dc17 --- /dev/null +++ b/hw/bsp/ch32v10x/linker/ch32v10x.ld @@ -0,0 +1,165 @@ +/* Define default values if not already defined */ +__FLASH_SIZE = DEFINED(__flash_size) ? __flash_size : 64K; +__RAM_SIZE = DEFINED(__ram_size) ? __ram_size : 20K; + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = __FLASH_SIZE + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = __RAM_SIZE +} + +ENTRY( _start ) + +__stack_size = 2048; + +PROVIDE( _stack_size = __stack_size ); + +SECTIONS +{ + .init : + { + _sinit = .; + . = ALIGN(4); + KEEP(*(SORT_NONE(.init))) + . = ALIGN(4); + _einit = .; + } >FLASH AT>FLASH + + .vector : + { + *(.vector); + . = ALIGN(64); + } >FLASH AT>FLASH + + .text : + { + . = ALIGN(4); + *(.text) + *(.text.*) + *(.rodata) + *(.rodata*) + *(.gnu.linkonce.t.*) + . = ALIGN(4); + } >FLASH AT>FLASH + + .fini : + { + KEEP(*(SORT_NONE(.fini))) + . = ALIGN(4); + } >FLASH AT>FLASH + + PROVIDE( _etext = . ); + PROVIDE( _eitcm = . ); + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH AT>FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH AT>FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH AT>FLASH + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >FLASH AT>FLASH + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >FLASH AT>FLASH + + .dalign : + { + . = ALIGN(4); + PROVIDE(_data_vma = .); + } >RAM AT>FLASH + + .dlalign : + { + . = ALIGN(4); + PROVIDE(_data_lma = .); + } >FLASH AT>FLASH + + .data : + { + *(.gnu.linkonce.r.*) + *(.data .data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + *(.sdata .sdata.*) + *(.sdata2.*) + *(.gnu.linkonce.s.*) + . = ALIGN(8); + *(.srodata.cst16) + *(.srodata.cst8) + *(.srodata.cst4) + *(.srodata.cst2) + *(.srodata .srodata.*) + . = ALIGN(4); + PROVIDE( _edata = .); + } >RAM AT>FLASH + + .bss : + { + . = ALIGN(4); + PROVIDE( _sbss = .); + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss*) + *(.gnu.linkonce.b.*) + *(COMMON*) + . = ALIGN(4); + PROVIDE( _ebss = .); + } >RAM AT>FLASH + + PROVIDE( _end = _ebss); + PROVIDE( end = . ); + + .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size : + { + PROVIDE( _heap_end = . ); + . = ALIGN(4); + PROVIDE(_susrstack = . ); + . = . + __stack_size; + PROVIDE( _eusrstack = .); + } >RAM + +} diff --git a/hw/bsp/ch32v10x/system_ch32v10x.c b/hw/bsp/ch32v10x/system_ch32v10x.c new file mode 100644 index 000000000..b083b83b4 --- /dev/null +++ b/hw/bsp/ch32v10x/system_ch32v10x.c @@ -0,0 +1,600 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : system_ch32v10x.c + * Author : WCH + * Version : V1.0.0 + * Date : 2020/04/30 + * Description : CH32V10x Device Peripheral Access Layer System Source File. +********************************************************************************* +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* Attention: This software (modified or not) and binary are used for +* microcontroller manufactured by Nanjing Qinheng Microelectronics. +*******************************************************************************/ +#include "ch32v10x.h" + +/* + * Uncomment the line corresponding to the desired System clock (SYSCLK) frequency (after + * reset the HSI is used as SYSCLK source). + * If none of the define below is enabled, the HSI is used as System clock source. + */ +//#define SYSCLK_FREQ_HSE HSE_VALUE +//#define SYSCLK_FREQ_48MHz_HSE 48000000 +//#define SYSCLK_FREQ_56MHz_HSE 56000000 +#define SYSCLK_FREQ_72MHz_HSE 72000000 +//#define SYSCLK_FREQ_HSI HSI_VALUE +//#define SYSCLK_FREQ_48MHz_HSI 48000000 +//#define SYSCLK_FREQ_56MHz_HSI 56000000 +//#define SYSCLK_FREQ_72MHz_HSI 72000000 + +/* Clock Definitions */ +#ifdef SYSCLK_FREQ_HSE +uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_48MHz_HSE +uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz_HSE; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_56MHz_HSE +uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz_HSE; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_72MHz_HSE +uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz_HSE; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_48MHz_HSI +uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz_HSI; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_56MHz_HSI +uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz_HSI; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_72MHz_HSI +uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz_HSI; /* System Clock Frequency (Core Clock) */ +#else +uint32_t SystemCoreClock = HSI_VALUE; /* System Clock Frequency (Core Clock) */ + +#endif + +__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + +/* ch32v10x_system_private_function_proto_types */ +static void SetSysClock(void); + +#ifdef SYSCLK_FREQ_HSE +static void SetSysClockToHSE( void ); +#elif defined SYSCLK_FREQ_48MHz_HSE +static void SetSysClockTo48_HSE( void ); +#elif defined SYSCLK_FREQ_56MHz_HSE +static void SetSysClockTo56_HSE( void ); +#elif defined SYSCLK_FREQ_72MHz_HSE +static void SetSysClockTo72_HSE( void ); +#elif defined SYSCLK_FREQ_48MHz_HSI +static void SetSysClockTo48_HSI( void ); +#elif defined SYSCLK_FREQ_56MHz_HSI +static void SetSysClockTo56_HSI( void ); +#elif defined SYSCLK_FREQ_72MHz_HSI +static void SetSysClockTo72_HSI( void ); + +#endif + +/********************************************************************* + * @fn SystemInit + * + * @brief Setup the microcontroller system Initialize the Embedded Flash Interface, + * the PLL and update the SystemCoreClock variable. + * + * @return none + */ +void SystemInit(void) +{ + RCC->CTLR |= (uint32_t)0x00000001; + RCC->CFGR0 &= (uint32_t)0xF8FF0000; + RCC->CTLR &= (uint32_t)0xFEF6FFFF; + RCC->CTLR &= (uint32_t)0xFFFBFFFF; + RCC->CFGR0 &= (uint32_t)0xFF80FFFF; + RCC->INTR = 0x009F0000; + SetSysClock(); +} + +/********************************************************************* + * @fn SystemCoreClockUpdate + * + * @brief Update SystemCoreClock variable according to Clock Register Values. + * + * @return none + */ +void SystemCoreClockUpdate(void) +{ + uint32_t tmp = 0, pllmull = 0, pllsource = 0; + + tmp = RCC->CFGR0 & RCC_SWS; + + switch(tmp) + { + case 0x00: + SystemCoreClock = HSI_VALUE; + break; + case 0x04: + SystemCoreClock = HSE_VALUE; + break; + case 0x08: + pllmull = RCC->CFGR0 & RCC_PLLMULL; + pllsource = RCC->CFGR0 & RCC_PLLSRC; + pllmull = (pllmull >> 18) + 2; + if(pllsource == 0x00) + { + if( EXTEN->EXTEN_CTR & EXTEN_PLL_HSI_PRE ) + { + SystemCoreClock = ( HSI_VALUE ) * pllmull; + } + else + { + SystemCoreClock = ( HSI_VALUE >> 1 ) * pllmull; + } + } + else + { + if((RCC->CFGR0 & RCC_PLLXTPRE) != (uint32_t)RESET) + { + SystemCoreClock = (HSE_VALUE >> 1) * pllmull; + } + else + { + SystemCoreClock = HSE_VALUE * pllmull; + } + } + break; + default: + SystemCoreClock = HSI_VALUE; + break; + } + + tmp = AHBPrescTable[((RCC->CFGR0 & RCC_HPRE) >> 4)]; + SystemCoreClock >>= tmp; +} + +/********************************************************************* + * @fn SetSysClock + * + * @brief Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClock(void) +{ + //GPIO_IPD_Unused(); +#ifdef SYSCLK_FREQ_HSE + SetSysClockToHSE(); +#elif defined SYSCLK_FREQ_48MHz_HSE + SetSysClockTo48_HSE(); +#elif defined SYSCLK_FREQ_56MHz_HSE + SetSysClockTo56_HSE(); +#elif defined SYSCLK_FREQ_72MHz_HSE + SetSysClockTo72_HSE(); +#elif defined SYSCLK_FREQ_48MHz_HSI + SetSysClockTo48_HSI(); +#elif defined SYSCLK_FREQ_56MHz_HSI + SetSysClockTo56_HSI(); +#elif defined SYSCLK_FREQ_72MHz_HSI + SetSysClockTo72_HSI(); + +#endif + + /* If none of the define above is enabled, the HSI is used as System clock + * source (default after reset) + */ +} + +#ifdef SYSCLK_FREQ_HSE + +/********************************************************************* + * @fn SetSysClockToHSE + * + * @brief Sets HSE as System clock source and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockToHSE(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if(HSEStatus == (uint32_t)0x01) + { + FLASH->ACTLR |= FLASH_ACTLR_PRFTBE; + /* Flash 0 wait state */ + FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); + FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_0; + + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV1; + + /* Select HSE as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_HSE; + + /* Wait till HSE is used as system clock source */ + while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x04) + { + } + } + else + { + /* If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + + +#elif defined SYSCLK_FREQ_48MHz_HSE + +/********************************************************************* + * @fn SetSysClockTo48_HSE + * + * @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo48_HSE(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if(HSEStatus == (uint32_t)0x01) + { + /* Enable Prefetch Buffer */ + FLASH->ACTLR |= FLASH_ACTLR_PRFTBE; + + /* Flash 1 wait state */ + FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); + FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1; + + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSE * 6 = 48 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL)); + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLMULL6); + + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } + } + else + { + /* + * If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + +#elif defined SYSCLK_FREQ_56MHz_HSE + +/********************************************************************* + * @fn SetSysClockTo56_HSE + * + * @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo56_HSE(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if(HSEStatus == (uint32_t)0x01) + { + /* Enable Prefetch Buffer */ + FLASH->ACTLR |= FLASH_ACTLR_PRFTBE; + + /* Flash 2 wait state */ + FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); + FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_2; + + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL)); + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLMULL7); + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } + } + else + { + /* + * If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + +#elif defined SYSCLK_FREQ_72MHz_HSE + +/********************************************************************* + * @fn SetSysClockTo72_HSE + * + * @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo72_HSE(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if(HSEStatus == (uint32_t)0x01) + { + /* Enable Prefetch Buffer */ + FLASH->ACTLR |= FLASH_ACTLR_PRFTBE; + + /* Flash 2 wait state */ + FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); + FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_2; + + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE | + RCC_PLLMULL)); + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLMULL9); + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } + } + else + { + /* + * If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + +#elif defined SYSCLK_FREQ_48MHz_HSI + +/********************************************************************* + * @fn SetSysClockTo48_HSI + * + * @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo48_HSI(void) +{ + EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE; + + /* Enable Prefetch Buffer */ + FLASH->ACTLR |= FLASH_ACTLR_PRFTBE; + + /* Flash 1 wait state */ + FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); + FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1; + + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSI * 6 = 48 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL)); + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL6); + + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } +} + +#elif defined SYSCLK_FREQ_56MHz_HSI + +/********************************************************************* + * @fn SetSysClockTo56_HSI + * + * @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo56_HSI(void) +{ + EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE; + + /* Enable Prefetch Buffer */ + FLASH->ACTLR |= FLASH_ACTLR_PRFTBE; + + /* Flash 1 wait state */ + FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); + FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1; + + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSI * 7 = 56 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL)); + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL7); + + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } +} + +#elif defined SYSCLK_FREQ_72MHz_HSI + +/********************************************************************* + * @fn SetSysClockTo72_HSI + * + * @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo72_HSI(void) +{ + EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE; + + /* Enable Prefetch Buffer */ + FLASH->ACTLR |= FLASH_ACTLR_PRFTBE; + + /* Flash 1 wait state */ + FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); + FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1; + + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSI * 9 = 72 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL)); + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL9); + + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } +} + +#endif diff --git a/hw/bsp/ch32v10x/system_ch32v10x.h b/hw/bsp/ch32v10x/system_ch32v10x.h new file mode 100644 index 000000000..d15624520 --- /dev/null +++ b/hw/bsp/ch32v10x/system_ch32v10x.h @@ -0,0 +1,29 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : system_ch32v10x.h + * Author : WCH + * Version : V1.0.0 + * Date : 2020/04/30 + * Description : CH32V10x Device Peripheral Access Layer System Header File. +********************************************************************************* +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* Attention: This software (modified or not) and binary are used for +* microcontroller manufactured by Nanjing Qinheng Microelectronics. +*******************************************************************************/ +#ifndef __SYSTEM_CH32V10x_H +#define __SYSTEM_CH32V10x_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint32_t SystemCoreClock; /* System Clock Frequency (Core Clock) */ + +/* System_Exported_Functions */ +extern void SystemInit(void); +extern void SystemCoreClockUpdate(void); + +#ifdef __cplusplus +} +#endif + +#endif /*__CH32V10x_SYSTEM_H */ diff --git a/hw/bsp/ch32v10x/wch-riscv.cfg b/hw/bsp/ch32v10x/wch-riscv.cfg new file mode 100644 index 000000000..aa35aa9c5 --- /dev/null +++ b/hw/bsp/ch32v10x/wch-riscv.cfg @@ -0,0 +1,17 @@ +adapter driver wlinke +adapter speed 6000 +transport select sdi + +wlink_set_address 0x00000000 +set _CHIPNAME wch_riscv +sdi newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001 + +set _TARGETNAME $_CHIPNAME.cpu + +target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME +$_TARGETNAME.0 configure -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1 +set _FLASHNAME $_CHIPNAME.flash + +flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0 + +echo "Ready for Remote Connections" diff --git a/hw/bsp/ch32v20x/boards/ch32v203_r0_1v0/board.mk b/hw/bsp/ch32v20x/boards/ch32v203_r0_1v0/board.mk deleted file mode 100644 index 7d7462312..000000000 --- a/hw/bsp/ch32v20x/boards/ch32v203_r0_1v0/board.mk +++ /dev/null @@ -1,7 +0,0 @@ -MCU_VARIANT = D6 - -CFLAGS += -DCFG_EXAMPLE_MSC_DUAL_READONLY - -LDFLAGS += \ - -Wl,--defsym=__flash_size=64K \ - -Wl,--defsym=__ram_size=20K \ diff --git a/hw/bsp/ch32v20x/boards/ch32v203_r0_1v0/board.cmake b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.cmake similarity index 68% rename from hw/bsp/ch32v20x/boards/ch32v203_r0_1v0/board.cmake rename to hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.cmake index 31b6b716e..4aae6bdc2 100644 --- a/hw/bsp/ch32v20x/boards/ch32v203_r0_1v0/board.cmake +++ b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.cmake @@ -1,12 +1,15 @@ set(MCU_VARIANT D6) -set(LD_FLASH_SIZE 64K) +# 64KB zero-wait, 224KB total flash +#set(LD_FLASH_SIZE 64K) +set(LD_FLASH_SIZE 224K) set(LD_RAM_SIZE 20K) # set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/../../linker/${CH32_FAMILY}_tinyuf2.ld) function(update_board TARGET) target_compile_definitions(${TARGET} PUBLIC + SYSCLK_FREQ_144MHz_HSE=144000000 CFG_EXAMPLE_MSC_DUAL_READONLY ) endfunction() diff --git a/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.h b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.h new file mode 100644 index 000000000..692cf11bf --- /dev/null +++ b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.h @@ -0,0 +1,21 @@ +#ifndef BOARD_H_ +#define BOARD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define LED_PORT GPIOA +#define LED_PIN GPIO_Pin_0 +#define LED_STATE_ON 0 + +#define UART_DEV USART1 +#define UART_CLOCK_EN() RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE) +#define UART_TX_PIN GPIO_Pin_9 +#define UART_RX_PIN GPIO_Pin_10 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.mk b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.mk new file mode 100644 index 000000000..bdd15f737 --- /dev/null +++ b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.mk @@ -0,0 +1,11 @@ +MCU_VARIANT = D6 + +CFLAGS += \ + -DSYSCLK_FREQ_144MHz_HSE=144000000 \ + -DCH32_FLASH_ENHANCE_READ_MODE=1 \ + -DCFG_EXAMPLE_MSC_DUAL_READONLY \ + +# 64KB zero-wait, 224KB total flash +LDFLAGS += \ + -Wl,--defsym=__FLASH_SIZE=224K \ + -Wl,--defsym=__RAM_SIZE=20K \ diff --git a/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake new file mode 100644 index 000000000..ecb8b378f --- /dev/null +++ b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake @@ -0,0 +1,13 @@ +set(MCU_VARIANT D6) + +# 32KB zero-wait, 224KB total flash +#set(LD_FLASH_SIZE 32K) +set(LD_FLASH_SIZE 224K) +set(LD_RAM_SIZE 10K) + +function(update_board TARGET) + target_compile_definitions(${TARGET} PUBLIC + SYSCLK_FREQ_144MHz_HSI=144000000 + CFG_EXAMPLE_MSC_DUAL_READONLY + ) +endfunction() diff --git a/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.h b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.h new file mode 100644 index 000000000..783831edd --- /dev/null +++ b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.h @@ -0,0 +1,21 @@ +#ifndef BOARD_H_ +#define BOARD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define LED_PORT GPIOA +#define LED_PIN GPIO_Pin_0 +#define LED_STATE_ON 0 + +#define UART_DEV USART2 +#define UART_CLOCK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE) +#define UART_TX_PIN GPIO_Pin_2 +#define UART_RX_PIN GPIO_Pin_3 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.mk b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.mk new file mode 100644 index 000000000..f71f53478 --- /dev/null +++ b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.mk @@ -0,0 +1,11 @@ +MCU_VARIANT = D6 + +CFLAGS += \ + -DSYSCLK_FREQ_144MHz_HSI=144000000 \ + -DCH32_FLASH_ENHANCE_READ_MODE=1 \ + -DCFG_EXAMPLE_MSC_DUAL_READONLY \ + +# 32KB zero-wait, 224KB total flash +LDFLAGS += \ + -Wl,--defsym=__FLASH_SIZE=224K \ + -Wl,--defsym=__RAM_SIZE=10K \ diff --git a/hw/bsp/ch32v20x/boards/nanoch32v203/board.cmake b/hw/bsp/ch32v20x/boards/nanoch32v203/board.cmake index 8d3e0326e..6c7712cdd 100644 --- a/hw/bsp/ch32v20x/boards/nanoch32v203/board.cmake +++ b/hw/bsp/ch32v20x/boards/nanoch32v203/board.cmake @@ -1,10 +1,13 @@ set(MCU_VARIANT D6) +# 64KB zero-wait, 224KB total flash set(LD_FLASH_SIZE 64K) +#set(LD_FLASH_SIZE 224K) set(LD_RAM_SIZE 20K) function(update_board TARGET) target_compile_definitions(${TARGET} PUBLIC + SYSCLK_FREQ_144MHz_HSE=144000000 CFG_EXAMPLE_MSC_DUAL_READONLY ) endfunction() diff --git a/hw/bsp/ch32v20x/boards/nanoch32v203/board.h b/hw/bsp/ch32v20x/boards/nanoch32v203/board.h index c8d28d90f..64eaf931e 100644 --- a/hw/bsp/ch32v20x/boards/nanoch32v203/board.h +++ b/hw/bsp/ch32v20x/boards/nanoch32v203/board.h @@ -8,7 +8,11 @@ extern "C" { #define LED_PORT GPIOA #define LED_PIN GPIO_Pin_15 #define LED_STATE_ON 0 -#define LED_CLOCK_EN() RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE) + +#define UART_DEV USART1 +#define UART_CLOCK_EN() RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE) +#define UART_TX_PIN GPIO_Pin_9 +#define UART_RX_PIN GPIO_Pin_10 #ifdef __cplusplus } diff --git a/hw/bsp/ch32v20x/boards/nanoch32v203/board.mk b/hw/bsp/ch32v20x/boards/nanoch32v203/board.mk index 7d7462312..362aace47 100644 --- a/hw/bsp/ch32v20x/boards/nanoch32v203/board.mk +++ b/hw/bsp/ch32v20x/boards/nanoch32v203/board.mk @@ -1,7 +1,11 @@ MCU_VARIANT = D6 -CFLAGS += -DCFG_EXAMPLE_MSC_DUAL_READONLY +CFLAGS += \ + -DSYSCLK_FREQ_144MHz_HSE=144000000 \ + -DCH32_FLASH_ENHANCE_READ_MODE=1 \ + -DCFG_EXAMPLE_MSC_DUAL_READONLY \ +# 64KB zero-wait , 224KB total flash LDFLAGS += \ - -Wl,--defsym=__flash_size=64K \ - -Wl,--defsym=__ram_size=20K \ + -Wl,--defsym=__FLASH_SIZE=224K \ + -Wl,--defsym=__RAM_SIZE=20K \ diff --git a/hw/bsp/ch32v20x/core_riscv.h b/hw/bsp/ch32v20x/core_riscv.h deleted file mode 100644 index be1b52a6c..000000000 --- a/hw/bsp/ch32v20x/core_riscv.h +++ /dev/null @@ -1,572 +0,0 @@ -/********************************** (C) COPYRIGHT ******************************* - * File Name : core_riscv.h - * Author : WCH - * Version : V1.0.0 - * Date : 2021/06/06 - * Description : RISC-V Core Peripheral Access Layer Header File for CH32V20x -********************************************************************************* -* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. -* Attention: This software (modified or not) and binary are used for -* microcontroller manufactured by Nanjing Qinheng Microelectronics. -*******************************************************************************/ -#ifndef __CORE_RISCV_H__ -#define __CORE_RISCV_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -/* IO definitions */ -#ifdef __cplusplus - #define __I volatile /* defines 'read only' permissions */ -#else - #define __I volatile const /* defines 'read only' permissions */ -#endif -#define __O volatile /* defines 'write only' permissions */ -#define __IO volatile /* defines 'read / write' permissions */ - -/* Standard Peripheral Library old types (maintained for legacy purpose) */ -typedef __I uint64_t vuc64; /* Read Only */ -typedef __I uint32_t vuc32; /* Read Only */ -typedef __I uint16_t vuc16; /* Read Only */ -typedef __I uint8_t vuc8; /* Read Only */ - -typedef const uint64_t uc64; /* Read Only */ -typedef const uint32_t uc32; /* Read Only */ -typedef const uint16_t uc16; /* Read Only */ -typedef const uint8_t uc8; /* Read Only */ - -typedef __I int64_t vsc64; /* Read Only */ -typedef __I int32_t vsc32; /* Read Only */ -typedef __I int16_t vsc16; /* Read Only */ -typedef __I int8_t vsc8; /* Read Only */ - -typedef const int64_t sc64; /* Read Only */ -typedef const int32_t sc32; /* Read Only */ -typedef const int16_t sc16; /* Read Only */ -typedef const int8_t sc8; /* Read Only */ - -typedef __IO uint64_t vu64; -typedef __IO uint32_t vu32; -typedef __IO uint16_t vu16; -typedef __IO uint8_t vu8; - -typedef uint64_t u64; -typedef uint32_t u32; -typedef uint16_t u16; -typedef uint8_t u8; - -typedef __IO int64_t vs64; -typedef __IO int32_t vs32; -typedef __IO int16_t vs16; -typedef __IO int8_t vs8; - -typedef int64_t s64; -typedef int32_t s32; -typedef int16_t s16; -typedef int8_t s8; - -typedef enum {NoREADY = 0, READY = !NoREADY} ErrorStatus; - -typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState; - -typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus; - -#define RV_STATIC_INLINE static inline - -/* memory mapped structure for Program Fast Interrupt Controller (PFIC) */ -typedef struct{ - __I uint32_t ISR[8]; - __I uint32_t IPR[8]; - __IO uint32_t ITHRESDR; - __IO uint32_t RESERVED; - __IO uint32_t CFGR; - __I uint32_t GISR; - __IO uint8_t VTFIDR[4]; - uint8_t RESERVED0[12]; - __IO uint32_t VTFADDR[4]; - uint8_t RESERVED1[0x90]; - __O uint32_t IENR[8]; - uint8_t RESERVED2[0x60]; - __O uint32_t IRER[8]; - uint8_t RESERVED3[0x60]; - __O uint32_t IPSR[8]; - uint8_t RESERVED4[0x60]; - __O uint32_t IPRR[8]; - uint8_t RESERVED5[0x60]; - __IO uint32_t IACTR[8]; - uint8_t RESERVED6[0xE0]; - __IO uint8_t IPRIOR[256]; - uint8_t RESERVED7[0x810]; - __IO uint32_t SCTLR; -}PFIC_Type; - -/* memory mapped structure for SysTick */ -typedef struct -{ - __IO uint32_t CTLR; - __IO uint32_t SR; - __IO uint64_t CNT; - __IO uint64_t CMP; -}SysTick_Type; - -#define PFIC ((PFIC_Type *) 0xE000E000 ) -#define NVIC PFIC -#define NVIC_KEY1 ((uint32_t)0xFA050000) -#define NVIC_KEY2 ((uint32_t)0xBCAF0000) -#define NVIC_KEY3 ((uint32_t)0xBEEF0000) - -#define SysTick ((SysTick_Type *) 0xE000F000) - -/********************************************************************* - * @fn __enable_irq - * - * @brief Enable Global Interrupt - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void __enable_irq(void) -{ - __asm volatile ("csrw 0x800, %0" : : "r" (0x6088) ); -} - -/********************************************************************* - * @fn __disable_irq - * - * @brief Disable Global Interrupt - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void __disable_irq(void) -{ - __asm volatile ("csrw 0x800, %0" : : "r" (0x6000) ); -} - -/********************************************************************* - * @fn __NOP - * - * @brief nop - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void __NOP(void) -{ - __asm volatile ("nop"); -} - -/********************************************************************* - * @fn NVIC_EnableIRQ - * - * @brief Disable Interrupt - * - * @param IRQn - Interrupt Numbers - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) -{ - NVIC->IENR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); -} - -/********************************************************************* - * @fn NVIC_DisableIRQ - * - * @brief Disable Interrupt - * - * @param IRQn - Interrupt Numbers - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) -{ - NVIC->IRER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); -} - -/********************************************************************* - * @fn NVIC_GetStatusIRQ - * - * @brief Get Interrupt Enable State - * - * @param IRQn - Interrupt Numbers - * - * @return 1 - Interrupt Pending Enable - * 0 - Interrupt Pending Disable - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE uint32_t NVIC_GetStatusIRQ(IRQn_Type IRQn) -{ - return((uint32_t) ((NVIC->ISR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); -} - -/********************************************************************* - * @fn NVIC_GetPendingIRQ - * - * @brief Get Interrupt Pending State - * - * @param IRQn - Interrupt Numbers - * - * @return 1 - Interrupt Pending Enable - * 0 - Interrupt Pending Disable - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) -{ - return((uint32_t) ((NVIC->IPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); -} - -/********************************************************************* - * @fn NVIC_SetPendingIRQ - * - * @brief Set Interrupt Pending - * - * @param IRQn - Interrupt Numbers - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) -{ - NVIC->IPSR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); -} - -/********************************************************************* - * @fn NVIC_ClearPendingIRQ - * - * @brief Clear Interrupt Pending - * - * @param IRQn - Interrupt Numbers - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) -{ - NVIC->IPRR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); -} - -/********************************************************************* - * @fn NVIC_GetActive - * - * @brief Get Interrupt Active State - * - * @param IRQn - Interrupt Numbers - * - * @return 1 - Interrupt Active - * 0 - Interrupt No Active - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) -{ - return((uint32_t)((NVIC->IACTR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); -} - -/********************************************************************* - * @fn NVIC_SetPriority - * - * @brief Set Interrupt Priority - * - * @param IRQn - Interrupt Numbers - * priority - bit7 - Pre-emption Priority - * bit[6:5] - Subpriority - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint8_t priority) -{ - NVIC->IPRIOR[(uint32_t)(IRQn)] = priority; -} - -/********************************************************************* - * @fn __WFI - * - * @brief Wait for Interrupt - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void __WFI(void) -{ - NVIC->SCTLR &= ~(1<<3); // wfi - asm volatile ("wfi"); -} - -/********************************************************************* - * @fn _SEV - * - * @brief Set Event - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void _SEV(void) -{ - uint32_t t; - - t = NVIC->SCTLR; - NVIC->SCTLR |= (1<<3)|(1<<5); - NVIC->SCTLR = (NVIC->SCTLR & ~(1<<5)) | ( t & (1<<5)); -} - -/********************************************************************* - * @fn _WFE - * - * @brief Wait for Events - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void _WFE(void) -{ - NVIC->SCTLR |= (1<<3); - asm volatile ("wfi"); -} - -/********************************************************************* - * @fn __WFE - * - * @brief Wait for Events - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void __WFE(void) -{ - _SEV(); - _WFE(); - _WFE(); -} - -/********************************************************************* - * @fn SetVTFIRQ - * - * @brief Set VTF Interrupt - * - * @param addr - VTF interrupt service function base address. - * IRQn - Interrupt Numbers - * num - VTF Interrupt Numbers - * NewState - DISABLE or ENABLE - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void SetVTFIRQ(uint32_t addr, IRQn_Type IRQn, uint8_t num, FunctionalState NewState){ - if(num > 3) return ; - - if (NewState != DISABLE) - { - NVIC->VTFIDR[num] = IRQn; - NVIC->VTFADDR[num] = ((addr&0xFFFFFFFE)|0x1); - } - else{ - NVIC->VTFIDR[num] = IRQn; - NVIC->VTFADDR[num] = ((addr&0xFFFFFFFE)&(~0x1)); - } -} - -/********************************************************************* - * @fn NVIC_SystemReset - * - * @brief Initiate a system reset request - * - * @return none - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void NVIC_SystemReset(void) -{ - NVIC->CFGR = NVIC_KEY3|(1<<7); -} - -/********************************************************************* - * @fn __AMOADD_W - * - * @brief Atomic Add with 32bit value - * Atomically ADD 32bit value with value in memory using amoadd.d. - * - * @param addr - Address pointer to data, address need to be 4byte aligned - * value - value to be ADDed - * - * @return return memory value + add value - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE int32_t __AMOADD_W(volatile int32_t *addr, int32_t value) -{ - int32_t result; - - __asm volatile ("amoadd.w %0, %2, %1" : \ - "=r"(result), "+A"(*addr) : "r"(value) : "memory"); - return *addr; -} - -/********************************************************************* - * @fn __AMOAND_W - * - * @brief Atomic And with 32bit value - * Atomically AND 32bit value with value in memory using amoand.d. - * - * @param addr - Address pointer to data, address need to be 4byte aligned - * value - value to be ANDed - * - * @return return memory value & and value - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE int32_t __AMOAND_W(volatile int32_t *addr, int32_t value) -{ - int32_t result; - - __asm volatile ("amoand.w %0, %2, %1" : \ - "=r"(result), "+A"(*addr) : "r"(value) : "memory"); - return *addr; -} - -/********************************************************************* - * @fn __AMOMAX_W - * - * @brief Atomic signed MAX with 32bit value - * Atomically signed max compare 32bit value with value in memory using amomax.d. - * - * @param addr - Address pointer to data, address need to be 4byte aligned - * value - value to be compared - * - * @return the bigger value - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE int32_t __AMOMAX_W(volatile int32_t *addr, int32_t value) -{ - int32_t result; - - __asm volatile ("amomax.w %0, %2, %1" : \ - "=r"(result), "+A"(*addr) : "r"(value) : "memory"); - return *addr; -} - -/********************************************************************* - * @fn __AMOMAXU_W - * - * @brief Atomic unsigned MAX with 32bit value - * Atomically unsigned max compare 32bit value with value in memory using amomaxu.d. - * - * @param addr - Address pointer to data, address need to be 4byte aligned - * value - value to be compared - * - * @return return the bigger value - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE uint32_t __AMOMAXU_W(volatile uint32_t *addr, uint32_t value) -{ - uint32_t result; - - __asm volatile ("amomaxu.w %0, %2, %1" : \ - "=r"(result), "+A"(*addr) : "r"(value) : "memory"); - return *addr; -} - -/********************************************************************* - * @fn __AMOMIN_W - * - * @brief Atomic signed MIN with 32bit value - * Atomically signed min compare 32bit value with value in memory using amomin.d. - * - * @param addr - Address pointer to data, address need to be 4byte aligned - * value - value to be compared - * - * @return the smaller value - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE int32_t __AMOMIN_W(volatile int32_t *addr, int32_t value) -{ - int32_t result; - - __asm volatile ("amomin.w %0, %2, %1" : \ - "=r"(result), "+A"(*addr) : "r"(value) : "memory"); - return *addr; -} - -/********************************************************************* - * @fn __AMOMINU_W - * - * @brief Atomic unsigned MIN with 32bit value - * Atomically unsigned min compare 32bit value with value in memory using amominu.d. - * - * @param addr - Address pointer to data, address need to be 4byte aligned - * value - value to be compared - * - * @return the smaller value - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE uint32_t __AMOMINU_W(volatile uint32_t *addr, uint32_t value) -{ - uint32_t result; - - __asm volatile ("amominu.w %0, %2, %1" : \ - "=r"(result), "+A"(*addr) : "r"(value) : "memory"); - return *addr; -} - -/********************************************************************* - * @fn __AMOOR_W - * - * @brief Atomic OR with 32bit value - * Atomically OR 32bit value with value in memory using amoor.d. - * - * @param addr - Address pointer to data, address need to be 4byte aligned - * value - value to be ORed - * - * @return return memory value | and value - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE int32_t __AMOOR_W(volatile int32_t *addr, int32_t value) -{ - int32_t result; - - __asm volatile ("amoor.w %0, %2, %1" : \ - "=r"(result), "+A"(*addr) : "r"(value) : "memory"); - return *addr; -} - -/********************************************************************* - * @fn __AMOSWAP_W - * - * @brief Atomically swap new 32bit value into memory using amoswap.d. - * - * @param addr - Address pointer to data, address need to be 4byte aligned - * newval - New value to be stored into the address - * - * @return return the original value in memory - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE uint32_t __AMOSWAP_W(volatile uint32_t *addr, uint32_t newval) -{ - uint32_t result; - - __asm volatile ("amoswap.w %0, %2, %1" : \ - "=r"(result), "+A"(*addr) : "r"(newval) : "memory"); - return result; -} - -/********************************************************************* - * @fn __AMOXOR_W - * - * @brief Atomic XOR with 32bit value - * Atomically XOR 32bit value with value in memory using amoxor.d. - * - * @param addr - Address pointer to data, address need to be 4byte aligned - * value - value to be XORed - * - * @return return memory value ^ and value - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE int32_t __AMOXOR_W(volatile int32_t *addr, int32_t value) -{ - int32_t result; - - __asm volatile ("amoxor.w %0, %2, %1" : \ - "=r"(result), "+A"(*addr) : "r"(value) : "memory"); - return *addr; -} - -/* Core_Exported_Functions */ -extern uint32_t __get_MSTATUS(void); -extern void __set_MSTATUS(uint32_t value); -extern uint32_t __get_MISA(void); -extern void __set_MISA(uint32_t value); -extern uint32_t __get_MTVEC(void); -extern void __set_MTVEC(uint32_t value); -extern uint32_t __get_MSCRATCH(void); -extern void __set_MSCRATCH(uint32_t value); -extern uint32_t __get_MEPC(void); -extern void __set_MEPC(uint32_t value); -extern uint32_t __get_MCAUSE(void); -extern void __set_MCAUSE(uint32_t value); -extern uint32_t __get_MTVAL(void); -extern void __set_MTVAL(uint32_t value); -extern uint32_t __get_MVENDORID(void); -extern uint32_t __get_MARCHID(void); -extern uint32_t __get_MIMPID(void); -extern uint32_t __get_MHARTID(void); -extern uint32_t __get_SP(void); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/hw/bsp/ch32v20x/family.c b/hw/bsp/ch32v20x/family.c index c32cfd46e..43dd7e032 100644 --- a/hw/bsp/ch32v20x/family.c +++ b/hw/bsp/ch32v20x/family.c @@ -1,6 +1,18 @@ #include + +// https://github.com/openwch/ch32v307/pull/90 +// https://github.com/openwch/ch32v20x/pull/12 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + #include "ch32v20x.h" +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + #include "bsp/board_api.h" #include "board.h" @@ -50,7 +62,6 @@ void USBWakeUp_IRQHandler(void) { #if CFG_TUSB_OS == OPT_OS_NONE - volatile uint32_t system_ticks = 0; __attribute__((interrupt)) @@ -72,7 +83,6 @@ uint32_t SysTick_Config(uint32_t ticks) { uint32_t board_millis(void) { return system_ticks; } - #endif void board_init(void) { @@ -84,28 +94,17 @@ void board_init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); - uint8_t usb_div; - switch (SystemCoreClock) { - case 48000000: usb_div = RCC_USBCLKSource_PLLCLK_Div1; break; - case 96000000: usb_div = RCC_USBCLKSource_PLLCLK_Div2; break; - case 144000000: usb_div = RCC_USBCLKSource_PLLCLK_Div3; break; - default: TU_ASSERT(0,); break; - } - RCC_USBCLKConfig(usb_div); - RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE); // FSDEV - RCC_AHBPeriphClockCmd(RCC_AHBPeriph_OTG_FS, ENABLE); // USB FS - GPIO_InitTypeDef GPIO_InitStructure = { .GPIO_Pin = LED_PIN, .GPIO_Mode = GPIO_Mode_Out_OD, - .GPIO_Speed = GPIO_Speed_50MHz, + .GPIO_Speed = GPIO_Speed_10MHz, }; GPIO_Init(LED_PORT, &GPIO_InitStructure); - // UART TX is PA9 - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); +#ifdef UART_DEV + UART_CLOCK_EN(); GPIO_InitTypeDef usart_init = { - .GPIO_Pin = GPIO_Pin_9, + .GPIO_Pin = UART_TX_PIN, .GPIO_Speed = GPIO_Speed_50MHz, .GPIO_Mode = GPIO_Mode_AF_PP, }; @@ -119,11 +118,51 @@ void board_init(void) { .USART_Mode = USART_Mode_Tx, .USART_HardwareFlowControl = USART_HardwareFlowControl_None, }; - USART_Init(USART1, &usart); - USART_Cmd(USART1, ENABLE); + USART_Init(UART_DEV, &usart); + USART_Cmd(UART_DEV, ENABLE); +#endif + + // USB init + uint8_t usb_div; + switch (SystemCoreClock) { + case 48000000: usb_div = RCC_USBCLKSource_PLLCLK_Div1; break; + case 96000000: usb_div = RCC_USBCLKSource_PLLCLK_Div2; break; + case 144000000: usb_div = RCC_USBCLKSource_PLLCLK_Div3; break; + default: TU_ASSERT(0,); break; + } + RCC_USBCLKConfig(usb_div); + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE); // FSDEV + RCC_AHBPeriphClockCmd(RCC_AHBPeriph_OTG_FS, ENABLE); // USB FS __enable_irq(); - board_delay(2); +} + +void board_reset_to_bootloader(void) { +// board_led_write(true); +// +// __disable_irq(); +// +// #if CFG_TUD_ENABLED +// tud_deinit(0); +// RCC_APB1PeriphResetCmd(RCC_APB1Periph_USB, ENABLE); +// RCC_APB1PeriphResetCmd(RCC_APB1Periph_USB, DISABLE); +// #endif +// +// SysTick->CTLR = 0; +// for (int i = WWDG_IRQn; i< DMA1_Channel8_IRQn; i++) { +// NVIC_DisableIRQ(i); +// } +// +// __enable_irq(); +// +// // define function pointer to BOOT ROM address +// void (*bootloader_entry)(void) = (void (*)(void))0x1FFF8000; +// +// bootloader_entry(); +// +// board_led_write(false); + + // while(1) { } } void board_led_write(bool state) { @@ -134,6 +173,17 @@ uint32_t board_button_read(void) { return false; } +size_t board_get_unique_id(uint8_t id[], size_t max_len) { + (void) max_len; + volatile uint32_t* ch32_uuid = ((volatile uint32_t*) 0x1FFFF7E8UL); + uint32_t* serial_32 = (uint32_t*) (uintptr_t) id; + serial_32[0] = ch32_uuid[0]; + serial_32[1] = ch32_uuid[1]; + serial_32[2] = ch32_uuid[2]; + + return 12; +} + int board_uart_read(uint8_t *buf, int len) { (void) buf; (void) len; @@ -141,11 +191,19 @@ int board_uart_read(uint8_t *buf, int len) { } int board_uart_write(void const *buf, int len) { +#ifdef UART_DEV const char *bufc = (const char *) buf; for (int i = 0; i < len; i++) { - while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); - USART_SendData(USART1, *bufc++); + while (USART_GetFlagStatus(UART_DEV, USART_FLAG_TC) == RESET); + USART_SendData(UART_DEV, *bufc++); } +#else + (void) buf; (void) len; +#endif return len; } + +//-------------------------------------------------------------------- +// Neopixel +//-------------------------------------------------------------------- diff --git a/hw/bsp/ch32v20x/family.cmake b/hw/bsp/ch32v20x/family.cmake index 1d574fdf8..380ef190d 100644 --- a/hw/bsp/ch32v20x/family.cmake +++ b/hw/bsp/ch32v20x/family.cmake @@ -41,6 +41,7 @@ function(add_board_target BOARD_TARGET) add_library(${BOARD_TARGET} STATIC ${SDK_SRC_DIR}/Core/core_riscv.c + ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_flash.c ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_gpio.c ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_misc.c ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_rcc.c @@ -49,6 +50,7 @@ function(add_board_target BOARD_TARGET) ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}} ) target_include_directories(${BOARD_TARGET} PUBLIC + ${SDK_SRC_DIR}/Core ${SDK_SRC_DIR}/Peripheral/inc ${CMAKE_CURRENT_FUNCTION_LIST_DIR} ) @@ -70,19 +72,25 @@ function(add_board_target BOARD_TARGET) update_board(${BOARD_TARGET}) + if (LD_FLASH_SIZE STREQUAL 224K) + target_compile_definitions(${BOARD_TARGET} PUBLIC + CH32_FLASH_ENHANCE_READ_MODE=1 + ) + endif() + if (CMAKE_C_COMPILER_ID STREQUAL "GNU") target_compile_options(${BOARD_TARGET} PUBLIC -mcmodel=medany ) target_link_options(${BOARD_TARGET} PUBLIC - "LINKER:--script=${LD_FILE_GNU}" - -Wl,--defsym=__flash_size=${LD_FLASH_SIZE} - -Wl,--defsym=__ram_size=${LD_RAM_SIZE} -nostartfiles --specs=nosys.specs --specs=nano.specs + -Wl,--defsym=__FLASH_SIZE=${LD_FLASH_SIZE} + -Wl,--defsym=__RAM_SIZE=${LD_RAM_SIZE} + "LINKER:--script=${LD_FILE_GNU}" ) elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang") - message(FATAL_ERROR "Clang is not supported for MSP432E4") + message(FATAL_ERROR "Clang is not supported for CH32v") elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR") target_link_options(${BOARD_TARGET} PUBLIC "LINKER:--config=${LD_FILE_IAR}" @@ -129,6 +137,7 @@ function(family_configure_example TARGET RTOS) # Flashing family_add_bin_hex(${TARGET}) family_flash_openocd_wch(${TARGET}) + family_flash_wlink_rs(${TARGET}) #family_add_uf2(${TARGET} ${UF2_FAMILY_ID}) #family_flash_uf2(${TARGET} ${UF2_FAMILY_ID}) diff --git a/hw/bsp/ch32v20x/family.mk b/hw/bsp/ch32v20x/family.mk index 49d4d2feb..08761dc0d 100644 --- a/hw/bsp/ch32v20x/family.mk +++ b/hw/bsp/ch32v20x/family.mk @@ -24,6 +24,9 @@ CFLAGS += \ -DCH32V20x_${MCU_VARIANT} \ -DCFG_TUSB_MCU=OPT_MCU_CH32V20X +# https://github.com/openwch/ch32v20x/pull/12 +CFLAGS += -Wno-error=strict-prototypes + ifeq ($(PORT),0) $(info "Using FSDEV driver") CFLAGS += -DCFG_TUD_WCH_USBIP_FSDEV=1 @@ -42,18 +45,20 @@ SRC_C += \ src/portable/wch/dcd_ch32_usbfs.c \ src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c \ $(SDK_SRC_DIR)/Core/core_riscv.c \ - $(SDK_SRC_DIR)/Peripheral/src/ch32v20x_gpio.c \ - $(SDK_SRC_DIR)/Peripheral/src/ch32v20x_misc.c \ - $(SDK_SRC_DIR)/Peripheral/src/ch32v20x_rcc.c \ - $(SDK_SRC_DIR)/Peripheral/src/ch32v20x_usart.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_gpio.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_misc.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_rcc.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_usart.c \ -SRC_S += $(SDK_SRC_DIR)/Startup/startup_ch32v20x_${MCU_VARIANT}.S +SRC_S += $(SDK_SRC_DIR)/Startup/startup_${CH32_FAMILY}_${MCU_VARIANT}.S INC += \ $(TOP)/$(BOARD_PATH) \ + $(TOP)/$(SDK_SRC_DIR)/Core \ $(TOP)/$(SDK_SRC_DIR)/Peripheral/inc \ FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/RISC-V OPENOCD_WCH_OPTION=-f $(TOP)/$(FAMILY_PATH)/wch-riscv.cfg -flash: flash-openocd-wch +flash: flash-wlink-rs +#flash: flash-openocd-wch diff --git a/hw/bsp/ch32v20x/linker/ch32v20x.ld b/hw/bsp/ch32v20x/linker/ch32v20x.ld index cd5c8dc17..f84808b0d 100644 --- a/hw/bsp/ch32v20x/linker/ch32v20x.ld +++ b/hw/bsp/ch32v20x/linker/ch32v20x.ld @@ -1,17 +1,16 @@ /* Define default values if not already defined */ -__FLASH_SIZE = DEFINED(__flash_size) ? __flash_size : 64K; -__RAM_SIZE = DEFINED(__ram_size) ? __ram_size : 20K; +__flash_size = DEFINED(__FLASH_SIZE) ? __FLASH_SIZE : 64K; +__ram_size = DEFINED(__RAM_SIZE) ? __RAM_SIZE : 20K; +__stack_size = DEFINED(__STACK_SIZE) ? __STACK_SIZE : 2048; MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = __FLASH_SIZE - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = __RAM_SIZE + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = __flash_size + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = __ram_size } ENTRY( _start ) -__stack_size = 2048; - PROVIDE( _stack_size = __stack_size ); SECTIONS diff --git a/hw/bsp/ch32v20x/system_ch32v20x.c b/hw/bsp/ch32v20x/system_ch32v20x.c index bdc0d498f..d32ee8d17 100644 --- a/hw/bsp/ch32v20x/system_ch32v20x.c +++ b/hw/bsp/ch32v20x/system_ch32v20x.c @@ -19,12 +19,12 @@ * If none of the define below is enabled, the HSI is used as System clock source. */ //#define SYSCLK_FREQ_HSE HSE_VALUE -//#define SYSCLK_FREQ_48MHz_HSE 48000000 +// #define SYSCLK_FREQ_48MHz_HSE 48000000 //#define SYSCLK_FREQ_56MHz_HSE 56000000 -//#define SYSCLK_FREQ_72MHz_HSE 72000000 +// #define SYSCLK_FREQ_72MHz_HSE 72000000 // #define SYSCLK_FREQ_96MHz_HSE 96000000 //#define SYSCLK_FREQ_120MHz_HSE 120000000 -#define SYSCLK_FREQ_144MHz_HSE 144000000 +//#define SYSCLK_FREQ_144MHz_HSE 144000000 //#define SYSCLK_FREQ_HSI HSI_VALUE //#define SYSCLK_FREQ_48MHz_HSI 48000000 //#define SYSCLK_FREQ_56MHz_HSI 56000000 @@ -109,6 +109,16 @@ static void SetSysClockTo144_HSI( void ); */ void SystemInit (void) { + // Enable Flash enhance read mode for full 224KB +#if defined(CH32_FLASH_ENHANCE_READ_MODE) && CH32_FLASH_ENHANCE_READ_MODE == 1 + FLASH->KEYR = 0x45670123; // FLASH_Unlock_Fast(); + FLASH->KEYR = 0xCDEF89AB; + + FLASH->CTLR |= (1 << 24); // Enhanced Read Mode + + FLASH->CTLR |= (1 << 15); // FLASH_Lock_Fast(); +#endif + RCC->CTLR |= (uint32_t)0x00000001; RCC->CFGR0 &= (uint32_t)0xF8FF0000; RCC->CTLR &= (uint32_t)0xFEF6FFFF; diff --git a/hw/bsp/ch32v307/core_riscv.h b/hw/bsp/ch32v307/core_riscv.h deleted file mode 100644 index a7ce10a00..000000000 --- a/hw/bsp/ch32v307/core_riscv.h +++ /dev/null @@ -1,379 +0,0 @@ -/********************************** (C) COPYRIGHT ******************************* -* File Name : core_riscv.h -* Author : WCH -* Version : V1.0.0 -* Date : 2021/06/06 -* Description : RISC-V Core Peripheral Access Layer Header File for CH32V30x -* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. -* SPDX-License-Identifier: Apache-2.0 -*******************************************************************************/ -#ifndef __CORE_RISCV_H__ -#define __CORE_RISCV_H__ - -/* IO definitions */ -#ifdef __cplusplus - #define __I volatile /* defines 'read only' permissions */ -#else - #define __I volatile const /* defines 'read only' permissions */ -#endif -#define __O volatile /* defines 'write only' permissions */ -#define __IO volatile /* defines 'read / write' permissions */ - -/* Standard Peripheral Library old types (maintained for legacy purpose) */ -typedef __I uint64_t vuc64; /* Read Only */ -typedef __I uint32_t vuc32; /* Read Only */ -typedef __I uint16_t vuc16; /* Read Only */ -typedef __I uint8_t vuc8; /* Read Only */ - -typedef const uint64_t uc64; /* Read Only */ -typedef const uint32_t uc32; /* Read Only */ -typedef const uint16_t uc16; /* Read Only */ -typedef const uint8_t uc8; /* Read Only */ - -typedef __I int64_t vsc64; /* Read Only */ -typedef __I int32_t vsc32; /* Read Only */ -typedef __I int16_t vsc16; /* Read Only */ -typedef __I int8_t vsc8; /* Read Only */ - -typedef const int64_t sc64; /* Read Only */ -typedef const int32_t sc32; /* Read Only */ -typedef const int16_t sc16; /* Read Only */ -typedef const int8_t sc8; /* Read Only */ - -typedef __IO uint64_t vu64; -typedef __IO uint32_t vu32; -typedef __IO uint16_t vu16; -typedef __IO uint8_t vu8; - -typedef uint64_t u64; -typedef uint32_t u32; -typedef uint16_t u16; -typedef uint8_t u8; - -typedef __IO int64_t vs64; -typedef __IO int32_t vs32; -typedef __IO int16_t vs16; -typedef __IO int8_t vs8; - -typedef int64_t s64; -typedef int32_t s32; -typedef int16_t s16; -typedef int8_t s8; - -typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus; - -typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState; - -typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus; - -#define RV_STATIC_INLINE static inline - -/* memory mapped structure for Program Fast Interrupt Controller (PFIC) */ -typedef struct{ - __I uint32_t ISR[8]; - __I uint32_t IPR[8]; - __IO uint32_t ITHRESDR; - __IO uint32_t RESERVED; - __IO uint32_t CFGR; - __I uint32_t GISR; - uint8_t VTFIDR[4]; - uint8_t RESERVED0[12]; - __IO uint32_t VTFADDR[4]; - uint8_t RESERVED1[0x90]; - __O uint32_t IENR[8]; - uint8_t RESERVED2[0x60]; - __O uint32_t IRER[8]; - uint8_t RESERVED3[0x60]; - __O uint32_t IPSR[8]; - uint8_t RESERVED4[0x60]; - __O uint32_t IPRR[8]; - uint8_t RESERVED5[0x60]; - __IO uint32_t IACTR[8]; - uint8_t RESERVED6[0xE0]; - __IO uint8_t IPRIOR[256]; - uint8_t RESERVED7[0x810]; - __IO uint32_t SCTLR; -}PFIC_Type; - -/* memory mapped structure for SysTick */ -typedef struct -{ - __IO u32 CTLR; - __IO u32 SR; - __IO u64 CNT; - __IO u64 CMP; -}SysTick_Type; - - -#define PFIC ((PFIC_Type *) 0xE000E000 ) -#define NVIC PFIC -#define NVIC_KEY1 ((uint32_t)0xFA050000) -#define NVIC_KEY2 ((uint32_t)0xBCAF0000) -#define NVIC_KEY3 ((uint32_t)0xBEEF0000) - -#define SysTick ((SysTick_Type *) 0xE000F000) - - -/********************************************************************* - * @fn __enable_irq - * - * @brief Enable Global Interrupt - * - * @return none - */ -RV_STATIC_INLINE void __enable_irq(void) -{ - __asm volatile ("csrw 0x800, %0" : : "r" (0x6088) ); -} - -/********************************************************************* - * @fn __disable_irq - * - * @brief Disable Global Interrupt - * - * @return none - */ -RV_STATIC_INLINE void __disable_irq(void) -{ - __asm volatile ("csrw 0x800, %0" : : "r" (0x6000) ); -} - -/********************************************************************* - * @fn __NOP - * - * @brief nop - * - * @return none - */ -RV_STATIC_INLINE void __NOP(void) -{ - __asm volatile ("nop"); -} - -/********************************************************************* - * @fn NVIC_EnableIRQ - * - * @brief Enable Interrupt - * - * @param IRQn: Interrupt Numbers - * - * @return none - */ -RV_STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) -{ - NVIC->IENR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); -} - -/********************************************************************* - * @fn NVIC_DisableIRQ - * - * @brief Disable Interrupt - * - * @param IRQn: Interrupt Numbers - * - * @return none - */ -RV_STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) -{ - NVIC->IRER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); -} - -/********************************************************************* - * @fn NVIC_GetStatusIRQ - * - * @brief Get Interrupt Enable State - * - * @param IRQn: Interrupt Numbers - * - * @return 1 - Interrupt Enable - * 0 - Interrupt Disable - */ -RV_STATIC_INLINE uint32_t NVIC_GetStatusIRQ(IRQn_Type IRQn) -{ - return((uint32_t) ((NVIC->ISR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); -} - -/********************************************************************* - * @fn NVIC_GetPendingIRQ - * - * @brief Get Interrupt Pending State - * - * @param IRQn: Interrupt Numbers - * - * @return 1 - Interrupt Pending Enable - * 0 - Interrupt Pending Disable - */ -RV_STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) -{ - return((uint32_t) ((NVIC->IPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); -} - -/********************************************************************* - * @fn NVIC_SetPendingIRQ - * - * @brief Set Interrupt Pending - * - * @param IRQn: Interrupt Numbers - * - * @return None - */ -RV_STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) -{ - NVIC->IPSR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); -} - -/********************************************************************* - * @fn NVIC_ClearPendingIRQ - * - * @brief Clear Interrupt Pending - * - * @param IRQn: Interrupt Numbers - * - * @return None - */ -RV_STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) -{ - NVIC->IPRR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); -} - -/********************************************************************* - * @fn NVIC_GetActive - * - * @brief Get Interrupt Active State - * - * @param IRQn: Interrupt Numbers - * - * @return 1 - Interrupt Active - * 0 - Interrupt No Active - */ -RV_STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) -{ - return((uint32_t)((NVIC->IACTR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); -} - -/********************************************************************* - * @fn NVIC_SetPriority - * - * @brief Set Interrupt Priority - * - * @param IRQn - Interrupt Numbers - * priority - - * bit7 - pre-emption priority - * bit6~bit4 - subpriority - * @return None - */ -RV_STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint8_t priority) -{ - NVIC->IPRIOR[(uint32_t)(IRQn)] = priority; -} - -/********************************************************************* - * @fn __WFI - * - * @brief Wait for Interrupt - * - * @return None - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void __WFI(void) -{ - NVIC->SCTLR &= ~(1<<3); // wfi - asm volatile ("wfi"); -} - -/********************************************************************* - * @fn __WFE - * - * @brief Wait for Events - * - * @return None - */ -__attribute__( ( always_inline ) ) RV_STATIC_INLINE void __WFE(void) -{ - uint32_t t; - - t = NVIC->SCTLR; - NVIC->SCTLR |= (1<<3)|(1<<5); // (wfi->wfe)+(__sev) - NVIC->SCTLR = (NVIC->SCTLR & ~(1<<5)) | ( t & (1<<5)); - asm volatile ("wfi"); - asm volatile ("wfi"); -} - -/********************************************************************* - * @fn SetVTFIRQ - * - * @brief Set VTF Interrupt - * - * @param add - VTF interrupt service function base address. - * IRQn -Interrupt Numbers - * num - VTF Interrupt Numbers - * NewState - DISABLE or ENABLE - * @return None - */ -RV_STATIC_INLINE void SetVTFIRQ(uint32_t addr, IRQn_Type IRQn, uint8_t num, FunctionalState NewState){ - if(num > 3) return ; - - if (NewState != DISABLE) - { - NVIC->VTFIDR[num] = IRQn; - NVIC->VTFADDR[num] = ((addr&0xFFFFFFFE)|0x1); - } - else{ - NVIC->VTFIDR[num] = IRQn; - NVIC->VTFADDR[num] = ((addr&0xFFFFFFFE)&(~0x1)); - } -} - -/********************************************************************* - * @fn NVIC_SystemReset - * - * @brief Initiate a system reset request - * - * @return None - */ -RV_STATIC_INLINE void NVIC_SystemReset(void) -{ - NVIC->CFGR = NVIC_KEY3|(1<<7); -} - - -/* Core_Exported_Functions */ -extern uint32_t __get_FFLAGS(void); -extern void __set_FFLAGS(uint32_t value); -extern uint32_t __get_FRM(void); -extern void __set_FRM(uint32_t value); -extern uint32_t __get_FCSR(void); -extern void __set_FCSR(uint32_t value); -extern uint32_t __get_MSTATUS(void); -extern void __set_MSTATUS(uint32_t value); -extern uint32_t __get_MISA(void); -extern void __set_MISA(uint32_t value); -extern uint32_t __get_MIE(void); -extern void __set_MIE(uint32_t value); -extern uint32_t __get_MTVEC(void); -extern void __set_MTVEC(uint32_t value); -extern uint32_t __get_MSCRATCH(void); -extern void __set_MSCRATCH(uint32_t value); -extern uint32_t __get_MEPC(void); -extern void __set_MEPC(uint32_t value); -extern uint32_t __get_MCAUSE(void); -extern void __set_MCAUSE(uint32_t value); -extern uint32_t __get_MTVAL(void); -extern void __set_MTVAL(uint32_t value); -extern uint32_t __get_MIP(void); -extern void __set_MIP(uint32_t value); -extern uint32_t __get_MCYCLE(void); -extern void __set_MCYCLE(uint32_t value); -extern uint32_t __get_MCYCLEH(void); -extern void __set_MCYCLEH(uint32_t value); -extern uint32_t __get_MINSTRET(void); -extern void __set_MINSTRET(uint32_t value); -extern uint32_t __get_MINSTRETH(void); -extern void __set_MINSTRETH(uint32_t value); -extern uint32_t __get_MVENDORID(void); -extern uint32_t __get_MARCHID(void); -extern uint32_t __get_MIMPID(void); -extern uint32_t __get_MHARTID(void); -extern uint32_t __get_SP(void); - - -#endif diff --git a/hw/bsp/ch32v307/debug_uart.c b/hw/bsp/ch32v307/debug_uart.c index fbabeeadc..2fd3a9d64 100644 --- a/hw/bsp/ch32v307/debug_uart.c +++ b/hw/bsp/ch32v307/debug_uart.c @@ -25,8 +25,17 @@ */ #include "debug_uart.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + #include +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif #define UART_RINGBUFFER_SIZE_TX 128 #define UART_RINGBUFFER_MASK_TX (UART_RINGBUFFER_SIZE_TX-1) diff --git a/hw/bsp/ch32v307/family.c b/hw/bsp/ch32v307/family.c index 0846b10a9..adf2dbea5 100644 --- a/hw/bsp/ch32v307/family.c +++ b/hw/bsp/ch32v307/family.c @@ -25,9 +25,22 @@ */ #include "stdio.h" + +// https://github.com/openwch/ch32v307/pull/90 +// https://github.com/openwch/ch32v20x/pull/12 + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + #include "debug_uart.h" #include "ch32v30x.h" +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + #include "bsp/board_api.h" #include "board.h" diff --git a/hw/bsp/ch32v307/family.cmake b/hw/bsp/ch32v307/family.cmake index af26bfc31..d603af62d 100644 --- a/hw/bsp/ch32v307/family.cmake +++ b/hw/bsp/ch32v307/family.cmake @@ -49,6 +49,7 @@ function(add_board_target BOARD_TARGET) ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}} ) target_include_directories(${BOARD_TARGET} PUBLIC + ${SDK_SRC_DIR}/Core ${SDK_SRC_DIR}/Peripheral/inc ${CMAKE_CURRENT_FUNCTION_LIST_DIR} ) diff --git a/hw/bsp/ch32v307/family.mk b/hw/bsp/ch32v307/family.mk index 30a070a9b..bf2732106 100644 --- a/hw/bsp/ch32v307/family.mk +++ b/hw/bsp/ch32v307/family.mk @@ -25,6 +25,9 @@ CFLAGS += \ -fsigned-char \ -DCFG_TUSB_MCU=OPT_MCU_CH32V307 \ +# https://github.com/openwch/ch32v307/pull/90 +CFLAGS += -Wno-error=strict-prototypes + ifeq ($(SPEED),high) $(info "Using USBHS driver for HighSpeed mode") CFLAGS += -DCFG_TUD_WCH_USBIP_USBHS=1 @@ -51,6 +54,7 @@ SRC_S += \ INC += \ $(TOP)/$(BOARD_PATH) \ + $(TOP)/$(SDK_SRC_DIR)/Core \ $(TOP)/$(SDK_SRC_DIR)/Peripheral/inc # For freeRTOS port source diff --git a/hw/bsp/da14695_dk_usb/da14695_dk_usb.c b/hw/bsp/da14695_dk_usb/da14695_dk_usb.c deleted file mode 100644 index 667b83de3..000000000 --- a/hw/bsp/da14695_dk_usb/da14695_dk_usb.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Jerzy Kasenberg - * - * 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. - */ - -#include "bsp/board_api.h" -#include -#include - -//--------------------------------------------------------------------+ -// Forward USB interrupt events to TinyUSB IRQ Handler -//--------------------------------------------------------------------+ -void USB_IRQHandler(void) -{ - tud_int_handler(0); -} - -//--------------------------------------------------------------------+ -// MACRO TYPEDEF CONSTANT ENUM -//--------------------------------------------------------------------+ - -#define LED_PIN 33 // P1.1 -#define LED_STATE_ON 1 -#define LED_STATE_OFF (1-LED_STATE_ON) - -#define BUTTON_PIN 6 - -void UnhandledIRQ(void) -{ - CRG_TOP->SYS_CTRL_REG = 0x80; - __BKPT(1); - while(1); -} - -// DA146xx driver function that must be called whenever VBUS changes. -extern void tusb_vbus_changed(bool present); - -void board_init(void) -{ - // LED - hal_gpio_init_out(LED_PIN, LED_STATE_ON); - - hal_gpio_init_out(1, 0); - hal_gpio_init_out(2, 0); - hal_gpio_init_out(3, 0); - hal_gpio_init_out(4, 0); - hal_gpio_init_out(5, 0); - - // Button - hal_gpio_init_in(BUTTON_PIN, HAL_GPIO_PULL_DOWN); - - // 1ms tick timer - SysTick_Config(SystemCoreClock / 1000); - -#if CFG_TUD_ENABLED - // This board is USB powered there is no need to monitor - // VBUS line. Notify driver that VBUS is present. - tusb_vbus_changed(true); - - /* Setup USB IRQ */ - NVIC_SetPriority(USB_IRQn, 2); - NVIC_EnableIRQ(USB_IRQn); - - /* Use PLL96 / 2 clock not HCLK */ - CRG_TOP->CLK_CTRL_REG &= ~CRG_TOP_CLK_CTRL_REG_USB_CLK_SRC_Msk; - - mcu_gpio_set_pin_function(14, MCU_GPIO_MODE_INPUT, MCU_GPIO_FUNC_USB); - mcu_gpio_set_pin_function(15, MCU_GPIO_MODE_INPUT, MCU_GPIO_FUNC_USB); -#endif -} - -//--------------------------------------------------------------------+ -// Board porting API -//--------------------------------------------------------------------+ - -void board_led_write(bool state) -{ - hal_gpio_write(LED_PIN, state ? LED_STATE_ON : LED_STATE_OFF); -} - -uint32_t board_button_read(void) -{ - // button is active HIGH - return hal_gpio_read(BUTTON_PIN); -} - -int board_uart_read(uint8_t* buf, int len) -{ - (void)buf; - (void)len; - return 0; -} - -int board_uart_write(void const * buf, int len) -{ - (void)buf; - (void)len; - - return 0; -} - -#if CFG_TUSB_OS == OPT_OS_NONE -volatile uint32_t system_ticks = 0; -void SysTick_Handler(void) -{ - system_ticks++; -} - -uint32_t board_millis(void) -{ - return system_ticks; -} -#endif diff --git a/hw/bsp/da1469x/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/da1469x/FreeRTOSConfig/FreeRTOSConfig.h new file mode 100644 index 000000000..4d4379a6f --- /dev/null +++ b/hw/bsp/da1469x/FreeRTOSConfig/FreeRTOSConfig.h @@ -0,0 +1,150 @@ +/* + * FreeRTOS Kernel V10.0.0 + * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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. If you wish to use our Amazon + * FreeRTOS name, please do so in a fair use way that does not cause confusion. + * + * 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. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html. + *----------------------------------------------------------*/ + +// skip if included from IAR assembler +#ifndef __IASMARM__ + #include "DA1469xAB.h" +#endif + +/* Cortex M23/M33 port configuration. */ +#define configENABLE_MPU 0 +#define configENABLE_FPU 1 +#define configENABLE_TRUSTZONE 0 +#define configMINIMAL_SECURE_STACK_SIZE (1024) +#define configRUN_FREERTOS_SECURE_ONLY 1 + +#define configUSE_PREEMPTION 1 +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +#define configCPU_CLOCK_HZ SystemCoreClock +#define configTICK_RATE_HZ ( 1000 ) +#define configMAX_PRIORITIES ( 5 ) +#define configMINIMAL_STACK_SIZE ( 128 ) +#define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) +#define configMAX_TASK_NAME_LEN 16 +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configQUEUE_REGISTRY_SIZE 4 +#define configUSE_QUEUE_SETS 0 +#define configUSE_TIME_SLICING 0 +#define configUSE_NEWLIB_REENTRANT 0 +#define configENABLE_BACKWARD_COMPATIBILITY 1 +#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 + +#define configSUPPORT_STATIC_ALLOCATION 1 +#define configSUPPORT_DYNAMIC_ALLOCATION 0 + +/* Hook function related definitions. */ +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configUSE_MALLOC_FAILED_HOOK 0 // cause nested extern warning +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configCHECK_HANDLER_INSTALLATION 0 + +/* Run time and task stats gathering related definitions. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define configRECORD_STACK_HIGH_ADDRESS 1 +#define configUSE_TRACE_FACILITY 1 // legacy trace +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 + +/* Co-routine definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES 2 + +/* Software timer related definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-2) +#define configTIMER_QUEUE_LENGTH 32 +#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE + +/* Optional functions - most linkers will remove unused functions anyway. */ +#define INCLUDE_vTaskPrioritySet 0 +#define INCLUDE_uxTaskPriorityGet 0 +#define INCLUDE_vTaskDelete 0 +#define INCLUDE_vTaskSuspend 1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY +#define INCLUDE_xResumeFromISR 0 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 0 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 0 +#define INCLUDE_xTaskGetIdleTaskHandle 0 +#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0 +#define INCLUDE_pcTaskGetTaskName 0 +#define INCLUDE_eTaskGetState 0 +#define INCLUDE_xEventGroupSetBitFromISR 0 +#define INCLUDE_xTimerPendFunctionCall 0 + +/* FreeRTOS hooks to NVIC vectors */ +#define xPortPendSVHandler PendSV_Handler +#define xPortSysTickHandler SysTick_Handler +#define vPortSVCHandler SVC_Handler + +//--------------------------------------------------------------------+ +// Interrupt nesting behavior configuration. +//--------------------------------------------------------------------+ + +// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header +#define configPRIO_BITS 4 + +/* The lowest interrupt priority that can be used in a call to a "set priority" function. */ +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY ((1< #include +#define LED_STATE_OFF (1-LED_STATE_ON) + //--------------------------------------------------------------------+ // Forward USB interrupt events to TinyUSB IRQ Handler //--------------------------------------------------------------------+ -void USB_IRQHandler(void) -{ +void USB_IRQHandler(void) { tud_int_handler(0); } -#if CFG_TUD_ENABLED -// DA146xx driver function that must be called whenever VBUS changes +// DA146xx driver function that must be called whenever VBUS changes. extern void tusb_vbus_changed(bool present); +#if defined(NEED_VBUS_MONITOR) && CFG_TUD_ENABLED // VBUS change interrupt handler -void VBUS_IRQHandler(void) -{ +void VBUS_IRQHandler(void) { bool present = (CRG_TOP->ANA_STATUS_REG & CRG_TOP_ANA_STATUS_REG_VBUS_AVAILABLE_Msk) != 0; // Clear VBUS interrupt CRG_TOP->VBUS_IRQ_CLEAR_REG = 1; @@ -54,22 +55,13 @@ void VBUS_IRQHandler(void) //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM //--------------------------------------------------------------------+ - -#define LED_PIN 33 -#define LED_STATE_ON 1 -#define LED_STATE_OFF 0 - -#define BUTTON_PIN 6 - -void UnhandledIRQ(void) -{ +void UnhandledIRQ(void) { CRG_TOP->SYS_CTRL_REG = 0x80; __BKPT(1); - while(1); + while (1); } -void board_init(void) -{ +void board_init(void) { // LED hal_gpio_init_out(LED_PIN, LED_STATE_ON); @@ -80,12 +72,13 @@ void board_init(void) hal_gpio_init_out(5, 0); // Button - hal_gpio_init_in(BUTTON_PIN, HAL_GPIO_PULL_UP); + hal_gpio_init_in(BUTTON_PIN, BUTTON_STATE_ACTIVE ? HAL_GPIO_PULL_DOWN : HAL_GPIO_PULL_UP); // 1ms tick timer SysTick_Config(SystemCoreClock / 1000); #if CFG_TUD_ENABLED + #ifdef NEED_VBUS_MONITOR // Setup interrupt for both connect and disconnect CRG_TOP->VBUS_IRQ_MASK_REG = CRG_TOP_VBUS_IRQ_MASK_REG_VBUS_IRQ_EN_FALL_Msk | CRG_TOP_VBUS_IRQ_MASK_REG_VBUS_IRQ_EN_RISE_Msk; @@ -94,6 +87,10 @@ void board_init(void) // otherwise it could go unnoticed. NVIC_SetPendingIRQ(VBUS_IRQn); NVIC_EnableIRQ(VBUS_IRQn); + #else + // This board is USB powered there is no need to monitor VBUS line. Notify driver that VBUS is present. + tusb_vbus_changed(true); + #endif /* Setup USB IRQ */ NVIC_SetPriority(USB_IRQn, 2); @@ -111,41 +108,35 @@ void board_init(void) // Board porting API //--------------------------------------------------------------------+ -void board_led_write(bool state) -{ +void board_led_write(bool state) { hal_gpio_write(LED_PIN, state ? LED_STATE_ON : LED_STATE_OFF); } -uint32_t board_button_read(void) -{ - // button is active LOW - return hal_gpio_read(BUTTON_PIN) ^ 1; +uint32_t board_button_read(void) { + return BUTTON_STATE_ACTIVE == hal_gpio_read(BUTTON_PIN); } -int board_uart_read(uint8_t* buf, int len) -{ - (void)buf; - (void)len; +int board_uart_read(uint8_t* buf, int len) { + (void) buf; + (void) len; return 0; } -int board_uart_write(void const * buf, int len) -{ - (void)buf; - (void)len; +int board_uart_write(void const* buf, int len) { + (void) buf; + (void) len; return 0; } #if CFG_TUSB_OS == OPT_OS_NONE volatile uint32_t system_ticks = 0; -void SysTick_Handler(void) -{ + +void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) -{ +uint32_t board_millis(void) { return system_ticks; } #endif diff --git a/hw/bsp/da1469x/family.cmake b/hw/bsp/da1469x/family.cmake new file mode 100644 index 000000000..8c89141fe --- /dev/null +++ b/hw/bsp/da1469x/family.cmake @@ -0,0 +1,141 @@ +include_guard() + +set(MCU_DIR ${TOP}/hw/mcu/dialog/da1469x) + +# include board specific +include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake) + +set(CMAKE_SYSTEM_PROCESSOR cortex-m33-nodsp CACHE INTERNAL "System Processor") +set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake) +set(FAMILY_MCUS DA1469X CACHE INTERNAL "") + +#------------------------------------ +# BOARD_TARGET +#------------------------------------ +# only need to be built ONCE for all examples +function(add_board_target BOARD_TARGET) + if (TARGET ${BOARD_TARGET}) + return() + endif () + + if (NOT DEFINED LD_FILE_GNU) + set(LD_FILE_GNU ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/linker/da1469x.ld) + endif () + + if (NOT DEFINED STARTUP_FILE_${CMAKE_C_COMPILER_ID}) + set(STARTUP_FILE_GNU ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/gcc_startup_da1469x.S) + set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU}) + endif () + + add_library(${BOARD_TARGET} STATIC + ${MCU_DIR}/src/system_da1469x.c + ${MCU_DIR}/src/da1469x_clock.c + ${MCU_DIR}/src/hal_gpio.c + ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}} + ) + target_compile_options(${BOARD_TARGET} PUBLIC -mthumb-interwork) + target_compile_definitions(${BOARD_TARGET} PUBLIC + CORE_M33 + CFG_TUD_ENDPOINT0_SIZE=8 + ) + target_include_directories(${BOARD_TARGET} PUBLIC + ${CMAKE_CURRENT_FUNCTION_LIST_DIR} + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD} + ${MCU_DIR}/include + ${MCU_DIR}/SDK_10.0.8.105/sdk/bsp/include + ) + + update_board(${BOARD_TARGET}) + + if (CMAKE_C_COMPILER_ID STREQUAL "GNU") + target_link_options(${BOARD_TARGET} PUBLIC + "LINKER:--script=${LD_FILE_GNU}" + -L${NRFX_DIR}/mdk + --specs=nosys.specs --specs=nano.specs + -nostartfiles + ) + elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang") + target_link_options(${BOARD_TARGET} PUBLIC + "LINKER:--script=${LD_FILE_GNU}" + -L${NRFX_DIR}/mdk + ) + elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR") + target_link_options(${BOARD_TARGET} PUBLIC + "LINKER:--config=${LD_FILE_IAR}" + ) + endif () +endfunction() + + +#------------------------------------ +# Functions +#------------------------------------ + +function(family_flash_jlink_dialog TARGET) + set(JLINKEXE JLinkExe) + set(JLINK_IF swd) + + # mkimage from sdk + set(MKIMAGE $ENV{HOME}/code/tinyusb-mcu-driver/dialog/SDK_10.0.8.105/binaries/mkimage) + + file(GENERATE OUTPUT $/version.h + CONTENT "#define SW_VERSION \"v_1.0.0.1\" +#define SW_VERSION_DATE \"2024-07-17 17:55\"" + ) + + file(GENERATE OUTPUT $/${TARGET}.jlink + CONTENT "r +halt +loadfile $/${TARGET}-image.bin 0x16000000 +r +go +exit" + ) + + add_custom_target(${TARGET}-image + DEPENDS ${TARGET} + COMMAND ${MKIMAGE} da1469x $/${TARGET}.bin $/version.h $/${TARGET}.bin.img + COMMAND cp ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/product_header.dump $/${TARGET}-image.bin + COMMAND cat $/${TARGET}.bin.img >> $/${TARGET}-image.bin + ) + add_custom_target(${TARGET}-jlink + DEPENDS ${TARGET}-image + COMMAND ${JLINKEXE} -device ${JLINK_DEVICE} -if ${JLINK_IF} -JTAGConf -1,-1 -speed auto -CommandFile $/${TARGET}.jlink + ) +endfunction() + + +function(family_configure_example TARGET RTOS) + family_configure_common(${TARGET} ${RTOS}) + + # Board target + add_board_target(board_${BOARD}) + + #---------- Port Specific ---------- + # These files are built for each example since it depends on example's tusb_config.h + target_sources(${TARGET} PUBLIC + # BSP + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c + ) + target_include_directories(${TARGET} PUBLIC + # family, hw, board + ${CMAKE_CURRENT_FUNCTION_LIST_DIR} + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../ + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD} + ) + + # Add TinyUSB target and port source + family_add_tinyusb(${TARGET} OPT_MCU_DA1469X ${RTOS}) + target_sources(${TARGET}-tinyusb PUBLIC + ${TOP}/src/portable/dialog/da146xx/dcd_da146xx.c + ) + target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) + + # Link dependencies + target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb) + + # Flashing + family_add_bin_hex(${TARGET}) + family_flash_jlink_dialog(${TARGET}) +endfunction() diff --git a/hw/bsp/da14695_dk_usb/board.mk b/hw/bsp/da1469x/family.mk similarity index 52% rename from hw/bsp/da14695_dk_usb/board.mk rename to hw/bsp/da1469x/family.mk index 980b1a361..f35fe2cb5 100644 --- a/hw/bsp/da14695_dk_usb/board.mk +++ b/hw/bsp/da1469x/family.mk @@ -1,4 +1,6 @@ -MCU_FAMILY_DIR = hw/mcu/dialog/da1469x +MCU_DIR = hw/mcu/dialog/da1469x + +include $(TOP)/$(BOARD_PATH)/board.mk CFLAGS += \ -flto \ @@ -8,48 +10,52 @@ CFLAGS += \ -mcpu=cortex-m33+nodsp \ -mfloat-abi=hard \ -mfpu=fpv5-sp-d16 \ - -nostdlib \ -DCORE_M33 \ -DCFG_TUSB_MCU=OPT_MCU_DA1469X \ -DCFG_TUD_ENDPOINT0_SIZE=8\ -LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs +LDFLAGS_GCC += \ + -nostdlib \ + --specs=nosys.specs --specs=nano.specs # All source paths should be relative to the top level. -LD_FILE = hw/bsp/$(BOARD)/da1469x.ld +LD_FILE = $(FAMILY_PATH)/linker/da1469x.ld # While this is for da1469x chip, there is chance that da1468x chip family will also work SRC_C += \ src/portable/dialog/da146xx/dcd_da146xx.c \ - $(MCU_FAMILY_DIR)/src/system_da1469x.c \ - $(MCU_FAMILY_DIR)/src/da1469x_clock.c \ - $(MCU_FAMILY_DIR)/src/hal_gpio.c \ + ${MCU_DIR}/src/system_da1469x.c \ + ${MCU_DIR}/src/da1469x_clock.c \ + ${MCU_DIR}/src/hal_gpio.c \ -SRC_S += hw/bsp/$(BOARD)/gcc_startup_da1469x.S +SRC_S += $(FAMILY_PATH)/gcc_startup_da1469x.S INC += \ - $(TOP)/hw/bsp/$(BOARD) \ - $(TOP)/$(MCU_FAMILY_DIR)/include \ - $(TOP)/$(MCU_FAMILY_DIR)/SDK_10.0.8.105/sdk/bsp/include + $(TOP)/$(BOARD_PATH) \ + $(TOP)/${MCU_DIR}/include \ + $(TOP)/${MCU_DIR}/SDK_10.0.8.105/sdk/bsp/include # For freeRTOS port source FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/ARM_CM33_NTZ/non_secure -# For flash-jlink target -JLINK_DEVICE = DA14695 - # flash using jlink but with some twists flash: flash-dialog -flash-dialog: $(BUILD)/$(PROJECT).bin +# SDK_BINARY_PATH is the path to the SDK binary files +SDK_BINARY_PATH = $(HOME)/code/tinyusb-mcu-driver/dialog/SDK_10.0.8.105/binaries +MKIMAGE = $(SDK_BINARY_PATH)/mkimage + +$(BUILD)/$(PROJECT)-image.bin: $(BUILD)/$(PROJECT).bin @echo '#define SW_VERSION "v_1.0.0.1"' >$(BUILD)/version.h - @echo '#define SW_VERSION_DATE "'`date +"%Y-%m-%d %H:%M"`'"' >>$(BUILD)/version.h - mkimage da1469x $(BUILD)/$(PROJECT).bin $(BUILD)/version.h $^.img - cp $(TOP)/hw/bsp/$(BOARD)/product_header.dump $(BUILD)/$(BOARD)-image.bin - cat $^.img >> $(BUILD)/$(BOARD)-image.bin + @echo '#define SW_VERSION_DATE "'`date +"%Y-%m-%d %H:%M"`'"' >> $(BUILD)/version.h + $(MKIMAGE) da1469x $^ $(BUILD)/version.h $^.img + cp $(TOP)/$(FAMILY_PATH)/product_header.dump $(BUILD)/$(PROJECT)-image.bin + cat $^.img >> $(BUILD)/$(PROJECT)-image.bin + +flash-dialog: $(BUILD)/$(PROJECT)-image.bin @echo r > $(BUILD)/$(BOARD).jlink @echo halt >> $(BUILD)/$(BOARD).jlink - @echo loadfile $(BUILD)/$(BOARD)-image.bin 0x16000000 >> $(BUILD)/$(BOARD).jlink + @echo loadfile $^ 0x16000000 >> $(BUILD)/$(BOARD).jlink @echo r >> $(BUILD)/$(BOARD).jlink @echo go >> $(BUILD)/$(BOARD).jlink @echo exit >> $(BUILD)/$(BOARD).jlink diff --git a/hw/bsp/da14695_dk_usb/gcc_startup_da1469x.S b/hw/bsp/da1469x/gcc_startup_da1469x.S similarity index 100% rename from hw/bsp/da14695_dk_usb/gcc_startup_da1469x.S rename to hw/bsp/da1469x/gcc_startup_da1469x.S diff --git a/hw/bsp/da14695_dk_usb/da1469x.ld b/hw/bsp/da1469x/linker/da1469x.ld similarity index 100% rename from hw/bsp/da14695_dk_usb/da1469x.ld rename to hw/bsp/da1469x/linker/da1469x.ld diff --git a/hw/bsp/da14695_dk_usb/product_header.dump b/hw/bsp/da1469x/product_header.dump similarity index 100% rename from hw/bsp/da14695_dk_usb/product_header.dump rename to hw/bsp/da1469x/product_header.dump diff --git a/hw/bsp/da1469x_dk_pro/board.mk b/hw/bsp/da1469x_dk_pro/board.mk deleted file mode 100644 index 5282f93a3..000000000 --- a/hw/bsp/da1469x_dk_pro/board.mk +++ /dev/null @@ -1,56 +0,0 @@ -MCU_FAMILY_DIR = hw/mcu/dialog/da1469x - -CFLAGS += \ - -flto \ - -mthumb \ - -mthumb-interwork \ - -mabi=aapcs \ - -mcpu=cortex-m33+nodsp \ - -mfloat-abi=hard \ - -mfpu=fpv5-sp-d16 \ - -nostdlib \ - -DCORE_M33 \ - -DCFG_TUSB_MCU=OPT_MCU_DA1469X \ - -DCFG_TUD_ENDPOINT0_SIZE=8\ - -LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs - -# All source paths should be relative to the top level. -LD_FILE = hw/bsp/$(BOARD)/da1469x.ld - -# While this is for da1469x chip, there is chance that da1468x chip family will also work -SRC_C += \ - src/portable/dialog/da146xx/dcd_da146xx.c \ - $(MCU_FAMILY_DIR)/src/system_da1469x.c \ - $(MCU_FAMILY_DIR)/src/da1469x_clock.c \ - $(MCU_FAMILY_DIR)/src/hal_gpio.c \ - -SRC_S += hw/bsp/$(BOARD)/gcc_startup_da1469x.S - -INC += \ - $(TOP)/hw/bsp/$(BOARD) \ - $(TOP)/$(MCU_FAMILY_DIR)/include \ - $(TOP)/$(MCU_FAMILY_DIR)/SDK_10.0.8.105/sdk/bsp/include - -# For freeRTOS port source -FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/ARM_CM33_NTZ/non_secure - -# For flash-jlink target -JLINK_DEVICE = DA14699 - -# flash using jlink but with some twists -flash: flash-dialog - -flash-dialog: $(BUILD)/$(PROJECT).bin - @echo '#define SW_VERSION "v_1.0.0.1"' >$(BUILD)/version.h - @echo '#define SW_VERSION_DATE "'`date +"%Y-%m-%d %H:%M"`'"' >>$(BUILD)/version.h - mkimage da1469x $(BUILD)/$(PROJECT).bin $(BUILD)/version.h $^.img - cp $(TOP)/hw/bsp/$(BOARD)/product_header.dump $(BUILD)/$(BOARD)-image.bin - cat $^.img >> $(BUILD)/$(BOARD)-image.bin - @echo r > $(BUILD)/$(BOARD).jlink - @echo halt >> $(BUILD)/$(BOARD).jlink - @echo loadfile $(BUILD)/$(BOARD)-image.bin 0x16000000 >> $(BUILD)/$(BOARD).jlink - @echo r >> $(BUILD)/$(BOARD).jlink - @echo go >> $(BUILD)/$(BOARD).jlink - @echo exit >> $(BUILD)/$(BOARD).jlink - $(JLINKEXE) -device $(JLINK_DEVICE) -if $(JLINK_IF) -JTAGConf -1,-1 -speed auto -CommandFile $(BUILD)/$(BOARD).jlink diff --git a/hw/bsp/da1469x_dk_pro/da1469x.ld b/hw/bsp/da1469x_dk_pro/da1469x.ld deleted file mode 100644 index 8cc1d9d99..000000000 --- a/hw/bsp/da1469x_dk_pro/da1469x.ld +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 License 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. - */ - -MEMORY -{ - /* - * Flash is remapped at 0x0 by 1st stage bootloader, but this is done with - * an offset derived from image header thus it is safer to use remapped - * address space at 0x0 instead of QSPI_M address space at 0x16000000. - * Bootloader partition is 32K, but 9K is currently reserved for product - * header (8K) and image header (1K). - * First 512 bytes of SYSRAM are remapped at 0x0 and used as ISR vector - * (there's no need to reallocate ISR vector) and thus cannot be used by - * application. - */ - - FLASH (r) : ORIGIN = (0x00000000), LENGTH = (1024 * 1024) - RAM (rw) : ORIGIN = (0x20000000), LENGTH = (512 * 1024) -} - -OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") - -/* Linker script to place sections and symbol values. Should be used together - * with other linker script that defines memory regions FLASH and RAM. - * It references following symbols, which must be defined in code: - * Reset_Handler : Entry of reset handler - * - * It defines following symbols, which code can use without definition: - * __exidx_start - * __exidx_end - * __etext - * __data_start__ - * __preinit_array_start - * __preinit_array_end - * __init_array_start - * __init_array_end - * __fini_array_start - * __fini_array_end - * __data_end__ - * __bss_start__ - * __bss_end__ - * __HeapBase - * __HeapLimit - * __StackLimit - * __StackTop - * __stack - * __bssnz_start__ - * __bssnz_end__ - */ -ENTRY(Reset_Handler) - -SECTIONS -{ - __text = .; - - .text : - { - __isr_vector_start = .; - KEEP(*(.isr_vector)) - /* ISR vector shall have exactly 512 bytes */ - . = __isr_vector_start + 0x200; - __isr_vector_end = .; - - *(.text) - *(.text.*) - - *(.libcmac.rom) - - KEEP(*(.init)) - KEEP(*(.fini)) - - /* .ctors */ - *crtbegin.o(.ctors) - *crtbegin?.o(.ctors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) - *(SORT(.ctors.*)) - *(.ctors) - - /* .dtors */ - *crtbegin.o(.dtors) - *crtbegin?.o(.dtors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) - *(SORT(.dtors.*)) - *(.dtors) - - *(.rodata*) - - *(.eh_frame*) - . = ALIGN(4); - } > FLASH - - .ARM.extab : - { - *(.ARM.extab* .gnu.linkonce.armextab.*) - . = ALIGN(4); - } > FLASH - - __exidx_start = .; - .ARM : - { - *(.ARM.exidx* .gnu.linkonce.armexidx.*) - . = ALIGN(4); - } > FLASH - __exidx_end = .; - - .intvect : - { - . = ALIGN(4); - __intvect_start__ = .; - . = . + (__isr_vector_end - __isr_vector_start); - . = ALIGN(4); - } > RAM - - .sleep_state (NOLOAD) : - { - . = ALIGN(4); - *(sleep_state) - } > RAM - - /* This section will be zeroed by RTT package init */ - .rtt (NOLOAD): - { - . = ALIGN(4); - *(.rtt) - . = ALIGN(4); - } > RAM - - __text_ram_addr = LOADADDR(.text_ram); - - .text_ram : - { - . = ALIGN(4); - __text_ram_start__ = .; - *(.text_ram*) - . = ALIGN(4); - __text_ram_end__ = .; - } > RAM AT > FLASH - - __etext = LOADADDR(.data); - - .data : - { - __data_start__ = .; - *(vtable) - *(.data*) - - . = ALIGN(4); - /* preinit data */ - PROVIDE_HIDDEN (__preinit_array_start = .); - *(.preinit_array) - PROVIDE_HIDDEN (__preinit_array_end = .); - - . = ALIGN(4); - /* init data */ - PROVIDE_HIDDEN (__init_array_start = .); - *(SORT(.init_array.*)) - *(.init_array) - PROVIDE_HIDDEN (__init_array_end = .); - - - . = ALIGN(4); - /* finit data */ - PROVIDE_HIDDEN (__fini_array_start = .); - *(SORT(.fini_array.*)) - *(.fini_array) - PROVIDE_HIDDEN (__fini_array_end = .); - - *(.jcr) - . = ALIGN(4); - /* All data end */ - __data_end__ = .; - } > RAM AT > FLASH - - .bssnz : - { - . = ALIGN(4); - __bssnz_start__ = .; - *(.bss.core.nz*) - . = ALIGN(4); - __bssnz_end__ = .; - } > RAM - - .bss : - { - . = ALIGN(4); - __bss_start__ = .; - *(.bss*) - *(COMMON) - . = ALIGN(4); - __bss_end__ = .; - } > RAM - - .cmac (NOLOAD) : - { - . = ALIGN(0x400); - *(.libcmac.ram) - } > RAM - - /* Heap starts after BSS */ - . = ALIGN(8); - __HeapBase = .; - - /* .stack_dummy section doesn't contains any symbols. It is only - * used for linker to calculate size of stack sections, and assign - * values to stack symbols later */ - .stack_dummy (COPY): - { - *(.stack*) - } > RAM - - _ram_start = ORIGIN(RAM); - - /* Set stack top to end of RAM, and stack limit move down by - * size of stack_dummy section */ - __StackTop = ORIGIN(RAM) + LENGTH(RAM); - __StackLimit = __StackTop - SIZEOF(.stack_dummy); - PROVIDE(__stack = __StackTop); - - /* Top of head is the bottom of the stack */ - __HeapLimit = __StackLimit; - end = __HeapLimit; - - /* Check if data + heap + stack exceeds RAM limit */ - ASSERT(__HeapBase <= __HeapLimit, "region RAM overflowed with stack") - - /* Check that intvect is at the beginning of RAM */ - ASSERT(__intvect_start__ == ORIGIN(RAM), "intvect is not at beginning of RAM") -} diff --git a/hw/bsp/da1469x_dk_pro/gcc_startup_da1469x.S b/hw/bsp/da1469x_dk_pro/gcc_startup_da1469x.S deleted file mode 100644 index d47fbcd97..000000000 --- a/hw/bsp/da1469x_dk_pro/gcc_startup_da1469x.S +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 License 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. - */ - - #include "syscfg/syscfg.h" - - .syntax unified - .arch armv7-m - - .section .stack - .align 3 -#ifdef __STACK_SIZE - .equ Stack_Size, __STACK_SIZE -#else - .equ Stack_Size, 0xC00 -#endif - .equ SYS_CTRL_REG, 0x50000024 - .equ CACHE_FLASH_REG, 0x100C0040 - .equ RESET_STAT_REG, 0x500000BC - - .globl __StackTop - .globl __StackLimit -__StackLimit: - .space Stack_Size - .size __StackLimit, . - __StackLimit -__StackTop: - .size __StackTop, . - __StackTop - - .section .heap - .align 3 -#ifdef __HEAP_SIZE - .equ Heap_Size, __HEAP_SIZE -#else - .equ Heap_Size, 0 -#endif - .globl __HeapBase - .globl __HeapLimit -__HeapBase: - .if Heap_Size - .space Heap_Size - .endif - .size __HeapBase, . - __HeapBase -__HeapLimit: - .size __HeapLimit, . - __HeapLimit - - .section .isr_vector - .align 2 - .globl __isr_vector -__isr_vector: - .long __StackTop - .long Reset_Handler - /* Cortex-M33 interrupts */ - .long NMI_Handler - .long HardFault_Handler - .long MemoryManagement_Handler - .long BusFault_Handler - .long UsageFault_Handler - .long SecureFault_Handler - .long 0 /* Reserved */ - .long 0 /* Reserved */ - .long 0 /* Reserved */ - .long SVC_Handler - .long DebugMonitor_Handler - .long 0 /* Reserved */ - .long PendSV_Handler - .long SysTick_Handler - /* DA1469x interrupts */ - .long SENSOR_NODE_IRQHandler - .long DMA_IRQHandler - .long CHARGER_STATE_IRQHandler - .long CHARGER_ERROR_IRQHandler - .long CMAC2SYS_IRQHandler - .long UART_IRQHandler - .long UART2_IRQHandler - .long UART3_IRQHandler - .long I2C_IRQHandler - .long I2C2_IRQHandler - .long SPI_IRQHandler - .long SPI2_IRQHandler - .long PCM_IRQHandler - .long SRC_IN_IRQHandler - .long SRC_OUT_IRQHandler - .long USB_IRQHandler - .long TIMER_IRQHandler - .long TIMER2_IRQHandler - .long RTC_IRQHandler - .long KEY_WKUP_GPIO_IRQHandler - .long PDC_IRQHandler - .long VBUS_IRQHandler - .long MRM_IRQHandler - .long MOTOR_CONTROLLER_IRQHandler - .long TRNG_IRQHandler - .long DCDC_IRQHandler - .long XTAL32M_RDY_IRQHandler - .long ADC_IRQHandler - .long ADC2_IRQHandler - .long CRYPTO_IRQHandler - .long CAPTIMER1_IRQHandler - .long RFDIAG_IRQHandler - .long LCD_CONTROLLER_IRQHandler - .long PLL_LOCK_IRQHandler - .long TIMER3_IRQHandler - .long TIMER4_IRQHandler - .long LRA_IRQHandler - .long RTC_EVENT_IRQHandler - .long GPIO_P0_IRQHandler - .long GPIO_P1_IRQHandler - .long 0 /* Reserved */ - .long 0 /* Reserved */ - .long 0 /* Reserved */ - .long 0 /* Reserved */ - .long 0 /* Reserved */ - .long 0 /* Reserved */ - .long 0 /* Reserved */ - .long 0 /* Reserved */ - .size __isr_vector, . - __isr_vector - - .text - .thumb - .thumb_func - .align 2 - .globl Reset_Handler - .type Reset_Handler, %function -Reset_Handler: - /* Make sure interrupt vector is remapped at 0x0 */ - ldr r1, =SYS_CTRL_REG - ldrh r2, [r1, #0] - orrs r2, r2, #8 - strh r2, [r1, #0] - -#if !MYNEWT_VAL(RAM_RESIDENT) -/* - * Flash is remapped at 0x0 with an offset, i.e. 0x0 does not correspond to - * 0x16000000 but to start of an image on flash. This is calculated from product - * header by 1st state bootloader and configured in CACHE_FLASH_REG. We need to - * retrieve proper offset value for calculations later. - */ - ldr r1, =CACHE_FLASH_REG - ldr r4, [r1, #0] - mov r2, r4 - mov r3, #0xFFFF - bic r4, r4, r3 /* CACHE_FLASH_REG[FLASH_REGION_BASE] */ - mov r3, #0xFFF0 - and r2, r2, r3 /* CACHE_FLASH_REG[FLASH_REGION_OFFSET] */ - lsr r2, r2, #2 - orr r4, r4, r2 - -/* Copy ISR vector from flash to RAM */ - ldr r1, =__isr_vector_start /* src ptr */ - ldr r2, =__isr_vector_end /* src end */ - ldr r3, =__intvect_start__ /* dst ptr */ -/* Make sure we copy from QSPIC address range, not from remapped range */ - cmp r1, r4 - itt lt - addlt r1, r1, r4 - addlt r2, r2, r4 -.loop_isr_copy: - cmp r1, r2 - ittt lt - ldrlt r0, [r1], #4 - strlt r0, [r3], #4 - blt .loop_isr_copy - -/* Copy QSPI code from flash to RAM */ - ldr r1, =__text_ram_addr /* src ptr */ - ldr r2, =__text_ram_start__ /* ptr */ - ldr r3, =__text_ram_end__ /* dst end */ -.loop_code_text_ram_copy: - cmp r2, r3 - ittt lt - ldrlt r0, [r1], #4 - strlt r0, [r2], #4 - blt .loop_code_text_ram_copy - -/* Copy data from flash to RAM */ - ldr r1, =__etext /* src ptr */ - ldr r2, =__data_start__ /* dst ptr */ - ldr r3, =__data_end__ /* dst end */ -.loop_data_copy: - cmp r2, r3 - ittt lt - ldrlt r0, [r1], #4 - strlt r0, [r2], #4 - blt .loop_data_copy -#endif - -/* Clear BSS */ - movs r0, 0 - ldr r1, =__bss_start__ - ldr r2, =__bss_end__ -.loop_bss_clear: - cmp r1, r2 - itt lt - strlt r0, [r1], #4 - blt .loop_bss_clear - - ldr r0, =__HeapBase - ldr r1, =__HeapLimit -/* Call static constructors */ - bl __libc_init_array - - bl SystemInit - bl main - - .pool - .size Reset_Handler, . - Reset_Handler - -/* Default interrupt handler */ - .type Default_Handler, %function -Default_Handler: - ldr r1, =SYS_CTRL_REG - ldrh r2, [r1, #0] - orrs r2, r2, #0x80 /* DEBUGGER_ENABLE */ - strh r2, [r1, #0] - b . - - .size Default_Handler, . - Default_Handler - -/* Default handlers for all interrupts */ - .macro IRQ handler - .weak \handler - .set \handler, Default_Handler - .endm - - /* Cortex-M33 interrupts */ - IRQ NMI_Handler - IRQ HardFault_Handler - IRQ MemoryManagement_Handler - IRQ BusFault_Handler - IRQ UsageFault_Handler - IRQ SecureFault_Handler - IRQ SVC_Handler - IRQ DebugMonitor_Handler - IRQ PendSV_Handler - IRQ SysTick_Handler - /* DA1469x interrupts */ - IRQ SENSOR_NODE_IRQHandler - IRQ DMA_IRQHandler - IRQ CHARGER_STATE_IRQHandler - IRQ CHARGER_ERROR_IRQHandler - IRQ CMAC2SYS_IRQHandler - IRQ UART_IRQHandler - IRQ UART2_IRQHandler - IRQ UART3_IRQHandler - IRQ I2C_IRQHandler - IRQ I2C2_IRQHandler - IRQ SPI_IRQHandler - IRQ SPI2_IRQHandler - IRQ PCM_IRQHandler - IRQ SRC_IN_IRQHandler - IRQ SRC_OUT_IRQHandler - IRQ USB_IRQHandler - IRQ TIMER_IRQHandler - IRQ TIMER2_IRQHandler - IRQ RTC_IRQHandler - IRQ KEY_WKUP_GPIO_IRQHandler - IRQ PDC_IRQHandler - IRQ VBUS_IRQHandler - IRQ MRM_IRQHandler - IRQ MOTOR_CONTROLLER_IRQHandler - IRQ TRNG_IRQHandler - IRQ DCDC_IRQHandler - IRQ XTAL32M_RDY_IRQHandler - IRQ ADC_IRQHandler - IRQ ADC2_IRQHandler - IRQ CRYPTO_IRQHandler - IRQ CAPTIMER1_IRQHandler - IRQ RFDIAG_IRQHandler - IRQ LCD_CONTROLLER_IRQHandler - IRQ PLL_LOCK_IRQHandler - IRQ TIMER3_IRQHandler - IRQ TIMER4_IRQHandler - IRQ LRA_IRQHandler - IRQ RTC_EVENT_IRQHandler - IRQ GPIO_P0_IRQHandler - IRQ GPIO_P1_IRQHandler - IRQ RESERVED40_IRQHandler - IRQ RESERVED41_IRQHandler - IRQ RESERVED42_IRQHandler - IRQ RESERVED43_IRQHandler - IRQ RESERVED44_IRQHandler - IRQ RESERVED45_IRQHandler - IRQ RESERVED46_IRQHandler - IRQ RESERVED47_IRQHandler - -.end diff --git a/hw/bsp/da1469x_dk_pro/product_header.dump b/hw/bsp/da1469x_dk_pro/product_header.dump deleted file mode 100644 index ea4842242..000000000 Binary files a/hw/bsp/da1469x_dk_pro/product_header.dump and /dev/null differ diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake index d9443f811..03a24c95b 100644 --- a/hw/bsp/family_support.cmake +++ b/hw/bsp/family_support.cmake @@ -69,6 +69,10 @@ if (NOT FAMILY STREQUAL rp2040) endif() endif() +if (NOT NO_WARN_RWX_SEGMENTS_SUPPORTED) + set(NO_WARN_RWX_SEGMENTS_SUPPORTED 1) +endif() + set(WARNING_FLAGS_GNU -Wall -Wextra @@ -140,7 +144,6 @@ function(family_filter RESULT DIR) endif() endfunction() - function(family_add_subdirectory DIR) family_filter(SHOULD_ADD "${DIR}") if (SHOULD_ADD) @@ -148,13 +151,11 @@ function(family_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) # set output suffix to .elf (skip espressif and rp2040) if(NOT FAMILY STREQUAL "espressif" AND NOT FAMILY STREQUAL "rp2040") @@ -168,7 +169,6 @@ function(family_initialize_project PROJECT DIR) endif() endfunction() - #------------------------------------------------------------- # Common Target Configure # Most families use these settings except rp2040 and espressif @@ -194,7 +194,6 @@ function(family_add_rtos TARGET RTOS) endif () endfunction() - # Add common configuration to example function(family_configure_common TARGET RTOS) family_add_rtos(${TARGET} ${RTOS}) @@ -205,20 +204,17 @@ function(family_configure_common TARGET RTOS) BOARD_${BOARD_UPPER} ) - # run size after build - find_program(SIZE_EXE ${CMAKE_SIZE}) - if(NOT ${SIZE_EXE} STREQUAL SIZE_EXE-NOTFOUND) - add_custom_command(TARGET ${TARGET} POST_BUILD - COMMAND ${SIZE_EXE} $ - ) - endif () - # Add warnings flags + # compile define from command line + if(DEFINED CFLAGS_CLI) + target_compile_options(${TARGET} PUBLIC ${CFLAGS_CLI}) + endif() + target_compile_options(${TARGET} PUBLIC ${WARNING_FLAGS_${CMAKE_C_COMPILER_ID}}) # Generate linker map file if (CMAKE_C_COMPILER_ID STREQUAL "GNU") target_link_options(${TARGET} PUBLIC "LINKER:-Map=$.map") - if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0) + if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0 AND NO_WARN_RWX_SEGMENTS_SUPPORTED) target_link_options(${TARGET} PUBLIC "LINKER:--no-warn-rwx-segments") endif () elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang") @@ -235,7 +231,6 @@ function(family_configure_common TARGET RTOS) # LOGGER option if (DEFINED LOGGER) target_compile_definitions(${TARGET} PUBLIC LOGGER_${LOGGER}) - # Add segger rtt to example if(LOGGER STREQUAL "RTT" OR LOGGER STREQUAL "rtt") if (NOT TARGET segger_rtt) @@ -246,8 +241,15 @@ function(family_configure_common TARGET RTOS) target_link_libraries(${TARGET} PUBLIC segger_rtt) endif () endif () -endfunction() + # run size after build + find_program(SIZE_EXE ${CMAKE_SIZE}) + if(NOT ${SIZE_EXE} STREQUAL SIZE_EXE-NOTFOUND) + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${SIZE_EXE} $ + ) + endif () +endfunction() # Add tinyusb to example function(family_add_tinyusb TARGET OPT_MCU RTOS) @@ -289,7 +291,6 @@ function(family_add_tinyusb TARGET OPT_MCU RTOS) endfunction() - # Add bin/hex output function(family_add_bin_hex TARGET) add_custom_command(TARGET ${TARGET} POST_BUILD @@ -363,7 +364,7 @@ function(family_add_default_example_warnings TARGET) ) if (CMAKE_C_COMPILER_ID STREQUAL "GNU") - if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0) + if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0 AND NO_WARN_RWX_SEGMENTS_SUPPORTED) target_link_options(${TARGET} PUBLIC "LINKER:--no-warn-rwx-segments") endif() @@ -398,6 +399,11 @@ function(family_flash_jlink TARGET) set(JLINK_IF swd) endif () + if (NOT DEFINED JLINK_OPTION) + set(JLINK_OPTION "") + endif () + separate_arguments(OPTION_LIST UNIX_COMMAND ${JLINK_OPTION}) + file(GENERATE OUTPUT $/${TARGET}.jlink CONTENT "halt @@ -409,7 +415,8 @@ exit" add_custom_target(${TARGET}-jlink DEPENDS ${TARGET} - COMMAND ${JLINKEXE} -device ${JLINK_DEVICE} -if ${JLINK_IF} -JTAGConf -1,-1 -speed auto -CommandFile $/${TARGET}.jlink + COMMAND ${JLINKEXE} -device ${JLINK_DEVICE} ${OPTION_LIST} -if ${JLINK_IF} -JTAGConf -1,-1 -speed auto -CommandFile $/${TARGET}.jlink + VERBATIM ) endfunction() @@ -427,6 +434,19 @@ function(family_flash_stlink TARGET) endfunction() +# Add flash st-flash target +function(family_flash_stflash TARGET) + if (NOT DEFINED ST_FLASH) + set(ST_FLASH st-flash) + endif () + + add_custom_target(${TARGET}-stflash + DEPENDS ${TARGET} + COMMAND ${ST_FLASH} write $/${TARGET}.bin 0x8000000 + ) +endfunction() + + # Add flash openocd target function(family_flash_openocd TARGET) if (NOT DEFINED OPENOCD) @@ -443,11 +463,12 @@ function(family_flash_openocd TARGET) # note skip verify since it has issue with rp2040 add_custom_target(${TARGET}-openocd DEPENDS ${TARGET} - COMMAND ${OPENOCD} ${OPTION_LIST} -c "program $ reset" ${OPTION_LIST2} -c exit + COMMAND ${OPENOCD} ${OPTION_LIST} -c init -c halt -c "program $ reset" ${OPTION_LIST2} -c exit VERBATIM ) endfunction() + # Add flash openocd-wch target # compiled from https://github.com/hathach/riscv-openocd-wch or https://github.com/dragonlock2/miscboards/blob/main/wch/SDK/riscv-openocd.tar.xz function(family_flash_openocd_wch TARGET) @@ -458,6 +479,20 @@ function(family_flash_openocd_wch TARGET) family_flash_openocd(${TARGET}) endfunction() + +# Add flash with https://github.com/ch32-rs/wlink +function(family_flash_wlink_rs TARGET) + if (NOT DEFINED WLINK_RS) + set(WLINK_RS wlink) + endif () + + add_custom_target(${TARGET}-wlink-rs + DEPENDS ${TARGET} + COMMAND ${WLINK_RS} flash $ + ) +endfunction() + + # Add flash pycod target function(family_flash_pyocd TARGET) if (NOT DEFINED PYOC) @@ -470,6 +505,7 @@ function(family_flash_pyocd TARGET) ) endfunction() + # Flash with UF2 function(family_flash_uf2 TARGET FAMILY_ID) add_custom_target(${TARGET}-uf2 @@ -478,6 +514,7 @@ function(family_flash_uf2 TARGET FAMILY_ID) ) endfunction() + # Add flash teensy_cli target function(family_flash_teensy TARGET) if (NOT DEFINED TEENSY_CLI) @@ -490,6 +527,7 @@ function(family_flash_teensy TARGET) ) endfunction() + # Add flash using NXP's LinkServer (redserver) # https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/linkserver-for-microcontrollers:LINKERSERVER function(family_flash_nxplink TARGET) diff --git a/hw/bsp/imxrt/family.c b/hw/bsp/imxrt/family.c index c9c4918ef..2f305aa4d 100644 --- a/hw/bsp/imxrt/family.c +++ b/hw/bsp/imxrt/family.c @@ -40,6 +40,7 @@ #include "fsl_iomuxc.h" #include "fsl_clock.h" #include "fsl_lpuart.h" +#include "fsl_ocotp.h" #ifdef __GNUC__ #pragma GCC diagnostic pop @@ -186,6 +187,29 @@ uint32_t board_button_read(void) { return BUTTON_STATE_ACTIVE == GPIO_PinRead(BUTTON_PORT, BUTTON_PIN); } +size_t board_get_unique_id(uint8_t id[], size_t max_len) { + (void) max_len; + + #if FSL_FEATURE_OCOTP_HAS_TIMING_CTRL + OCOTP_Init(OCOTP, CLOCK_GetFreq(kCLOCK_IpgClk)); + #else + OCOTP_Init(OCOTP, 0u); + #endif + + // Reads shadow registers 0x01 - 0x04 (Configuration and Manufacturing Info) + // into 8 bit wide destination, avoiding punning. + for (int i = 0; i < 4; ++i) { + uint32_t wr = OCOTP_ReadFuseShadowRegister(OCOTP, i + 1); + for (int j = 0; j < 4; j++) { + id[i*4+j] = wr & 0xff; + wr >>= 8; + } + } + OCOTP_Deinit(OCOTP); + + return 16; +} + int board_uart_read(uint8_t* buf, int len) { int count = 0; diff --git a/hw/bsp/imxrt/family.cmake b/hw/bsp/imxrt/family.cmake index f4d7378a5..d917e9777 100644 --- a/hw/bsp/imxrt/family.cmake +++ b/hw/bsp/imxrt/family.cmake @@ -44,6 +44,7 @@ function(add_board_target BOARD_TARGET) ${SDK_DIR}/drivers/igpio/fsl_gpio.c ${SDK_DIR}/drivers/lpspi/fsl_lpspi.c ${SDK_DIR}/drivers/lpuart/fsl_lpuart.c + ${SDK_DIR}/drivers/ocotp/fsl_ocotp.c ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_VARIANT_WITH_CORE}.c ${SDK_DIR}/devices/${MCU_VARIANT}/xip/fsl_flexspi_nor_boot.c ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c @@ -75,6 +76,7 @@ function(add_board_target BOARD_TARGET) ${SDK_DIR}/drivers/igpio ${SDK_DIR}/drivers/lpspi ${SDK_DIR}/drivers/lpuart + ${SDK_DIR}/drivers/ocotp ) update_board(${BOARD_TARGET}) diff --git a/hw/bsp/imxrt/family.mk b/hw/bsp/imxrt/family.mk index bde4c4ba6..f00afb6a4 100644 --- a/hw/bsp/imxrt/family.mk +++ b/hw/bsp/imxrt/family.mk @@ -50,7 +50,8 @@ SRC_C += \ $(SDK_DIR)/drivers/common/fsl_common.c \ $(SDK_DIR)/drivers/common/fsl_common_arm.c \ $(SDK_DIR)/drivers/igpio/fsl_gpio.c \ - $(SDK_DIR)/drivers/lpuart/fsl_lpuart.c + $(SDK_DIR)/drivers/lpuart/fsl_lpuart.c \ + $(SDK_DIR)/drivers/ocotp/fsl_ocotp.c \ # Optional drivers: only available for some mcus: rt1160, rt1170 ifneq (,$(wildcard ${TOP}/${MCU_DIR}/drivers/fsl_dcdc.c)) @@ -68,7 +69,8 @@ INC += \ $(TOP)/$(MCU_DIR)/drivers \ $(TOP)/$(SDK_DIR)/drivers/common \ $(TOP)/$(SDK_DIR)/drivers/igpio \ - $(TOP)/$(SDK_DIR)/drivers/lpuart + $(TOP)/$(SDK_DIR)/drivers/lpuart \ + $(TOP)/$(SDK_DIR)/drivers/ocotp \ SRC_S += $(MCU_DIR)/gcc/startup_$(MCU_VARIANT_WITH_CORE).S diff --git a/hw/bsp/lpc11/family.c b/hw/bsp/lpc11/family.c index bff7110b8..e75bc4919 100644 --- a/hw/bsp/lpc11/family.c +++ b/hw/bsp/lpc11/family.c @@ -90,6 +90,13 @@ void board_led_write(bool state) { Chip_GPIO_SetPinState(LPC_GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1 - LED_STATE_ON)); } +size_t board_get_unique_id(uint8_t id[], size_t max_len) { + if ( max_len < 16 ) return 0; + uint32_t* id32 = (uint32_t*) (uintptr_t) id; + id32[0] = Chip_IAP_ReadUID(); + return 4; +} + uint32_t board_button_read(void) { return BUTTON_STATE_ACTIVE == Chip_GPIO_GetPinState(LPC_GPIO, BUTTON_PORT, BUTTON_PIN); } diff --git a/hw/bsp/lpc11/family.cmake b/hw/bsp/lpc11/family.cmake index f17a48dd7..818600656 100644 --- a/hw/bsp/lpc11/family.cmake +++ b/hw/bsp/lpc11/family.cmake @@ -30,6 +30,7 @@ function(add_board_target BOARD_TARGET) ${SDK_DIR}/../gcc/cr_startup_lpc${LPC_FAMILY}.c ${SDK_DIR}/src/chip_${LPC_FAMILY}.c ${SDK_DIR}/src/clock_${LPC_FAMILY}.c + ${SDK_DIR}/src/iap.c ${SDK_DIR}/src/iocon_${LPC_FAMILY}.c ${SDK_DIR}/src/sysinit_${LPC_FAMILY}.c ) diff --git a/hw/bsp/lpc11/family.mk b/hw/bsp/lpc11/family.mk index 09713fd55..a3ec33768 100644 --- a/hw/bsp/lpc11/family.mk +++ b/hw/bsp/lpc11/family.mk @@ -9,13 +9,18 @@ CFLAGS += \ -DCFG_TUSB_MCU=OPT_MCU_LPC11UXX \ -DCFG_TUSB_MEM_ALIGN='__attribute__((aligned(64)))' -LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs +# mcu driver cause following warnings +CFLAGS += \ + -Wno-error=incompatible-pointer-types \ + +LDFLAGS_GCC += --specs=nosys.specs --specs=nano.specs SRC_C += \ src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c \ $(MCU_DIR)/../gcc/cr_startup_lpc$(MCU_DRV).c \ $(MCU_DIR)/src/chip_$(MCU_DRV).c \ $(MCU_DIR)/src/clock_$(MCU_DRV).c \ + $(MCU_DIR)/src/iap.c \ $(MCU_DIR)/src/iocon_$(MCU_DRV).c \ $(MCU_DIR)/src/sysinit_$(MCU_DRV).c diff --git a/hw/bsp/lpc55/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/lpc55/FreeRTOSConfig/FreeRTOSConfig.h index 6f10a7ab0..d3f357576 100644 --- a/hw/bsp/lpc55/FreeRTOSConfig/FreeRTOSConfig.h +++ b/hw/bsp/lpc55/FreeRTOSConfig/FreeRTOSConfig.h @@ -52,6 +52,7 @@ #define configENABLE_FPU 1 #define configENABLE_TRUSTZONE 0 #define configMINIMAL_SECURE_STACK_SIZE (1024) +#define configRUN_FREERTOS_SECURE_ONLY 1 #define configUSE_PREEMPTION 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 diff --git a/hw/bsp/lpc55/family.c b/hw/bsp/lpc55/family.c index cfd5b7032..075832ea7 100644 --- a/hw/bsp/lpc55/family.c +++ b/hw/bsp/lpc55/family.c @@ -81,51 +81,85 @@ void USB1_IRQHandler(void) { } /**************************************************************** -name: BOARD_BootClockFROHF96M +name: BOARD_BootClockPLL100M outputs: -- {id: SYSTICK_clock.outFreq, value: 96 MHz} -- {id: System_clock.outFreq, value: 96 MHz} +- {id: System_clock.outFreq, value: 100 MHz} settings: -- {id: SYSCON.MAINCLKSELA.sel, value: SYSCON.fro_hf} +- {id: PLL0_Mode, value: Normal} +- {id: ANALOG_CONTROL_FRO192M_CTRL_ENDI_FRO_96M_CFG, value: Enable} +- {id: ENABLE_CLKIN_ENA, value: Enabled} +- {id: ENABLE_SYSTEM_CLK_OUT, value: Enabled} +- {id: SYSCON.MAINCLKSELB.sel, value: SYSCON.PLL0_BYPASS} +- {id: SYSCON.PLL0CLKSEL.sel, value: SYSCON.CLK_IN_EN} +- {id: SYSCON.PLL0M_MULT.scale, value: '100', locked: true} +- {id: SYSCON.PLL0N_DIV.scale, value: '4', locked: true} +- {id: SYSCON.PLL0_PDEC.scale, value: '4', locked: true} sources: -- {id: SYSCON.fro_hf.outFreq, value: 96 MHz} +- {id: ANACTRL.fro_hf.outFreq, value: 96 MHz} +- {id: SYSCON.XTAL32M.outFreq, value: 16 MHz, enabled: true} ******************************************************************/ -void BootClockFROHF96M(void) { - /*!< Set up the clock sources */ - /*!< Set up FRO */ - POWER_DisablePD(kPDRUNCFG_PD_FRO192M); /*!< Ensure FRO is on */ - CLOCK_SetupFROClocking(12000000U); /*!< Set up FRO to the 12 MHz, just for sure */ - CLOCK_AttachClk(kFRO12M_to_MAIN_CLK); /*!< Switch to FRO 12MHz first to ensure we can change voltage without - accidentally being below the voltage for current speed */ +void BOARD_BootClockPLL100M(void) +{ + /*!< Set up the clock sources */ + /*!< Configure FRO192M */ + POWER_DisablePD(kPDRUNCFG_PD_FRO192M); /*!< Ensure FRO is on */ + CLOCK_SetupFROClocking(12000000U); /*!< Set up FRO to the 12 MHz, just for sure */ + CLOCK_AttachClk(kFRO12M_to_MAIN_CLK); /*!< Switch to FRO 12MHz first to ensure we can change the clock setting */ - CLOCK_SetupFROClocking(96000000U); /*!< Set up high frequency FRO output to selected frequency */ + CLOCK_SetupFROClocking(96000000U); /* Enable FRO HF(96MHz) output */ - POWER_SetVoltageForFreq(96000000U); /*!< Set voltage for the one of the fastest clock outputs: System clock output */ - CLOCK_SetFLASHAccessCyclesForFreq(96000000U); /*!< Set FLASH wait states for core */ + /*!< Configure XTAL32M */ + POWER_DisablePD(kPDRUNCFG_PD_XTAL32M); /* Ensure XTAL32M is powered */ + POWER_DisablePD(kPDRUNCFG_PD_LDOXO32M); /* Ensure XTAL32M is powered */ + CLOCK_SetupExtClocking(16000000U); /* Enable clk_in clock */ + SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK; /* Enable clk_in from XTAL32M clock */ + ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_SYSTEM_CLK_OUT_MASK; /* Enable clk_in to system */ - /*!< Set up dividers */ - CLOCK_SetClkDiv(kCLOCK_DivAhbClk, 1U, false); /*!< Set AHBCLKDIV divider to value 1 */ + POWER_SetVoltageForFreq(100000000U); /*!< Set voltage for the one of the fastest clock outputs: System clock output */ + CLOCK_SetFLASHAccessCyclesForFreq(100000000U); /*!< Set FLASH wait states for core */ - /*!< Set up clock selectors - Attach clocks to the peripheries */ - CLOCK_AttachClk(kFRO_HF_to_MAIN_CLK); /*!< Switch MAIN_CLK to FRO_HF */ + /*!< Set up PLL */ + CLOCK_AttachClk(kEXT_CLK_to_PLL0); /*!< Switch PLL0CLKSEL to EXT_CLK */ + POWER_DisablePD(kPDRUNCFG_PD_PLL0); /* Ensure PLL is on */ + POWER_DisablePD(kPDRUNCFG_PD_PLL0_SSCG); + const pll_setup_t pll0Setup = { + .pllctrl = SYSCON_PLL0CTRL_CLKEN_MASK | SYSCON_PLL0CTRL_SELI(53U) | SYSCON_PLL0CTRL_SELP(26U), + .pllndec = SYSCON_PLL0NDEC_NDIV(4U), + .pllpdec = SYSCON_PLL0PDEC_PDIV(2U), + .pllsscg = {0x0U,(SYSCON_PLL0SSCG1_MDIV_EXT(100U) | SYSCON_PLL0SSCG1_SEL_EXT_MASK)}, + .pllRate = 100000000U, + .flags = PLL_SETUPFLAG_WAITLOCK + }; + CLOCK_SetPLL0Freq(&pll0Setup); /*!< Configure PLL0 to the desired values */ - /*!< Set SystemCoreClock variable. */ - SystemCoreClock = 96000000U; + /*!< Set up dividers */ + CLOCK_SetClkDiv(kCLOCK_DivAhbClk, 1U, false); /*!< Set AHBCLKDIV divider to value 1 */ + + /*!< Set up clock selectors - Attach clocks to the peripheries */ + CLOCK_AttachClk(kPLL0_to_MAIN_CLK); /*!< Switch MAIN_CLK to PLL0 */ + + /*< Set SystemCoreClock variable. */ + SystemCoreClock = 100000000U; } void board_init(void) { // Enable IOCON clock CLOCK_EnableClock(kCLOCK_Iocon); - // Init 96 MHz clock - BootClockFROHF96M(); + // Init 100 MHz clock + BOARD_BootClockPLL100M(); +#if CFG_TUSB_OS == OPT_OS_NONE // 1ms tick timer SysTick_Config(SystemCoreClock / 1000); -#if CFG_TUSB_OS == OPT_OS_FREERTOS +#elif CFG_TUSB_OS == OPT_OS_FREERTOS + // Explicitly disable systick to prevent its ISR runs before scheduler start + SysTick->CTRL &= ~1U; + // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) NVIC_SetPriority(USB0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); + NVIC_SetPriority(USB1_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); #endif // Init all GPIO ports @@ -239,6 +273,10 @@ void board_init(void) { // phytx &= ~(USBPHY_TX_D_CAL_MASK | USBPHY_TX_TXCAL45DM_MASK | USBPHY_TX_TXCAL45DP_MASK); // phytx |= USBPHY_TX_D_CAL(0x0C) | USBPHY_TX_TXCAL45DP(0x06) | USBPHY_TX_TXCAL45DM(0x06); // USBPHY->TX = phytx; + + ARM_MPU_SetMemAttr(0, 0x44); // Normal memory, non-cacheable (inner and outer) + ARM_MPU_SetRegion(0, ARM_MPU_RBAR(0x40100000, ARM_MPU_SH_NON, 0, 1, 1), ARM_MPU_RLAR(0x40104000, 0)); + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); #endif } diff --git a/hw/bsp/ra/family.c b/hw/bsp/ra/family.c index db8988a36..a8b4ebcc7 100644 --- a/hw/bsp/ra/family.c +++ b/hw/bsp/ra/family.c @@ -147,6 +147,13 @@ uint32_t board_button_read(void) { return lvl == BUTTON_STATE_ACTIVE; } +size_t board_get_unique_id(uint8_t id[], size_t max_len) { + max_len = tu_min32(max_len, sizeof(bsp_unique_id_t)); + bsp_unique_id_t const *uid = R_BSP_UniqueIdGet(); + memcpy(id, uid->unique_id_bytes, max_len); + return max_len; +} + int board_uart_read(uint8_t *buf, int len) { (void) buf; (void) len; @@ -169,7 +176,6 @@ void SysTick_Handler(void) { uint32_t board_millis(void) { return system_ticks; } - #endif //--------------------------------------------------------------------+ diff --git a/hw/bsp/rp2040/board.h b/hw/bsp/rp2040/board.h index 3849894ce..733e93797 100644 --- a/hw/bsp/rp2040/board.h +++ b/hw/bsp/rp2040/board.h @@ -78,7 +78,12 @@ // USB Host MAX3421E //-------------------------------------------------------------------- -#define MAX3421_SPI PICO_DEFAULT_SPI_INSTANCE +#ifdef PICO_DEFAULT_SPI +#define MAX3421_SPI PICO_DEFAULT_SPI // sdk v2 +#else +#define MAX3421_SPI PICO_DEFAULT_SPI_INSTANCE // sdk v1 +#endif + #define MAX3421_SCK_PIN PICO_DEFAULT_SPI_SCK_PIN #define MAX3421_MOSI_PIN PICO_DEFAULT_SPI_TX_PIN #define MAX3421_MISO_PIN PICO_DEFAULT_SPI_RX_PIN diff --git a/hw/bsp/rp2040/family.c b/hw/bsp/rp2040/family.c index cffb632f3..250989ce9 100644 --- a/hw/bsp/rp2040/family.c +++ b/hw/bsp/rp2040/family.c @@ -31,6 +31,7 @@ #include "hardware/gpio.h" #include "hardware/sync.h" #include "hardware/resets.h" +#include "hardware/clocks.h" #include "hardware/structs/ioqspi.h" #include "hardware/structs/sio.h" @@ -274,7 +275,15 @@ static void max3421_init(void) { gpio_set_function(MAX3421_SCK_PIN, GPIO_FUNC_SPI); gpio_set_function(MAX3421_MOSI_PIN, GPIO_FUNC_SPI); gpio_set_function(MAX3421_MISO_PIN, GPIO_FUNC_SPI); + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnull-dereference" +#endif spi_set_format(MAX3421_SPI, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif } //// API to enable/disable MAX3421 INTR pin interrupt diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index b41d7fbea..d91821ffa 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -133,7 +133,10 @@ target_sources(tinyusb_bsp INTERFACE target_include_directories(tinyusb_bsp INTERFACE ${TOP}/hw ) -target_link_libraries(tinyusb_bsp INTERFACE pico_unique_id) +target_link_libraries(tinyusb_bsp INTERFACE + pico_unique_id + hardware_clocks + ) # tinyusb_additions will hold our extra settings for examples add_library(tinyusb_additions INTERFACE) @@ -175,6 +178,11 @@ function(family_configure_target TARGET RTOS) # export RTOS_SUFFIX to parent scope set(RTOS_SUFFIX ${RTOS_SUFFIX} PARENT_SCOPE) + # compile define from command line + if(DEFINED CFLAGS_CLI) + target_compile_options(${TARGET} PUBLIC ${CFLAGS_CLI}) + endif() + pico_add_extra_outputs(${TARGET}) pico_enable_stdio_uart(${TARGET} 1) target_link_libraries(${TARGET} PUBLIC pico_stdlib tinyusb_board${RTOS_SUFFIX} tinyusb_additions) diff --git a/hw/bsp/stm32f0/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/stm32f0/FreeRTOSConfig/FreeRTOSConfig.h index 37e7e0943..999dbfe9e 100644 --- a/hw/bsp/stm32f0/FreeRTOSConfig/FreeRTOSConfig.h +++ b/hw/bsp/stm32f0/FreeRTOSConfig/FreeRTOSConfig.h @@ -58,7 +58,7 @@ #define configCPU_CLOCK_HZ SystemCoreClock #define configTICK_RATE_HZ ( 1000 ) #define configMAX_PRIORITIES ( 5 ) -#define configMINIMAL_STACK_SIZE ( 128 ) +#define configMINIMAL_STACK_SIZE ( 200 ) #define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) #define configMAX_TASK_NAME_LEN 16 #define configUSE_16_BIT_TICKS 0 diff --git a/hw/bsp/stm32f1/boards/stm32f103ze_iar/board.cmake b/hw/bsp/stm32f1/boards/stm32f103ze_iar/board.cmake new file mode 100644 index 000000000..7672ff338 --- /dev/null +++ b/hw/bsp/stm32f1/boards/stm32f103ze_iar/board.cmake @@ -0,0 +1,15 @@ +set(MCU_VARIANT stm32f103xe) +set(JLINK_DEVICE stm32f103ze) +#set(JLINK_OPTION "-USB 320000338") + +string(TOUPPER ${MCU_VARIANT} MCU_VARIANT_UPPER) + +set(LD_FILE_GNU ${ST_CMSIS}/Source/Templates/gcc/linker/${MCU_VARIANT_UPPER}_FLASH.ld) +set(LD_FILE_IAR ${ST_CMSIS}/Source/Templates/iar/linker/${MCU_VARIANT}_flash.icf) + +function(update_board TARGET) + target_compile_definitions(${TARGET} PUBLIC + STM32F103xE + HSE_VALUE=8000000U + ) +endfunction() diff --git a/hw/bsp/stm32f1/boards/stm32f103ze_iar/board.h b/hw/bsp/stm32f1/boards/stm32f103ze_iar/board.h new file mode 100644 index 000000000..d31102d32 --- /dev/null +++ b/hw/bsp/stm32f1/boards/stm32f103ze_iar/board.h @@ -0,0 +1,97 @@ +/* + * 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_PORT GPIOF +#define LED_PIN GPIO_PIN_6 +#define LED_STATE_ON 1 + +// Button +#define BUTTON_PORT GPIOG +#define BUTTON_PIN GPIO_PIN_8 +#define BUTTON_STATE_ACTIVE 0 + +// USB Connect +#define USB_CONNECT_PORT GPIOG +#define USB_CONNECT_PIN GPIO_PIN_11 +#define USB_CONNECT_STATE 0 + +// UART +//#define UART_DEV USART1 +//#define UART_CLK_EN __HAL_RCC_USART1_CLK_ENABLE +//#define UART_GPIO_PORT GPIOA +//#define UART_GPIO_AF GPIO_AF1_USART1 +//#define UART_TX_PIN GPIO_PIN_9 +//#define UART_RX_PIN GPIO_PIN_10 + +//--------------------------------------------------------------------+ +// RCC Clock +//--------------------------------------------------------------------+ +static inline void board_stm32f1_clock_init(void) +{ + RCC_ClkInitTypeDef clkinitstruct = {0}; + RCC_OscInitTypeDef oscinitstruct = {0}; + RCC_PeriphCLKInitTypeDef rccperiphclkinit = {0}; + + /* Enable HSE Oscillator and activate PLL with HSE as source */ + oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + oscinitstruct.HSEState = RCC_HSE_ON; + oscinitstruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; + oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9; + oscinitstruct.PLL.PLLState = RCC_PLL_ON; + oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + HAL_RCC_OscConfig(&oscinitstruct); + + /* USB clock selection */ + rccperiphclkinit.PeriphClockSelection = RCC_PERIPHCLK_USB; + rccperiphclkinit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5; + HAL_RCCEx_PeriphCLKConfig(&rccperiphclkinit); + + /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ + clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); + clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2; + clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1; + HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2); +} + +static inline void board_vbus_sense_init(void) +{ +} + +#ifdef __cplusplus + } +#endif + +#endif /* BOARD_H_ */ diff --git a/hw/bsp/stm32f1/boards/stm32f103ze_iar/board.mk b/hw/bsp/stm32f1/boards/stm32f103ze_iar/board.mk new file mode 100644 index 000000000..5b17d8036 --- /dev/null +++ b/hw/bsp/stm32f1/boards/stm32f103ze_iar/board.mk @@ -0,0 +1,13 @@ +MCU_VARIANT = stm32f103xe + +CFLAGS += -DSTM32F103xE -DHSE_VALUE=8000000U + +# Linker +LD_FILE_GCC = ${ST_CMSIS}/Source/Templates/gcc/linker/STM32F103XE_FLASH.ld +LD_FILE_IAR = ${ST_CMSIS}/Source/Templates/iar/linker/${MCU_VARIANT}_flash.icf + +# For flash-jlink target +JLINK_DEVICE = stm32f103ze + +# flash target ROM bootloader +flash: flash-jlink diff --git a/hw/bsp/stm32f1/family.c b/hw/bsp/stm32f1/family.c index 70f81e7fc..600fc28c0 100644 --- a/hw/bsp/stm32f1/family.c +++ b/hw/bsp/stm32f1/family.c @@ -57,6 +57,19 @@ void board_init(void) { __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); +#ifdef __HAL_RCC_GPIOE_CLK_ENABLE + __HAL_RCC_GPIOE_CLK_ENABLE(); +#endif + +#ifdef __HAL_RCC_GPIOF_CLK_ENABLE + __HAL_RCC_GPIOF_CLK_ENABLE(); +#endif + +#ifdef __HAL_RCC_GPIOG_CLK_ENABLE + __HAL_RCC_GPIOG_CLK_ENABLE(); +#endif + + #if CFG_TUSB_OS == OPT_OS_NONE // 1ms tick timer SysTick_Config(SystemCoreClock / 1000); @@ -107,10 +120,18 @@ void board_init(void) { HAL_UART_Init(&UartHandle); #endif +#ifdef USB_CONNECT_PIN + GPIO_InitStruct.Pin = USB_CONNECT_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + HAL_GPIO_Init(USB_CONNECT_PORT, &GPIO_InitStruct); +#endif + // USB Pins // Configure USB DM and DP pins. GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12); - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); @@ -119,6 +140,18 @@ void board_init(void) { __HAL_RCC_USB_CLK_ENABLE(); } +#ifdef USB_CONNECT_PIN +void dcd_disconnect(uint8_t rhport) { + (void)rhport; + HAL_GPIO_WritePin(USB_CONNECT_PORT, USB_CONNECT_PIN, 1-USB_CONNECT_STATE); +} + +void dcd_connect(uint8_t rhport) { + (void)rhport; + HAL_GPIO_WritePin(USB_CONNECT_PORT, USB_CONNECT_PIN, USB_CONNECT_STATE); +} +#endif + //--------------------------------------------------------------------+ // Board porting API //--------------------------------------------------------------------+ diff --git a/hw/bsp/stm32f1/family.mk b/hw/bsp/stm32f1/family.mk index 03fbf4010..364616304 100644 --- a/hw/bsp/stm32f1/family.mk +++ b/hw/bsp/stm32f1/family.mk @@ -1,10 +1,10 @@ ST_FAMILY = f1 -DEPS_SUBMODULES += lib/CMSIS_5 hw/mcu/st/cmsis_device_$(ST_FAMILY) hw/mcu/st/stm32$(ST_FAMILY)xx_hal_driver +DEPS_SUBMODULES += lib/CMSIS_5 hw/mcu/st/cmsis_device_${ST_FAMILY} hw/mcu/st/stm32${ST_FAMILY}xx_hal_driver -ST_CMSIS = hw/mcu/st/cmsis_device_$(ST_FAMILY) -ST_HAL_DRIVER = hw/mcu/st/stm32$(ST_FAMILY)xx_hal_driver +ST_CMSIS = hw/mcu/st/cmsis_device_${ST_FAMILY} +ST_HAL_DRIVER = hw/mcu/st/stm32${ST_FAMILY}xx_hal_driver -include $(TOP)/$(BOARD_PATH)/board.mk +include ${TOP}/${BOARD_PATH}/board.mk CPU_CORE ?= cortex-m3 # -------------- @@ -29,23 +29,23 @@ LDFLAGS_GCC += \ # ------------------------ SRC_C += \ src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c \ - $(ST_CMSIS)/Source/Templates/system_stm32$(ST_FAMILY)xx.c \ - $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal.c \ - $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_cortex.c \ - $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_rcc.c \ - $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_rcc_ex.c \ - $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_gpio.c \ - $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_uart.c + ${ST_CMSIS}/Source/Templates/system_stm32${ST_FAMILY}xx.c \ + ${ST_HAL_DRIVER}/Src/stm32${ST_FAMILY}xx_hal.c \ + ${ST_HAL_DRIVER}/Src/stm32${ST_FAMILY}xx_hal_cortex.c \ + ${ST_HAL_DRIVER}/Src/stm32${ST_FAMILY}xx_hal_rcc.c \ + ${ST_HAL_DRIVER}/Src/stm32${ST_FAMILY}xx_hal_rcc_ex.c \ + ${ST_HAL_DRIVER}/Src/stm32${ST_FAMILY}xx_hal_gpio.c \ + ${ST_HAL_DRIVER}/Src/stm32${ST_FAMILY}xx_hal_uart.c INC += \ - $(TOP)/$(BOARD_PATH) \ - $(TOP)/lib/CMSIS_5/CMSIS/Core/Include \ - $(TOP)/$(ST_CMSIS)/Include \ - $(TOP)/$(ST_HAL_DRIVER)/Inc + ${TOP}/${BOARD_PATH} \ + ${TOP}/lib/CMSIS_5/CMSIS/Core/Include \ + ${TOP}/${ST_CMSIS}/Include \ + ${TOP}/${ST_HAL_DRIVER}/Inc # Startup -SRC_S_GCC += $(ST_CMSIS)/Source/Templates/gcc/startup_$(MCU_VARIANT).s -SRC_S_IAR += $(ST_CMSIS)/Source/Templates/iar/startup_$(MCU_VARIANT).s +SRC_S_GCC += ${ST_CMSIS}/Source/Templates/gcc/startup_${MCU_VARIANT}.s +SRC_S_IAR += ${ST_CMSIS}/Source/Templates/iar/startup_${MCU_VARIANT}.s # flash target ROM bootloader: flash-dfu-util DFU_UTIL_OPTION = -a 0 --dfuse-address 0x08000000 diff --git a/hw/bsp/stm32f3/family.c b/hw/bsp/stm32f3/family.c index e7488ba84..f94dd95cf 100644 --- a/hw/bsp/stm32f3/family.c +++ b/hw/bsp/stm32f3/family.c @@ -116,6 +116,19 @@ uint32_t board_button_read(void) { return BUTTON_STATE_ACTIVE == HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_PIN); } +size_t board_get_unique_id(uint8_t id[], size_t max_len) { + (void) max_len; + volatile uint32_t * stm32_uuid = (volatile uint32_t *) UID_BASE; + uint32_t* id32 = (uint32_t*) (uintptr_t) id; + uint8_t const len = 12; + + id32[0] = stm32_uuid[0]; + id32[1] = stm32_uuid[1]; + id32[2] = stm32_uuid[2]; + + return len; +} + int board_uart_read(uint8_t* buf, int len) { (void) buf; (void) len; diff --git a/hw/bsp/stm32f7/family.mk b/hw/bsp/stm32f7/family.mk index e261b0467..bae7d6d5b 100644 --- a/hw/bsp/stm32f7/family.mk +++ b/hw/bsp/stm32f7/family.mk @@ -24,6 +24,7 @@ ifeq ($(PORT), 1) $(info "Using OTG_HS in FullSpeed mode") endif else + CFLAGS += -DBOARD_TUD_MAX_SPEED=OPT_MODE_FULL_SPEED $(info "Using OTG_FS") endif diff --git a/hw/bsp/stm32g0/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/stm32g0/FreeRTOSConfig/FreeRTOSConfig.h index 82cb0cdb3..76133ea2d 100644 --- a/hw/bsp/stm32g0/FreeRTOSConfig/FreeRTOSConfig.h +++ b/hw/bsp/stm32g0/FreeRTOSConfig/FreeRTOSConfig.h @@ -58,7 +58,7 @@ #define configCPU_CLOCK_HZ SystemCoreClock #define configTICK_RATE_HZ ( 1000 ) #define configMAX_PRIORITIES ( 5 ) -#define configMINIMAL_STACK_SIZE ( 128 ) +#define configMINIMAL_STACK_SIZE ( 200 ) #define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) #define configMAX_TASK_NAME_LEN 16 #define configUSE_16_BIT_TICKS 0 diff --git a/hw/bsp/stm32g0/family.cmake b/hw/bsp/stm32g0/family.cmake index b6838c619..0dafa9c0a 100644 --- a/hw/bsp/stm32g0/family.cmake +++ b/hw/bsp/stm32g0/family.cmake @@ -15,7 +15,7 @@ set(CMAKE_SYSTEM_PROCESSOR cortex-m0plus CACHE INTERNAL "System Processor") set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake) set(FAMILY_MCUS STM32G0 CACHE INTERNAL "") - +set(OPENOCD_OPTION "-f interface/stlink.cfg -f target/stm32g0x.cfg") #------------------------------------ # BOARD_TARGET @@ -112,6 +112,7 @@ function(family_configure_example TARGET RTOS) target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb) # Flashing - family_flash_stlink(${TARGET}) family_flash_jlink(${TARGET}) + family_flash_stlink(${TARGET}) + #family_flash_openocd(${TARGET}) endfunction() diff --git a/hw/bsp/stm32g4/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/stm32g4/FreeRTOSConfig/FreeRTOSConfig.h index fa3f0ac33..891ea4b79 100644 --- a/hw/bsp/stm32g4/FreeRTOSConfig/FreeRTOSConfig.h +++ b/hw/bsp/stm32g4/FreeRTOSConfig/FreeRTOSConfig.h @@ -58,7 +58,7 @@ #define configCPU_CLOCK_HZ SystemCoreClock #define configTICK_RATE_HZ ( 1000 ) #define configMAX_PRIORITIES ( 5 ) -#define configMINIMAL_STACK_SIZE ( 128 ) +#define configMINIMAL_STACK_SIZE ( 200 ) #define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) #define configMAX_TASK_NAME_LEN 16 #define configUSE_16_BIT_TICKS 0 diff --git a/hw/bsp/stm32h5/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/stm32h5/FreeRTOSConfig/FreeRTOSConfig.h index cf6e23c1b..6a9c90a4a 100644 --- a/hw/bsp/stm32h5/FreeRTOSConfig/FreeRTOSConfig.h +++ b/hw/bsp/stm32h5/FreeRTOSConfig/FreeRTOSConfig.h @@ -58,7 +58,7 @@ #define configCPU_CLOCK_HZ SystemCoreClock #define configTICK_RATE_HZ ( 1000 ) #define configMAX_PRIORITIES ( 5 ) -#define configMINIMAL_STACK_SIZE ( 128 ) +#define configMINIMAL_STACK_SIZE ( 200 ) #define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) #define configMAX_TASK_NAME_LEN 16 #define configUSE_16_BIT_TICKS 0 diff --git a/hw/bsp/stm32l0/boards/stm32l052dap52/board.h b/hw/bsp/stm32l0/boards/stm32l052dap52/board.h index c8963199b..ee83bbcbc 100644 --- a/hw/bsp/stm32l0/boards/stm32l052dap52/board.h +++ b/hw/bsp/stm32l0/boards/stm32l052dap52/board.h @@ -54,16 +54,17 @@ //--------------------------------------------------------------------+ static inline void board_stm32l0_clock_init(void) { - RCC_ClkInitTypeDef RCC_ClkInitStruct; - RCC_OscInitTypeDef RCC_OscInitStruct; - RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; - static RCC_CRSInitTypeDef RCC_CRSInitStruct; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; + RCC_CRSInitTypeDef RCC_CRSInitStruct = {0}; /* Enable HSI Oscillator to be used as System clock source Enable HSI48 Oscillator to be used as USB clock source */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_HSI48; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSI48State = RCC_HSI48_ON; + RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; HAL_RCC_OscConfig(&RCC_OscInitStruct); /* Select HSI48 as USB clock source */ diff --git a/hw/bsp/stm32l0/boards/stm32l0538disco/board.cmake b/hw/bsp/stm32l0/boards/stm32l0538disco/board.cmake index 8d7b537d5..895bbb3ab 100644 --- a/hw/bsp/stm32l0/boards/stm32l0538disco/board.cmake +++ b/hw/bsp/stm32l0/boards/stm32l0538disco/board.cmake @@ -1,5 +1,6 @@ set(MCU_VARIANT stm32l053xx) set(JLINK_DEVICE stm32l053r8) +#set(JLINK_OPTION "-USB 778921770") set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/STM32L053C8Tx_FLASH.ld) diff --git a/hw/bsp/stm32l0/boards/stm32l0538disco/board.h b/hw/bsp/stm32l0/boards/stm32l0538disco/board.h index 0722e3102..5cda1c15a 100644 --- a/hw/bsp/stm32l0/boards/stm32l0538disco/board.h +++ b/hw/bsp/stm32l0/boards/stm32l0538disco/board.h @@ -54,16 +54,17 @@ //--------------------------------------------------------------------+ static inline void board_stm32l0_clock_init(void) { - RCC_ClkInitTypeDef RCC_ClkInitStruct; - RCC_OscInitTypeDef RCC_OscInitStruct; - RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; - static RCC_CRSInitTypeDef RCC_CRSInitStruct; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; + RCC_CRSInitTypeDef RCC_CRSInitStruct = {0}; /* Enable HSI Oscillator to be used as System clock source Enable HSI48 Oscillator to be used as USB clock source */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_HSI48; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSI48State = RCC_HSI48_ON; + RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; HAL_RCC_OscConfig(&RCC_OscInitStruct); /* Select HSI48 as USB clock source */ diff --git a/hw/bsp/stm32l0/family.c b/hw/bsp/stm32l0/family.c index ea7f0b73d..c8c88d687 100644 --- a/hw/bsp/stm32l0/family.c +++ b/hw/bsp/stm32l0/family.c @@ -126,6 +126,19 @@ uint32_t board_button_read(void) { return BUTTON_STATE_ACTIVE == HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_PIN); } +size_t board_get_unique_id(uint8_t id[], size_t max_len) { + (void) max_len; + volatile uint32_t * stm32_uuid = (volatile uint32_t *) UID_BASE; + uint32_t* id32 = (uint32_t*) (uintptr_t) id; + uint8_t const len = 12; + + id32[0] = stm32_uuid[0]; + id32[1] = stm32_uuid[1]; + id32[2] = stm32_uuid[2]; + + return len; +} + int board_uart_read(uint8_t* buf, int len) { (void) buf; (void) len; diff --git a/hw/bsp/stm32u5/boards/stm32u545nucleo/board.cmake b/hw/bsp/stm32u5/boards/stm32u545nucleo/board.cmake new file mode 100644 index 000000000..d42501948 --- /dev/null +++ b/hw/bsp/stm32u5/boards/stm32u545nucleo/board.cmake @@ -0,0 +1,8 @@ +set(MCU_VARIANT stm32u545xx) +set(JLINK_DEVICE stm32u545re) + +function(update_board TARGET) + target_compile_definitions(${TARGET} PUBLIC + STM32U545xx + ) +endfunction() diff --git a/hw/bsp/stm32u5/boards/stm32u545nucleo/board.h b/hw/bsp/stm32u5/boards/stm32u545nucleo/board.h new file mode 100644 index 000000000..7f3bf462c --- /dev/null +++ b/hw/bsp/stm32u5/boards/stm32u545nucleo/board.h @@ -0,0 +1,112 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023, 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 GREEN +#define LED_PORT GPIOA +#define LED_PIN GPIO_PIN_5 +#define LED_STATE_ON 1 + +// BUTTON +#define BUTTON_PORT GPIOC +#define BUTTON_PIN GPIO_PIN_13 +#define BUTTON_STATE_ACTIVE 1 + +// UART Enable for STLink VCOM +#define UART_DEV LPUART1 +#define UART_CLK_EN __HAL_RCC_LPUART1_CLK_ENABLE +#define UART_GPIO_PORT GPIOA +#define UART_GPIO_AF GPIO_AF8_LPUART1 +#define UART_TX_PIN GPIO_PIN_2 +#define UART_RX_PIN GPIO_PIN_3 + +//--------------------------------------------------------------------+ +// RCC Clock +//--------------------------------------------------------------------+ + +static void SystemClock_Config(void) { + RCC_OscInitTypeDef RCC_OscInitStruct = { 0 }; + RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 }; + RCC_PeriphCLKInitTypeDef PeriphClkInit = { 0 }; + + /* Enable Power Clock */ + __HAL_RCC_PWR_CLK_ENABLE(); + + /** Configure the main internal regulator output voltage + */ + HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); + + /** Initializes the CPU, AHB and APB buses clocks + */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48 | RCC_OSCILLATORTYPE_HSI; + RCC_OscInitStruct.HSIState = RCC_HSI_ON; + RCC_OscInitStruct.HSI48State = RCC_HSI48_ON; + RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; + RCC_OscInitStruct.PLL.PLLMBOOST = RCC_PLLMBOOST_DIV1; + RCC_OscInitStruct.PLL.PLLM = 1; + RCC_OscInitStruct.PLL.PLLN = 10; + RCC_OscInitStruct.PLL.PLLP = 2; + RCC_OscInitStruct.PLL.PLLQ = 2; + RCC_OscInitStruct.PLL.PLLR = 1; + RCC_OscInitStruct.PLL.PLLRGE = RCC_PLLVCIRANGE_1; + RCC_OscInitStruct.PLL.PLLFRACN = 0; + HAL_RCC_OscConfig(&RCC_OscInitStruct); + + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_CLK48; + PeriphClkInit.IclkClockSelection = RCC_CLK48CLKSOURCE_HSI48; + + HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit); + + /** Initializes the CPU, AHB and APB buses clocks + */ + RCC_ClkInitStruct.ClockType = + RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_PCLK3; + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; + RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1; + + HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); +} + +static void SystemPower_Config(void) { +} + +#ifdef __cplusplus +} +#endif + +#endif /* BOARD_H_ */ diff --git a/hw/bsp/stm32u5/boards/stm32u545nucleo/board.mk b/hw/bsp/stm32u5/boards/stm32u545nucleo/board.mk new file mode 100644 index 000000000..072c595fb --- /dev/null +++ b/hw/bsp/stm32u5/boards/stm32u545nucleo/board.mk @@ -0,0 +1,11 @@ +CFLAGS += \ + -DSTM32U545xx \ + +# All source paths should be relative to the top level. +LD_FILE = ${FAMILY_PATH}/linker/STM32U545xx_FLASH.ld + +SRC_S += $(ST_CMSIS)/Source/Templates/gcc/startup_stm32u545xx.s + +MCU_VARIANT = stm32u545xx +# For flash-jlink target +JLINK_DEVICE = stm32u545re diff --git a/hw/bsp/stm32u5/boards/stm32u575eval/board.mk b/hw/bsp/stm32u5/boards/stm32u575eval/board.mk index 922d67f83..fee56f2ba 100644 --- a/hw/bsp/stm32u5/boards/stm32u575eval/board.mk +++ b/hw/bsp/stm32u5/boards/stm32u575eval/board.mk @@ -6,5 +6,6 @@ LD_FILE = ${FAMILY_PATH}/linker/STM32U575xx_FLASH.ld SRC_S += $(ST_CMSIS)/Source/Templates/gcc/startup_stm32u575xx.s +MCU_VARIANT = stm32u575xx # For flash-jlink target JLINK_DEVICE = stm32u575ai diff --git a/hw/bsp/stm32u5/boards/stm32u575nucleo/board.mk b/hw/bsp/stm32u5/boards/stm32u575nucleo/board.mk index 1dd157a68..c83ec3999 100644 --- a/hw/bsp/stm32u5/boards/stm32u575nucleo/board.mk +++ b/hw/bsp/stm32u5/boards/stm32u575nucleo/board.mk @@ -6,5 +6,6 @@ LD_FILE = ${FAMILY_PATH}/linker/STM32U575xx_FLASH.ld SRC_S += $(ST_CMSIS)/Source/Templates/gcc/startup_stm32u575xx.s +MCU_VARIANT = stm32u575xx # For flash-jlink target JLINK_DEVICE = stm32u575zi diff --git a/hw/bsp/stm32u5/boards/stm32u5a5nucleo/board.mk b/hw/bsp/stm32u5/boards/stm32u5a5nucleo/board.mk index e759cec24..4bebe3330 100644 --- a/hw/bsp/stm32u5/boards/stm32u5a5nucleo/board.mk +++ b/hw/bsp/stm32u5/boards/stm32u5a5nucleo/board.mk @@ -7,5 +7,6 @@ LD_FILE = ${BOARD_PATH}/STM32U5A5ZJTXQ_FLASH.ld SRC_S += $(ST_CMSIS)/Source/Templates/gcc/startup_stm32u5a5xx.s +MCU_VARIANT = stm32u5a5xx # For flash-jlink target JLINK_DEVICE = stm32u575zi diff --git a/hw/bsp/stm32u5/family.c b/hw/bsp/stm32u5/family.c index d779b5c96..3cc7cc511 100644 --- a/hw/bsp/stm32u5/family.c +++ b/hw/bsp/stm32u5/family.c @@ -72,7 +72,9 @@ void board_init(void) { __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); +#ifdef GPIOF __HAL_RCC_GPIOF_CLK_ENABLE(); +#endif __HAL_RCC_GPIOG_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); @@ -140,6 +142,17 @@ void board_init(void) { GPIO_InitStruct.Alternate = GPIO_AF10_USB; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); +#ifdef USB_DRD_FS + // STM32U535/STM32U545 + + /* Enable USB power on Pwrctrl CR2 register */ + HAL_PWREx_EnableVddUSB(); + + /* USB clock enable */ + __HAL_RCC_USB_FS_CLK_ENABLE(); + +#endif + #ifdef USB_OTG_FS #if CFG_TUSB_OS == OPT_OS_FREERTOS // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) @@ -170,7 +183,9 @@ void board_init(void) { /* USB clock enable */ __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); -#else +#endif + +#ifdef USB_OTG_HS // STM59x/Ax/Fx/Gx only have 1 USB HS port #if CFG_TUSB_OS == OPT_OS_FREERTOS diff --git a/hw/bsp/stm32u5/family.cmake b/hw/bsp/stm32u5/family.cmake index 7402540b7..f7a7aeb33 100644 --- a/hw/bsp/stm32u5/family.cmake +++ b/hw/bsp/stm32u5/family.cmake @@ -102,10 +102,16 @@ function(family_configure_example TARGET RTOS) # Add TinyUSB target and port source family_add_tinyusb(${TARGET} OPT_MCU_STM32U5 ${RTOS}) - target_sources(${TARGET}-tinyusb PUBLIC - ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c - #${TOP}/src/portable/st/typec/typec_stm32.c - ) + if ((${MCU_VARIANT} STREQUAL "stm32u535xx") OR (${MCU_VARIANT} STREQUAL "stm32u545xx")) + target_sources(${TARGET}-tinyusb PUBLIC + ${TOP}/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c + ) + else () + target_sources(${TARGET}-tinyusb PUBLIC + ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c + #${TOP}/src/portable/st/typec/typec_stm32.c + ) + endif () target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) # Link dependencies diff --git a/hw/bsp/stm32u5/family.mk b/hw/bsp/stm32u5/family.mk index be5809340..89193b99f 100644 --- a/hw/bsp/stm32u5/family.mk +++ b/hw/bsp/stm32u5/family.mk @@ -27,18 +27,28 @@ LDFLAGS_GCC += \ --specs=nosys.specs --specs=nano.specs SRC_C += \ - src/portable/synopsys/dwc2/dcd_dwc2.c \ $(ST_CMSIS)/Source/Templates/system_stm32$(ST_FAMILY)xx.c \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal.c \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_cortex.c \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_gpio.c \ - $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_icache.c \ + $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_icache.c \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_pwr.c \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_pwr_ex.c \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_rcc.c \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_rcc_ex.c \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_uart.c +ifeq ($(MCU_VARIANT),stm32u545xx) +SRC_C += \ + src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +else ifeq ($(MCU_VARIANT),stm32u535xx) +SRC_C += \ + src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +else +SRC_C += \ + src/portable/synopsys/dwc2/dcd_dwc2.c +endif + INC += \ $(TOP)/lib/CMSIS_5/CMSIS/Core/Include \ $(TOP)/$(ST_CMSIS)/Include \ diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h index d6f3e22e2..2f97c0f23 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 = 0x80000000, + AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000u, } audio_data_format_type_I_t; /// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification @@ -640,7 +640,7 @@ typedef enum AUDIO_CHANNEL_CONFIG_BOTTOM_CENTER = 0x01000000, AUDIO_CHANNEL_CONFIG_BACK_LEFT_OF_CENTER = 0x02000000, AUDIO_CHANNEL_CONFIG_BACK_RIGHT_OF_CENTER = 0x04000000, - AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000, + AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000u, } audio_channel_config_t; /// AUDIO Channel Cluster Descriptor (4.1) diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 46db96ea9..8c86de433 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -2,6 +2,7 @@ * The MIT License (MIT) * * Copyright (c) 2020 Reinhard Panhuber, Jerzy Kasenberg + * Copyright (c) 2023 HiFiPhile * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -65,8 +66,10 @@ //--------------------------------------------------------------------+ // Use ring buffer if it's available, some MCUs need extra RAM requirements +// For DWC2 enable ring buffer will disable DMA (if available) #ifndef TUD_AUDIO_PREFER_RING_BUFFER - #if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX + #if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX || \ + defined(TUP_USBIP_DWC2) #define TUD_AUDIO_PREFER_RING_BUFFER 0 #else #define TUD_AUDIO_PREFER_RING_BUFFER 1 @@ -118,23 +121,23 @@ // 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 - IN_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ]; + tu_static IN_SW_BUF_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 + tu_static 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 - IN_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ]; + tu_static IN_SW_BUF_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 + tu_static 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 - IN_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ]; + tu_static IN_SW_BUF_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 + tu_static osal_mutex_def_t ep_in_ff_mutex_wr_3; // No need for read mutex as only USB driver reads from FIFO #endif #endif // CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0 #endif // CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING @@ -144,38 +147,38 @@ // - 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_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX]; + tu_static CFG_TUD_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_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX]; + tu_static CFG_TUD_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_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX]; + tu_static CFG_TUD_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 - OUT_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ]; + tu_static OUT_SW_BUF_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 + tu_static 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 - OUT_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ]; + tu_static OUT_SW_BUF_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 + tu_static 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 - OUT_SW_BUF_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ]; + tu_static OUT_SW_BUF_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 + tu_static osal_mutex_def_t ep_out_ff_mutex_rd_3; // No need for write mutex as only USB driver writes into FIFO #endif #endif // CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0 #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING @@ -185,89 +188,89 @@ // - 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_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX]; + tu_static CFG_TUD_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_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX]; + tu_static CFG_TUD_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_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX]; + tu_static CFG_TUD_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_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ]; +tu_static CFG_TUD_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_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ]; +tu_static CFG_TUD_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_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ]; +tu_static CFG_TUD_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 -uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT]; +tu_static 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 -uint8_t alt_setting_2[CFG_TUD_AUDIO_FUNC_2_N_AS_INT]; +tu_static 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 -uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT]; +tu_static 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]; - tu_fifo_t tx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO]; + tu_static 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_static 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 + tu_static 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]; - tu_fifo_t tx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO]; + tu_static 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_static 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 + tu_static 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]; - tu_fifo_t tx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO]; + tu_static 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_static 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 + tu_static 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 #endif #endif #endif #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]; - tu_fifo_t rx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO]; + tu_static 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_static 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 + tu_static 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]; - tu_fifo_t rx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO]; + tu_static 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_static 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 + tu_static 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]; - tu_fifo_t rx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO]; + tu_static 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_static 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 + tu_static 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 #endif #endif #endif @@ -300,32 +303,18 @@ typedef struct bool mounted; // Device opened - /*------------- From this point, data is not cleared by bus reset -------------*/ - uint16_t desc_length; // Length of audio function descriptor - // Buffer for control requests - uint8_t * ctrl_buf; - uint8_t ctrl_buf_sz; - - // Current active alternate settings - uint8_t * alt_setting; // We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP! - - // EP Transfer buffers and FIFOs -#if CFG_TUD_AUDIO_ENABLE_EP_OUT -#if !CFG_TUD_AUDIO_ENABLE_DECODING - tu_fifo_t ep_out_ff; -#endif - #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP struct { - CFG_TUSB_MEM_ALIGN uint32_t value; // Feedback value for asynchronous mode (in 16.16 format). + CFG_TUSB_MEM_ALIGN uint32_t send_buf; + uint32_t value; // Feedback value for asynchronous mode (in 16.16 format). uint32_t min_value; // min value according to UAC2 FMT-2.0 section 2.3.1.1. uint32_t max_value; // max value according to UAC2 FMT-2.0 section 2.3.1.1. uint8_t frame_shift; // bInterval-1 in unit of frame (FS), micro-frame (HS) uint8_t compute_method; - + bool format_correction; union { uint8_t power_of_2; // pre-computed power of 2 shift float float_const; // pre-computed float constant @@ -335,28 +324,17 @@ typedef struct uint32_t mclk_freq; }fixed; -#if 0 // implement later struct { - uint32_t nominal_value; - uint32_t threshold_bytes; + uint32_t nom_value; // In 16.16 format + uint32_t fifo_lvl_avg; // In 16.16 format + uint16_t fifo_lvl_thr; // fifo level threshold + uint16_t rate_const[2]; // pre-computed feedback/fifo_depth rate }fifo_count; -#endif }compute; } feedback; #endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT - -#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING - tu_fifo_t ep_in_ff; -#endif - - // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74) -#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP - CFG_TUSB_MEM_ALIGN uint8_t ep_int_buf[6]; -#endif - // Decoding parameters - parameters are set when alternate AS interface is set by host // Coding is currently only supported for EP. Software coding corresponding to AS interfaces without EPs are not supported currently. #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING @@ -365,8 +343,7 @@ typedef struct #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING audio_data_format_type_I_t format_type_I_rx; - uint8_t n_bytes_per_sampe_rx; - uint8_t n_channels_per_ff_rx; + uint8_t n_bytes_per_sample_rx; uint8_t n_ff_used_rx; #endif #endif @@ -382,26 +359,54 @@ typedef struct #if CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL) audio_format_type_t format_type_tx; uint8_t n_channels_tx; - uint8_t n_bytes_per_sampe_tx; + uint8_t n_bytes_per_sample_tx; #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING audio_data_format_type_I_t format_type_I_tx; - uint8_t n_channels_per_ff_tx; uint8_t n_ff_used_tx; #endif #endif + /*------------- From this point, data is not cleared by bus reset -------------*/ + + // Buffer for control requests + uint8_t * ctrl_buf; + uint8_t ctrl_buf_sz; + + // Current active alternate settings + uint8_t * alt_setting; // We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP! + + // EP Transfer buffers and FIFOs +#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING + tu_fifo_t ep_out_ff; +#endif + +#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING + tu_fifo_t ep_in_ff; +#endif + + // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74) +#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP + CFG_TUSB_MEM_ALIGN uint8_t ep_int_buf[6]; +#endif + // Support FIFOs for software encoding and decoding #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING tu_fifo_t * rx_supp_ff; uint8_t n_rx_supp_ff; uint16_t rx_supp_ff_sz_max; +#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING + uint8_t n_channels_per_ff_rx; +#endif #endif #if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING tu_fifo_t * tx_supp_ff; uint8_t n_tx_supp_ff; uint16_t tx_supp_ff_sz_max; +#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING + uint8_t n_channels_per_ff_tx; +#endif #endif // Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR the support FIFOs are used @@ -427,10 +432,147 @@ typedef struct #define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ctrl_buf) +//--------------------------------------------------------------------+ +// WEAK FUNCTION STUBS +//--------------------------------------------------------------------+ + +#if CFG_TUD_AUDIO_ENABLE_EP_IN +TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting) { + (void) rhport; + (void) func_id; + (void) ep_in; + (void) cur_alt_setting; + return true; +} + +TU_ATTR_WEAK bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting) { + (void) rhport; + (void) n_bytes_copied; + (void) func_id; + (void) ep_in; + (void) cur_alt_setting; + return true; +} +#endif + +#if CFG_TUD_AUDIO_ENABLE_EP_OUT +TU_ATTR_WEAK bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) { + (void) rhport; + (void) n_bytes_received; + (void) func_id; + (void) ep_out; + (void) cur_alt_setting; + return true; +} + +TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) { + (void) rhport; + (void) n_bytes_received; + (void) func_id; + (void) ep_out; + (void) cur_alt_setting; + return true; +} +#endif + +#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP +TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id) { + (void) func_id; +} + +TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param) { + (void) func_id; + (void) alt_itf; + feedback_param->method = AUDIO_FEEDBACK_METHOD_DISABLED; +} + +TU_ATTR_WEAK bool tud_audio_feedback_format_correction_cb(uint8_t func_id) { + (void) func_id; + return CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION; +} +#endif + +TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift) { + (void) func_id; + (void) frame_number; + (void) interval_shift; +} + +#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP +TU_ATTR_WEAK void tud_audio_int_done_cb(uint8_t rhport) { + (void) rhport; +} +#endif + +// Invoked when audio set interface request received +TU_ATTR_WEAK bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) { + (void) rhport; + (void) p_request; + return true; +} + +// Invoked when audio set interface request received which closes an EP +TU_ATTR_WEAK bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) { + (void) rhport; + (void) p_request; + return true; +} + +// Invoked when audio class specific set request received for an EP +TU_ATTR_WEAK bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) { + (void) rhport; + (void) p_request; + (void) pBuff; + 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 +} + +// Invoked when audio class specific set request received for an interface +TU_ATTR_WEAK bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) { + (void) rhport; + (void) p_request; + (void) pBuff; + 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 +} + +// Invoked when audio class specific set request received for an entity +TU_ATTR_WEAK bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) { + (void) rhport; + (void) p_request; + (void) pBuff; + 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 +} + +// Invoked when audio class specific get request received for an EP +TU_ATTR_WEAK bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request) { + (void) rhport; + (void) p_request; + TU_LOG2(" No EP get request callback available!\r\n"); + return false; // Stall +} + +// Invoked when audio class specific get request received for an interface +TU_ATTR_WEAK bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) { + (void) rhport; + (void) p_request; + TU_LOG2(" No interface get request callback available!\r\n"); + return false; // Stall +} + +// Invoked when audio class specific get request received for an entity +TU_ATTR_WEAK bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request) { + (void) rhport; + (void) p_request; + TU_LOG2(" No entity get request callback available!\r\n"); + return false; // Stall +} + //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ -CFG_TUD_MEM_SECTION audiod_function_t _audiod_fct[CFG_TUD_AUDIO]; +tu_static CFG_TUD_MEM_SECTION audiod_function_t _audiod_fct[CFG_TUD_AUDIO]; #if CFG_TUD_AUDIO_ENABLE_EP_OUT static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received); @@ -473,7 +615,8 @@ static uint16_t audiod_tx_packet_size(const uint16_t* norminal_size, uint16_t da #endif #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq); +static bool audiod_set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq); +static void audiod_fb_fifo_count_update(audiod_function_t* audio, uint16_t lvl_new); #endif bool tud_audio_n_mounted(uint8_t func_id) @@ -554,19 +697,13 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t uint8_t const *dummy2; uint8_t idx_audio_fct = 0; - if (tud_audio_rx_done_pre_read_cb || tud_audio_rx_done_post_read_cb) - { - idx_audio_fct = audiod_get_audio_fct_idx(audio); - TU_VERIFY(audiod_get_AS_interface_index(audio->ep_out_as_intf_num, audio, &idxItf, &dummy2)); - } + idx_audio_fct = audiod_get_audio_fct_idx(audio); + TU_VERIFY(audiod_get_AS_interface_index(audio->ep_out_as_intf_num, audio, &idxItf, &dummy2)); // Call a weak callback here - a possibility for user to get informed an audio packet was received and data gets now loaded into EP FIFO (or decoded into support RX software FIFO) - if (tud_audio_rx_done_pre_read_cb) - { - TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf])); - } + TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf])); -#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT +#if CFG_TUD_AUDIO_ENABLE_DECODING switch (audio->format_type_rx) { @@ -615,13 +752,17 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz), false); #endif +#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + if(audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FIFO_COUNT) + { + audiod_fb_fifo_count_update(audio, tu_fifo_count(&audio->ep_out_ff)); + } +#endif + #endif // Call a weak callback here - a possibility for user to get informed decoding was completed - if (tud_audio_rx_done_post_read_cb) - { - TU_VERIFY(tud_audio_rx_done_post_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf])); - } + TU_VERIFY(tud_audio_rx_done_post_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf])); return true; } @@ -707,16 +848,16 @@ static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, u if (info.len_lin != 0) { 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]; + src = &audio->lin_buf_out[cnt_ff*audio->n_channels_per_ff_rx * audio->n_bytes_per_sample_rx]; dst_end = info.ptr_lin + info.len_lin; - src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_lin, dst_end, src, n_ff_used); + src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sample_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(audio->n_bytes_per_sampe_rx, info.ptr_wrap, dst_end, src, n_ff_used); + audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sample_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); } @@ -725,6 +866,13 @@ static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, u // Number of bytes should be a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX but checking makes no sense - no way to correct it // TU_VERIFY(cnt != n_bytes); +#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + if(audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FIFO_COUNT) + { + audiod_fb_fifo_count_update(audio, tu_fifo_count(&audio->rx_supp_ff[0])); + } +#endif + return true; } #endif //CFG_TUD_AUDIO_ENABLE_DECODING @@ -848,7 +996,7 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t * audio) // Call a weak callback here - a possibility for user to get informed former TX was completed and data gets now loaded into EP in buffer (in case FIFOs are used) or // if no FIFOs are used the user may use this call back to load its data into the EP IN buffer by use of tud_audio_n_write_ep_in_buffer(). - if (tud_audio_tx_done_pre_load_cb) TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf])); + TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf])); // Send everything in ISO EP FIFO uint16_t n_bytes_tx; @@ -911,7 +1059,7 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t * audio) #endif // Call a weak callback here - a possibility for user to get informed former TX was completed and how many bytes were loaded for the next frame - if (tud_audio_tx_done_post_load_cb) TU_VERIFY(tud_audio_tx_done_post_load_cb(rhport, n_bytes_tx, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf])); + TU_VERIFY(tud_audio_tx_done_post_load_cb(rhport, n_bytes_tx, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf])); return true; } @@ -1024,7 +1172,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi // Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT! nBytesPerFFToSend = tu_min16(nBytesPerFFToSend, audio->ep_in_sz / n_ff_used); // Round to full number of samples (flooring) - uint16_t const nSlotSize = audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx; + uint16_t const nSlotSize = audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx; nBytesPerFFToSend = (nBytesPerFFToSend / nSlotSize) * nSlotSize; #endif @@ -1036,7 +1184,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++) { - dst = &audio->lin_buf_in[cnt_ff*audio->n_channels_per_ff_tx*audio->n_bytes_per_sampe_tx]; + dst = &audio->lin_buf_in[cnt_ff*audio->n_channels_per_ff_tx*audio->n_bytes_per_sample_tx]; tu_fifo_get_read_info(&audio->tx_supp_ff[cnt_ff], &info); @@ -1044,7 +1192,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi { info.len_lin = tu_min16(nBytesPerFFToSend, info.len_lin); // Limit up to desired length 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); + dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sample_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); @@ -1053,7 +1201,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi if (info.len_wrap != 0) { 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); + audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sample_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); @@ -1067,9 +1215,38 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi // This function is called once a transmit of a feedback packet was successfully completed. Here, we get the next feedback value to be sent #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -static inline bool audiod_fb_send(uint8_t rhport, audiod_function_t *audio) +static inline bool audiod_fb_send(audiod_function_t *audio) { - return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->feedback.value, 4); + bool apply_correction = (TUSB_SPEED_FULL == tud_speed_get()) && audio->feedback.format_correction; + // Format the feedback value + if (apply_correction) + { + uint8_t * fb = (uint8_t *) &audio->feedback.send_buf; + + // For FS format is 10.14 + *(fb++) = (audio->feedback.value >> 2) & 0xFF; + *(fb++) = (audio->feedback.value >> 10) & 0xFF; + *(fb++) = (audio->feedback.value >> 18) & 0xFF; + *fb = 0; + } else + { + audio->feedback.send_buf = audio->feedback.value; + } + + // About feedback format on FS + // + // 3 variables: Format | packetSize | sendSize | Working OS: + // 16.16 4 4 Linux, Windows + // 16.16 4 3 Linux + // 16.16 3 4 Linux + // 16.16 3 3 Linux + // 10.14 4 4 Linux + // 10.14 4 3 Linux + // 10.14 3 4 Linux, OSX + // 10.14 3 3 Linux, OSX + // + // We send 3 bytes since sending packet larger than wMaxPacketSize is pretty ugly + return usbd_edpt_xfer(audio->rhport, audio->ep_fb, (uint8_t *) &audio->feedback.send_buf, apply_correction ? 3 : 4); } #endif @@ -1490,7 +1667,8 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin #endif uint8_t const *p_desc = _audiod_fct[i].p_desc; uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN; - while (p_desc < p_desc_end) + // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning + while (p_desc_end - p_desc > 0) { if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) { @@ -1686,7 +1864,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * #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)); + TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request)); audio->ep_in = 0; // Necessary? @@ -1717,7 +1895,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * #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)); + TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request)); audio->ep_out = 0; // Necessary? @@ -1740,7 +1918,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN; // p_desc starts at required interface with alternate setting zero - while (p_desc < p_desc_end) + // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning + while (p_desc_end - p_desc > 0) { // Find correct interface if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == alt) @@ -1750,7 +1929,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * #endif // From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary uint8_t foundEPs = 0, nEps = ((tusb_desc_interface_t const * )p_desc)->bNumEndpoints; - while (foundEPs < nEps && p_desc < p_desc_end) + // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning + while (foundEPs < nEps && (p_desc_end - p_desc > 0)) { if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) { @@ -1779,8 +1959,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap #if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING - const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / (audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx)) - * (audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx)); + const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / (audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx)) + * (audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx)); for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++) { tu_fifo_config(&audio->tx_supp_ff[cnt], audio->tx_supp_ff[cnt].buffer, active_fifo_depth, 1, true); @@ -1810,7 +1990,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING - const uint16_t active_fifo_depth = (audio->rx_supp_ff_sz_max / audio->n_bytes_per_sampe_rx) * audio->n_bytes_per_sampe_rx; + const uint16_t active_fifo_depth = (audio->rx_supp_ff_sz_max / audio->n_bytes_per_sample_rx) * audio->n_bytes_per_sample_rx; for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++) { tu_fifo_config(&audio->rx_supp_ff[cnt], audio->rx_supp_ff[cnt].buffer, active_fifo_depth, 1, true); @@ -1833,9 +2013,6 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * { audio->ep_fb = ep_addr; audio->feedback.frame_shift = desc_ep->bInterval -1; - - // Enable SOF interrupt if callback is implemented - if (tud_audio_feedback_interval_isr) usbd_sof_enable(rhport, SOF_CONSUMER_AUDIO, true); } #endif #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT @@ -1848,20 +2025,23 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * TU_VERIFY(foundEPs == nEps); // Invoke one callback for a final set interface - if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request)); + TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request)); #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP - // Prepare feedback computation if callback is available - if (tud_audio_feedback_params_cb) + // Prepare feedback computation if endpoint is available + if(audio->ep_fb != 0) { audio_feedback_params_t fb_param; tud_audio_feedback_params_cb(func_id, alt, &fb_param); audio->feedback.compute_method = fb_param.method; + if(TUSB_SPEED_FULL == tud_speed_get()) + audio->feedback.format_correction = tud_audio_feedback_format_correction_cb(func_id); + // Minimal/Maximum value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame) uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000; - audio->feedback.min_value = (fb_param.sample_freq/frame_div - 1) << 16; + audio->feedback.min_value = ((fb_param.sample_freq - 1)/frame_div) << 16; audio->feedback.max_value = (fb_param.sample_freq/frame_div + 1) << 16; switch(fb_param.method) @@ -1869,20 +2049,32 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT: case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2: - set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq); + audiod_set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq); break; - #if 0 // implement later case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: { - uint64_t fb64 = ((uint64_t) fb_param.sample_freq) << 16; - audio->feedback.compute.fifo_count.nominal_value = (uint32_t) (fb64 / frame_div); - audio->feedback.compute.fifo_count.threshold_bytes = fb_param.fifo_count.threshold_bytes; - - tud_audio_fb_set(audio->feedback.compute.fifo_count.nominal_value); + // Initialize the threshold level to half filled + uint16_t fifo_lvl_thr; +#if CFG_TUD_AUDIO_ENABLE_DECODING + fifo_lvl_thr = tu_fifo_depth(&audio->rx_supp_ff[0]) / 2; +#else + fifo_lvl_thr = tu_fifo_depth(&audio->ep_out_ff) / 2; +#endif + audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_lvl_thr; + audio->feedback.compute.fifo_count.fifo_lvl_avg = ((uint32_t)fifo_lvl_thr) << 16; + // Avoid 64bit division + uint32_t nominal = ((fb_param.sample_freq / 100) << 16) / (frame_div / 100); + audio->feedback.compute.fifo_count.nom_value = nominal; + audio->feedback.compute.fifo_count.rate_const[0] = (audio->feedback.max_value - nominal) / fifo_lvl_thr; + audio->feedback.compute.fifo_count.rate_const[1] = (nominal - audio->feedback.min_value) / fifo_lvl_thr; + // On HS feedback is more sensitive since packet size can vary every MSOF, could cause instability + if(tud_speed_get() == TUSB_SPEED_HIGH) { + audio->feedback.compute.fifo_count.rate_const[0] /= 8; + audio->feedback.compute.fifo_count.rate_const[1] /= 8; + } } break; - #endif // nothing to do default: break; @@ -1900,16 +2092,19 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP // Disable SOF interrupt if no driver has any enabled feedback EP - bool disable = true; + bool enable_sof = false; for(uint8_t i=0; i < CFG_TUD_AUDIO; i++) { - if (_audiod_fct[i].ep_fb != 0) + if (_audiod_fct[i].ep_fb != 0 && + (_audiod_fct[i].feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED || + _audiod_fct[i].feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT || + _audiod_fct[i].feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2 )) { - disable = false; + enable_sof = true; break; } } - if (disable) usbd_sof_enable(rhport, SOF_CONSUMER_AUDIO, false); + usbd_sof_enable(rhport, SOF_CONSUMER_AUDIO, enable_sof); #endif #if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL @@ -1939,35 +2134,19 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const 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)); + // 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 - } + // Invoke callback + return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf); } 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)); + // 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 - } + // Invoke callback + return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf); } } break; @@ -1976,19 +2155,11 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const { uint8_t ep = TU_U16_LOW(p_request->wIndex); - 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)); + // 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 - } + // Invoke callback + return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf); } break; // Unknown/Unsupported recipient @@ -2045,15 +2216,7 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const // 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) { - 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 - } + return tud_audio_get_req_entity_cb(rhport, p_request); } } else @@ -2064,15 +2227,7 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const // 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) { - 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 - } + return tud_audio_get_req_itf_cb(rhport, p_request); } } } @@ -2088,15 +2243,7 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const // 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) { - 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 - } + return tud_audio_get_req_ep_cb(rhport, p_request); } } break; @@ -2151,7 +2298,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 // I assume here, that things above are handled by PHY // All transmission is done - what remains to do is to inform job was completed - if (tud_audio_int_done_cb) tud_audio_int_done_cb(rhport); + tud_audio_int_done_cb(rhport); return true; } @@ -2192,13 +2339,13 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 // Transmission of feedback EP finished if (audio->ep_fb == ep_addr) { - if (tud_audio_fb_done_cb) tud_audio_fb_done_cb(func_id); + tud_audio_fb_done_cb(func_id); // Schedule a transmit with the new value if EP is not busy - if (!usbd_edpt_busy(rhport, audio->ep_fb)) + if (usbd_edpt_claim(rhport, audio->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, audio); + return audiod_fb_send(audio); } } #endif @@ -2210,7 +2357,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq) +static bool audiod_set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq) { // Check if frame interval is within sane limits // The interval value n_frames was taken from the descriptors within audiod_set_interface() @@ -2229,11 +2376,11 @@ static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, u if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq)) { audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2; - audio->feedback.compute.power_of_2 = 16 - audio->feedback.frame_shift - tu_log2(mclk_freq / sample_freq); + audio->feedback.compute.power_of_2 = 16 - (audio->feedback.frame_shift - 1) - tu_log2(mclk_freq / sample_freq); } else if ( audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) { - audio->feedback.compute.float_const = (float)sample_freq / mclk_freq * (1UL << (16 - audio->feedback.frame_shift)); + audio->feedback.compute.float_const = (float)sample_freq / mclk_freq * (1UL << (16 - (audio->feedback.frame_shift - 1))); } else { @@ -2244,6 +2391,38 @@ static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, u return true; } +static void audiod_fb_fifo_count_update(audiod_function_t* audio, uint16_t lvl_new) +{ + /* Low-pass (averaging) filter */ + uint32_t lvl = audio->feedback.compute.fifo_count.fifo_lvl_avg; + lvl = (uint32_t)(((uint64_t)lvl * 63 + ((uint32_t)lvl_new << 16)) >> 6); + audio->feedback.compute.fifo_count.fifo_lvl_avg = lvl; + + uint32_t const ff_lvl = lvl >> 16; + uint16_t const ff_thr = audio->feedback.compute.fifo_count.fifo_lvl_thr; + uint16_t const *rate = audio->feedback.compute.fifo_count.rate_const; + + uint32_t feedback; + + if(ff_lvl < ff_thr) + { + feedback = audio->feedback.compute.fifo_count.nom_value + (ff_thr - ff_lvl) * rate[0]; + } else + { + feedback = audio->feedback.compute.fifo_count.nom_value - (ff_lvl - ff_thr) * rate[1]; + } + + if ( feedback > audio->feedback.max_value ) feedback = audio->feedback.max_value; + if ( feedback < audio->feedback.min_value ) feedback = audio->feedback.min_value; + audio->feedback.value = feedback; + + // Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value + if (usbd_edpt_claim(audio->rhport, audio->ep_fb)) + { + audiod_fb_send(audio); + } +} + uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) { audiod_function_t* audio = &_audiod_fct[func_id]; @@ -2261,7 +2440,7 @@ uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: { - uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - audio->feedback.frame_shift); + uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - (audio->feedback.frame_shift - 1)); feedback = (uint32_t) (fb64 / audio->feedback.compute.fixed.mclk_freq); } break; @@ -2280,6 +2459,21 @@ uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) return feedback; } + +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); + + _audiod_fct[func_id].feedback.value = feedback; + + // Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value + if (usbd_edpt_claim(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_fb)) + { + return audiod_fb_send(&_audiod_fct[func_id]); + } + + return true; +} #endif TU_ATTR_FAST_FUNC void audiod_sof_isr (uint8_t rhport, uint32_t frame_count) @@ -2306,7 +2500,7 @@ TU_ATTR_FAST_FUNC void audiod_sof_isr (uint8_t rhport, uint32_t frame_count) uint32_t const interval = 1UL << (audio->feedback.frame_shift - hs_adjust); if ( 0 == (frame_count & (interval-1)) ) { - if(tud_audio_feedback_interval_isr) tud_audio_feedback_interval_isr(i, frame_count, audio->feedback.frame_shift); + tud_audio_feedback_interval_isr(i, frame_count, audio->feedback.frame_shift); } } } @@ -2394,7 +2588,8 @@ static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t * audio p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength; uint8_t tmp = 0; - while (p_desc < p_desc_end) + // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning + while (p_desc_end - p_desc > 0) { // 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)->bAlternateSetting == 0) @@ -2447,7 +2642,8 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t * uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength + p_desc; p_desc = tu_desc_next(p_desc); // Get past CS AC descriptor - while (p_desc < p_desc_end) + // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning + while (p_desc_end - p_desc > 0) { if (p_desc[3] == entityID) // Entity IDs are always at offset 3 { @@ -2471,8 +2667,8 @@ static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id) // Get pointer at beginning and end uint8_t const *p_desc = _audiod_fct[i].p_desc; uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN; - - while (p_desc < p_desc_end) + // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning + while (p_desc_end - p_desc > 0) { if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)_audiod_fct[i].p_desc)->bInterfaceNumber == itf) { @@ -2500,7 +2696,8 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id) uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc); p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength; - while (p_desc < p_desc_end) + // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning + while (p_desc_end - p_desc > 0) { if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT && ((tusb_desc_endpoint_t const * )p_desc)->bEndpointAddress == ep) { @@ -2531,8 +2728,8 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * #endif p_desc = tu_desc_next(p_desc); // Exclude standard AS interface descriptor of current alternate interface descriptor - - while (p_desc < p_desc_end) + // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning + while (p_desc_end - p_desc > 0) { // Abort if follow up descriptor is a new standard interface descriptor - indicates the last AS descriptor was already finished if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) break; @@ -2581,14 +2778,14 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * #if CFG_TUD_AUDIO_ENABLE_EP_IN if (as_itf == audio->ep_in_as_intf_num) { - audio->n_bytes_per_sampe_tx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize; + audio->n_bytes_per_sample_tx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize; } #endif #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING if (as_itf == audio->ep_out_as_intf_num) { - audio->n_bytes_per_sampe_rx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize; + audio->n_bytes_per_sample_rx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize; } #endif } @@ -2607,7 +2804,7 @@ static bool audiod_calc_tx_packet_sz(audiod_function_t* audio) { TU_VERIFY(audio->format_type_tx == AUDIO_FORMAT_TYPE_I); TU_VERIFY(audio->n_channels_tx); - TU_VERIFY(audio->n_bytes_per_sampe_tx); + TU_VERIFY(audio->n_bytes_per_sample_tx); TU_VERIFY(audio->interval_tx); TU_VERIFY(audio->sample_rate_tx); @@ -2616,9 +2813,9 @@ static bool audiod_calc_tx_packet_sz(audiod_function_t* audio) const uint16_t sample_normimal = (uint16_t)(audio->sample_rate_tx * interval / ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000)); const uint16_t sample_reminder = (uint16_t)(audio->sample_rate_tx * interval % ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000)); - const uint16_t packet_sz_tx_min = (uint16_t)((sample_normimal - 1) * audio->n_channels_tx * audio->n_bytes_per_sampe_tx); - const uint16_t packet_sz_tx_norm = (uint16_t)(sample_normimal * audio->n_channels_tx * audio->n_bytes_per_sampe_tx); - const uint16_t packet_sz_tx_max = (uint16_t)((sample_normimal + 1) * audio->n_channels_tx * audio->n_bytes_per_sampe_tx); + const uint16_t packet_sz_tx_min = (uint16_t)((sample_normimal - 1) * audio->n_channels_tx * audio->n_bytes_per_sample_tx); + const uint16_t packet_sz_tx_norm = (uint16_t)(sample_normimal * audio->n_channels_tx * audio->n_bytes_per_sample_tx); + const uint16_t packet_sz_tx_max = (uint16_t)((sample_normimal + 1) * audio->n_channels_tx * audio->n_bytes_per_sample_tx); // Endpoint size must larger than packet size TU_ASSERT(packet_sz_tx_max <= audio->ep_in_sz); @@ -2691,44 +2888,8 @@ static uint16_t audiod_tx_packet_size(const uint16_t* norminal_size, uint16_t da #endif -#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP - -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 CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION - if ( TUSB_SPEED_FULL == tud_speed_get() ) - { - uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].feedback.value; - - // 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 -#else - { - // Send value as-is, caller will choose the appropriate format - _audiod_fct[func_id].feedback.value = 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)) - { - return audiod_fb_send(_audiod_fct[func_id].rhport, &_audiod_fct[func_id]); - } - - return true; -} -#endif - // No security checks here - internal function only which should always succeed -uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio) +static uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio) { for (uint8_t cnt=0; cnt < CFG_TUD_AUDIO; cnt++) { diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index b16514fd4..ae253f49d 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -3,6 +3,7 @@ * * Copyright (c) 2020 Ha Thach (tinyusb.org) * Copyright (c) 2020 Reinhard Panhuber + * Copyright (c) 2023 HiFiPhile * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -192,6 +193,7 @@ #endif // Enable/disable conversion from 16.16 to 10.14 format on full-speed devices. See tud_audio_n_fb_set(). +// Can be override by tud_audio_feedback_format_correction_cb() #ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION #define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0 // 0 or 1 #endif @@ -242,7 +244,8 @@ // Enable encoding/decodings - for these to work, support FIFOs need to be setup in appropriate numbers and size // The actual coding parameters of active AS alternate interface is parsed from the descriptors -// The item size of the FIFO is always fixed to one i.e. bytes! Furthermore, the actively used FIFO depth is reconfigured such that the depth is a multiple of the current sample size in order to avoid samples to get split up in case of a wrap in the FIFO ring buffer (depth = (max_depth / sampe_sz) * sampe_sz)! +// The item size of the FIFO is always fixed to one i.e. bytes! Furthermore, the actively used FIFO depth is reconfigured such that the depth is a multiple +// of the current sample size in order to avoid samples to get split up in case of a wrap in the FIFO ring buffer (depth = (max_depth / sample_sz) * sample_sz)! // This is important to remind in case you use DMAs! If the sample sizes changes, the DMA MUST BE RECONFIGURED just like the FIFOs for a different depth!!! // For PCM encoding/decoding @@ -446,63 +449,80 @@ static inline bool tud_audio_int_write (const audio_interru bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len); //--------------------------------------------------------------------+ -// Application Callback API (weak is optional) +// Application Callback API //--------------------------------------------------------------------+ #if CFG_TUD_AUDIO_ENABLE_EP_IN -TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting); -TU_ATTR_WEAK bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting); +bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting); +bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting); #endif #if CFG_TUD_AUDIO_ENABLE_EP_OUT -TU_ATTR_WEAK bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting); -TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting); +bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting); +bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting); #endif #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP -TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id); +void tud_audio_fb_done_cb(uint8_t func_id); -// determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined e.g. from some fill status of some FIFO buffer. Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load, disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 8 frames), i.e. a larger delay is introduced. +// Note about feedback calculation +// +// Option 1 - AUDIO_FEEDBACK_METHOD_FIFO_COUNT +// Feedback value is calculated within the audio driver by regulating the FIFO level to half fill. +// Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load, well tested +// (Windows, Linux, OSX) with a reliable result so far. +// Disadvantage: A FIFO of minimal 4 frames is needed to compensate for jitter, an average delay of 2 frames is introduced. +// +// Option 2 - AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED / AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT +// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information about the master clock f_m from +// which the audio sample frequency f_s is derived, f_s itself, and the cycle count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter). +// See tud_audio_set_fb_params() and tud_audio_feedback_update() +// Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller and thus a smaller delay is possible. +// Disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. (The most critical point is the reading of the cycle counter value of f_m. +// It is read from within the SOF ISR - see: audiod_sof() -, hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is introduced). +// Long-term drift could occur since error is accumulated. +// +// Option 3 - manual +// Determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined e.g. from some fill status of some FIFO buffer. +// Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load. +// Disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 6 frames), i.e. a larger delay is introduced. -// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information about the master clock f_m from which the audio sample frequency f_s is derived, f_s itself, and the cycle count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter) - see tud_audio_set_fb_params(). Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller (e.g. 2 frames) and thus a smaller delay is possible, disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. This option is a great starting point to try the SOF ISR option but depending on your hardware setup (performance of the CPU) it might not work. If so, figure out why and use the next option. (The most critical point is the reading of the cycle counter value of f_m. It is read from within the SOF ISR - see: audiod_sof() -, hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is introduced). - -// Feedback value is determined by the user by use of SOF interrupt. The user may use tud_audio_sof_isr() which is called every SOF (of course only invoked when an alternate interface other than zero was set). The number of frames used to determine the feedback value for the currently active alternate setting can be get by tud_audio_get_fb_n_frames(). The feedback value must be set by use of tud_audio_n_fb_set(). // This function is used to provide data rate feedback from an asynchronous sink. Feedback value will be sent at FB endpoint interval till it's changed. // // The feedback format is specified to be 16.16 for HS and 10.14 for FS devices (see Universal Serial Bus Specification Revision 2.0 5.12.4.2). By default, -// the choice of format is left to the caller and feedback argument is sent as-is. If CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION is set, then tinyusb -// expects 16.16 format and handles the conversion to 10.14 on FS. +// the choice of format is left to the caller and feedback argument is sent as-is. If CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION is set or tud_audio_feedback_format_correction_cb() +// return true, then tinyusb expects 16.16 format and handles the conversion to 10.14 on FS. +// +// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and it seems the +// driver can work with either format. // -// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and macOS it seems the -// driver can work with either format. So a good compromise is to keep format correction disabled and stick to 16.16 format. - // Feedback value can be determined from within the SOF ISR of the audio driver. This should reduce jitter. If the feature is used, the user can not set the feedback value. - +// // Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec // Boiled down, the feedback value Ff = n_samples / (micro)frame. -// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13 for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s) +// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13 +// for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s) // The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where P = min( ceil(log2(f_m / f_s)), K) // feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles within fb_n_frames - bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback); -static inline bool tud_audio_fb_set(uint32_t feedback); -// Update feedback value with passed cycles since last time this update function is called. +// Update feedback value with passed MCLK cycles since last time this update function is called. // Typically called within tud_audio_sof_isr(). Required tud_audio_feedback_params_cb() is implemented // This function will also call tud_audio_feedback_set() // return feedback value in 16.16 for reference (0 for error) +// Example : +// binterval=3 (4ms); FS = 48kHz; MCLK = 12.288MHz +// In 4 SOF MCLK counted 49152 cycles uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles); enum { AUDIO_FEEDBACK_METHOD_DISABLED, AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED, AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT, - AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2, - - // impelemnt later - // AUDIO_FEEDBACK_METHOD_FIFO_COUNT + AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2, // For driver internal use only + AUDIO_FEEDBACK_METHOD_FIFO_COUNT }; typedef struct { @@ -514,52 +534,50 @@ typedef struct { uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on }frequency; -#if 0 // implement later - struct { - uint32_t threshold_bytes; // minimum number of bytes received to be considered as filled/ready - }fifo_count; -#endif }; }audio_feedback_params_t; // Invoked when needed to set feedback parameters -TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param); +void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param); // Callback in ISR context, invoked periodically according to feedback endpoint bInterval. // Could be used to compute and update feedback value, should be placed in RAM if possible // frame_number : current SOF count // interval_shift: number of bit shift i.e log2(interval) from Feedback endpoint descriptor -TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift); +TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift); +// (Full-Speed only) Callback to set feedback format correction is applied or not, +// default to CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION if not implemented. +bool tud_audio_feedback_format_correction_cb(uint8_t func_id); #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP #if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP -TU_ATTR_WEAK void tud_audio_int_done_cb(uint8_t rhport); +void tud_audio_int_done_cb(uint8_t rhport); #endif // Invoked when audio set interface request received -TU_ATTR_WEAK bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request); +bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request); // Invoked when audio set interface request received which closes an EP -TU_ATTR_WEAK bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request); +bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request); // Invoked when audio class specific set request received for an EP -TU_ATTR_WEAK bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff); +bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff); // Invoked when audio class specific set request received for an interface -TU_ATTR_WEAK bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff); +bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff); // Invoked when audio class specific set request received for an entity -TU_ATTR_WEAK bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff); +bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff); // Invoked when audio class specific get request received for an EP -TU_ATTR_WEAK bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request); +bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request); // Invoked when audio class specific get request received for an interface -TU_ATTR_WEAK bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request); +bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request); // Invoked when audio class specific get request received for an entity -TU_ATTR_WEAK bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request); +bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request); //--------------------------------------------------------------------+ // Inline Functions diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index f36725eba..ebc408f5a 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -45,8 +45,7 @@ //--------------------------------------------------------------------+ #define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -typedef struct -{ +typedef struct { uint8_t itf_num; uint8_t ep_notif; uint8_t ep_in; @@ -56,7 +55,7 @@ typedef struct uint8_t line_state; /*------------- From this point, data is not cleared by bus reset -------------*/ - char wanted_char; + char wanted_char; TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // FIFO @@ -72,19 +71,22 @@ typedef struct // Endpoint Transfer buffer CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE]; CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE]; - -}cdcd_interface_t; +} cdcd_interface_t; #define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char) //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ -CFG_TUD_MEM_SECTION tu_static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; +CFG_TUD_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; +static tud_cdc_configure_fifo_t _cdcd_fifo_cfg; -static bool _prep_out_transaction (cdcd_interface_t* p_cdc) -{ +static bool _prep_out_transaction (cdcd_interface_t* p_cdc) { uint8_t const rhport = 0; + + // Skip if usb is not ready yet + TU_VERIFY(tud_ready() && p_cdc->ep_out); + uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff); // Prepare for incoming data but only allow what we can store in the ring buffer. @@ -99,14 +101,11 @@ static bool _prep_out_transaction (cdcd_interface_t* p_cdc) // fifo can be changed before endpoint is claimed available = tu_fifo_remaining(&p_cdc->rx_ff); - if ( available >= sizeof(p_cdc->epout_buf) ) - { + if ( available >= sizeof(p_cdc->epout_buf) ) { return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf)); - }else - { + }else { // Release endpoint since we don't make any transfer usbd_edpt_release(rhport, p_cdc->ep_out); - return false; } } @@ -114,51 +113,53 @@ static bool _prep_out_transaction (cdcd_interface_t* p_cdc) //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ -bool tud_cdc_n_connected(uint8_t itf) -{ + +bool tud_cdc_configure_fifo(tud_cdc_configure_fifo_t const* cfg) { + TU_VERIFY(cfg); + _cdcd_fifo_cfg = (*cfg); + return true; +} + +bool tud_cdc_n_ready(uint8_t itf) { + return tud_ready() && _cdcd_itf[itf].ep_in != 0 && _cdcd_itf[itf].ep_out != 0; +} + +bool tud_cdc_n_connected(uint8_t itf) { // DTR (bit 0) active is considered as connected return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0); } -uint8_t tud_cdc_n_get_line_state (uint8_t itf) -{ +uint8_t tud_cdc_n_get_line_state(uint8_t itf) { return _cdcd_itf[itf].line_state; } -void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding) -{ +void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding) { (*coding) = _cdcd_itf[itf].line_coding; } -void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted) -{ +void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted) { _cdcd_itf[itf].wanted_char = wanted; } - //--------------------------------------------------------------------+ // READ API //--------------------------------------------------------------------+ -uint32_t tud_cdc_n_available(uint8_t itf) -{ +uint32_t tud_cdc_n_available(uint8_t itf) { return tu_fifo_count(&_cdcd_itf[itf].rx_ff); } -uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) -{ +uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) { cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX)); _prep_out_transaction(p_cdc); return num_read; } -bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr) -{ +bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr) { return tu_fifo_peek(&_cdcd_itf[itf].rx_ff, chr); } -void tud_cdc_n_read_flush (uint8_t itf) -{ +void tud_cdc_n_read_flush(uint8_t itf) { cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; tu_fifo_clear(&p_cdc->rx_ff); _prep_out_transaction(p_cdc); @@ -167,16 +168,15 @@ void tud_cdc_n_read_flush (uint8_t itf) //--------------------------------------------------------------------+ // WRITE API //--------------------------------------------------------------------+ -uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) -{ +uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) { cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX)); // flush if queue more than packet size - if ( tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE - #if CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE - || tu_fifo_full(&p_cdc->tx_ff) // check full if fifo size is less than packet size - #endif + if (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE + #if CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE + || tu_fifo_full(&p_cdc->tx_ff) // check full if fifo size is less than packet size + #endif ) { tud_cdc_n_write_flush(itf); } @@ -184,30 +184,27 @@ uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) return ret; } -uint32_t tud_cdc_n_write_flush (uint8_t itf) -{ +uint32_t tud_cdc_n_write_flush(uint8_t itf) { cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; // Skip if usb is not ready yet - TU_VERIFY( tud_ready(), 0 ); + TU_VERIFY(tud_ready(), 0); // No data to send - if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0; + if (!tu_fifo_count(&p_cdc->tx_ff)) return 0; uint8_t const rhport = 0; // Claim the endpoint - TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 ); + TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_in), 0); // Pull data from FIFO uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf)); - if ( count ) - { - TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 ); + if (count) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0); return count; - }else - { + } else { // Release endpoint since we don't make any transfer // Note: data is dropped if terminal is not connected usbd_edpt_release(rhport, p_cdc->ep_in); @@ -215,33 +212,30 @@ uint32_t tud_cdc_n_write_flush (uint8_t itf) } } -uint32_t tud_cdc_n_write_available (uint8_t itf) -{ +uint32_t tud_cdc_n_write_available(uint8_t itf) { return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff); } -bool tud_cdc_n_write_clear (uint8_t itf) -{ +bool tud_cdc_n_write_clear(uint8_t itf) { return tu_fifo_clear(&_cdcd_itf[itf].tx_ff); } //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ -void cdcd_init(void) -{ +void cdcd_init(void) { tu_memclr(_cdcd_itf, sizeof(_cdcd_itf)); + tu_memclr(&_cdcd_fifo_cfg, sizeof(_cdcd_fifo_cfg)); - for(uint8_t i=0; iwanted_char = (char) -1; // default line coding is : stop bit = 1, parity = none, data bits = 8 - p_cdc->line_coding.bit_rate = 115200; + p_cdc->line_coding.bit_rate = 115200; p_cdc->line_coding.stop_bits = 0; - p_cdc->line_coding.parity = 0; + p_cdc->line_coding.parity = 0; p_cdc->line_coding.data_bits = 8; // Config RX fifo @@ -285,35 +279,28 @@ bool cdcd_deinit(void) { return true; } -void cdcd_reset(uint8_t rhport) -{ +void cdcd_reset(uint8_t rhport) { (void) rhport; - for(uint8_t i=0; irx_ff); - #if !CFG_TUD_CDC_PERSISTENT_TX_BUFF - tu_fifo_clear(&p_cdc->tx_ff); - #endif + if (!_cdcd_fifo_cfg.rx_persistent) tu_fifo_clear(&p_cdc->rx_ff); + if (!_cdcd_fifo_cfg.tx_persistent) tu_fifo_clear(&p_cdc->tx_ff); tu_fifo_set_overwritable(&p_cdc->tx_ff, true); } } -uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) -{ +uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) { // Only support ACM subclass TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass && CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0); // Find available interface - cdcd_interface_t * p_cdc = NULL; - for(uint8_t cdc_id=0; cdc_iditf_num = itf_desc->bInterfaceNumber; uint16_t drv_len = sizeof(tusb_desc_interface_t); - uint8_t const * p_desc = tu_desc_next( itf_desc ); + uint8_t const* p_desc = tu_desc_next(itf_desc); // Communication Functional Descriptors - while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len ) - { + while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) { drv_len += tu_desc_len(p_desc); - p_desc = tu_desc_next(p_desc); + p_desc = tu_desc_next(p_desc); } - if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) - { + if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) { // notification endpoint - tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; + tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc; - TU_ASSERT( usbd_edpt_open(rhport, desc_ep), 0 ); + 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); + p_desc = tu_desc_next(p_desc); } //------------- Data Interface (if any) -------------// - if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && - (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) - { + if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) { // next to endpoint descriptor drv_len += tu_desc_len(p_desc); - p_desc = tu_desc_next(p_desc); + p_desc = tu_desc_next(p_desc); // Open endpoint pair - TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0 ); + TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0); - drv_len += 2*sizeof(tusb_desc_endpoint_t); + drv_len += 2 * sizeof(tusb_desc_endpoint_t); } // Prepare for incoming data @@ -368,8 +352,7 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1 // Invoked when a control transfer occurred on an interface of this class // Driver response accordingly to the request and the transfer stage (setup/data/ack) // return false to stall control endpoint (e.g unsupported request) -bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) -{ +bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) { // Handle class request only TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); @@ -377,42 +360,33 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t cdcd_interface_t* p_cdc = _cdcd_itf; // Identify which interface to use - for ( ; ; itf++, p_cdc++) - { + for (;; itf++, p_cdc++) { if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; - if ( p_cdc->itf_num == request->wIndex ) break; + if (p_cdc->itf_num == request->wIndex) break; } - switch ( request->bRequest ) - { + switch (request->bRequest) { case CDC_REQUEST_SET_LINE_CODING: - if (stage == CONTROL_STAGE_SETUP) - { + if (stage == CONTROL_STAGE_SETUP) { TU_LOG_DRV(" Set Line Coding\r\n"); tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); + } else if (stage == CONTROL_STAGE_ACK) { + if (tud_cdc_line_coding_cb) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding); } - else if ( stage == CONTROL_STAGE_ACK) - { - if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding); - } - break; + break; case CDC_REQUEST_GET_LINE_CODING: - if (stage == CONTROL_STAGE_SETUP) - { + if (stage == CONTROL_STAGE_SETUP) { TU_LOG_DRV(" Get Line Coding\r\n"); tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); } - break; + break; case CDC_REQUEST_SET_CONTROL_LINE_STATE: - if (stage == CONTROL_STAGE_SETUP) - { + if (stage == CONTROL_STAGE_SETUP) { tud_control_status(rhport, request); - } - else if (stage == CONTROL_STAGE_ACK) - { + } else if (stage == CONTROL_STAGE_ACK) { // CDC PSTN v1.2 section 6.3.12 // Bit 0: Indicates if DTE is present or not. // This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready) @@ -429,61 +403,54 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts); // Invoke callback - if ( tud_cdc_line_state_cb ) tud_cdc_line_state_cb(itf, dtr, rts); + if (tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, dtr, rts); } - break; - case CDC_REQUEST_SEND_BREAK: - if (stage == CONTROL_STAGE_SETUP) - { - tud_control_status(rhport, request); - } - else if (stage == CONTROL_STAGE_ACK) - { - TU_LOG_DRV(" Send Break\r\n"); - if ( tud_cdc_send_break_cb ) tud_cdc_send_break_cb(itf, request->wValue); - } - break; + break; - default: return false; // stall unsupported request + case CDC_REQUEST_SEND_BREAK: + if (stage == CONTROL_STAGE_SETUP) { + tud_control_status(rhport, request); + } else if (stage == CONTROL_STAGE_ACK) { + TU_LOG_DRV(" Send Break\r\n"); + if (tud_cdc_send_break_cb) tud_cdc_send_break_cb(itf, request->wValue); + } + break; + + default: + return false; // stall unsupported request } return true; } -bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) -{ +bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { (void) result; uint8_t itf; cdcd_interface_t* p_cdc; // Identify which interface to use - for (itf = 0; itf < CFG_TUD_CDC; itf++) - { + for (itf = 0; itf < CFG_TUD_CDC; itf++) { p_cdc = &_cdcd_itf[itf]; - if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break; + if ((ep_addr == p_cdc->ep_out) || (ep_addr == p_cdc->ep_in)) break; } TU_ASSERT(itf < CFG_TUD_CDC); // Received new data - if ( ep_addr == p_cdc->ep_out ) - { + if (ep_addr == p_cdc->ep_out) { tu_fifo_write_n(&p_cdc->rx_ff, p_cdc->epout_buf, (uint16_t) xferred_bytes); // Check for wanted char and invoke callback if needed - if ( tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1) ) - { - for ( uint32_t i = 0; i < xferred_bytes; i++ ) - { - if ( (p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff) ) - { + if (tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1)) { + for (uint32_t i = 0; i < xferred_bytes; i++) { + if ((p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff)) { tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char); } } } // invoke receive callback (if there is still data) - if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf); + if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff)) tud_cdc_rx_cb(itf); // prepare for OUT transaction _prep_out_transaction(p_cdc); @@ -492,19 +459,15 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ // Data sent to host, we continue to fetch from tx fifo to send. // Note: This will cause incorrect baudrate set in line coding. // Though maybe the baudrate is not really important !!! - if ( ep_addr == p_cdc->ep_in ) - { + if (ep_addr == p_cdc->ep_in) { // invoke transmit callback to possibly refill tx fifo - if ( tud_cdc_tx_complete_cb ) tud_cdc_tx_complete_cb(itf); + if (tud_cdc_tx_complete_cb) tud_cdc_tx_complete_cb(itf); - if ( 0 == tud_cdc_n_write_flush(itf) ) - { + if (0 == tud_cdc_n_write_flush(itf)) { // If there is no data left, a ZLP should be sent if // xferred_bytes is multiple of EP Packet size and not zero - if ( !tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) ) - { - if ( usbd_edpt_claim(rhport, p_cdc->ep_in) ) - { + if (!tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE - 1)))) { + if (usbd_edpt_claim(rhport, p_cdc->ep_in)) { usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0); } } diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index db709b3bc..3ad7c8baf 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -24,8 +24,8 @@ * This file is part of the TinyUSB stack. */ -#ifndef _TUSB_CDC_DEVICE_H_ -#define _TUSB_CDC_DEVICE_H_ +#ifndef TUSB_CDC_DEVICE_H_ +#define TUSB_CDC_DEVICE_H_ #include "cdc.h" @@ -41,94 +41,148 @@ #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #endif -// By default the TX fifo buffer is cleared on connect / bus reset. -// Enable this to persist any data in the fifo instead. -#ifndef CFG_TUD_CDC_PERSISTENT_TX_BUFF - #define CFG_TUD_CDC_PERSISTENT_TX_BUFF (0) -#endif - #ifdef __cplusplus extern "C" { #endif -/** \addtogroup CDC_Serial Serial - * @{ - * \defgroup CDC_Serial_Device Device - * @{ */ +//--------------------------------------------------------------------+ +// Driver Configuration +//--------------------------------------------------------------------+ + +typedef struct TU_ATTR_PACKED { + uint8_t rx_persistent : 1; // keep rx fifo on bus reset or disconnect + uint8_t tx_persistent : 1; // keep tx fifo on bus reset or disconnect +} tud_cdc_configure_fifo_t; + +// Configure CDC FIFOs behavior +bool tud_cdc_configure_fifo(tud_cdc_configure_fifo_t const* cfg); //--------------------------------------------------------------------+ -// Application API (Multiple Ports) -// CFG_TUD_CDC > 1 +// Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1 //--------------------------------------------------------------------+ +// Check if interface is ready +bool tud_cdc_n_ready(uint8_t itf); + // Check if terminal is connected to this port -bool tud_cdc_n_connected (uint8_t itf); +bool tud_cdc_n_connected(uint8_t itf); // Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) -uint8_t tud_cdc_n_get_line_state (uint8_t itf); +uint8_t tud_cdc_n_get_line_state(uint8_t itf); // Get current line encoding: bit rate, stop bits parity etc .. -void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding); +void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding); // Set special character that will trigger tud_cdc_rx_wanted_cb() callback on receiving -void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted); +void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted); // Get the number of bytes available for reading -uint32_t tud_cdc_n_available (uint8_t itf); +uint32_t tud_cdc_n_available(uint8_t itf); // Read received bytes -uint32_t tud_cdc_n_read (uint8_t itf, void* buffer, uint32_t bufsize); +uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize); // Read a byte, return -1 if there is none -static inline -int32_t tud_cdc_n_read_char (uint8_t itf); +TU_ATTR_ALWAYS_INLINE static inline int32_t tud_cdc_n_read_char(uint8_t itf) { + uint8_t ch; + return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1; +} // Clear the received FIFO -void tud_cdc_n_read_flush (uint8_t itf); +void tud_cdc_n_read_flush(uint8_t itf); // Get a byte from FIFO without removing it -bool tud_cdc_n_peek (uint8_t itf, uint8_t* ui8); +bool tud_cdc_n_peek(uint8_t itf, uint8_t* ui8); // Write bytes to TX FIFO, data may remain in the FIFO for a while -uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bufsize); +uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize); // Write a byte -static inline -uint32_t tud_cdc_n_write_char (uint8_t itf, char ch); +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch) { + return tud_cdc_n_write(itf, &ch, 1); +} // Write a null-terminated string -static inline -uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str); +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_n_write_str(uint8_t itf, char const* str) { + return tud_cdc_n_write(itf, str, strlen(str)); +} // Force sending data if possible, return number of forced bytes -uint32_t tud_cdc_n_write_flush (uint8_t itf); +uint32_t tud_cdc_n_write_flush(uint8_t itf); // Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation. -uint32_t tud_cdc_n_write_available (uint8_t itf); +uint32_t tud_cdc_n_write_available(uint8_t itf); // Clear the transmit FIFO -bool tud_cdc_n_write_clear (uint8_t itf); +bool tud_cdc_n_write_clear(uint8_t itf); //--------------------------------------------------------------------+ // Application API (Single Port) //--------------------------------------------------------------------+ -static inline bool tud_cdc_connected (void); -static inline uint8_t tud_cdc_get_line_state (void); -static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding); -static inline void tud_cdc_set_wanted_char (char wanted); -static inline uint32_t tud_cdc_available (void); -static inline int32_t tud_cdc_read_char (void); -static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize); -static inline void tud_cdc_read_flush (void); -static inline bool tud_cdc_peek (uint8_t* ui8); +TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_ready(void) { + return tud_cdc_n_ready(0); +} -static inline uint32_t tud_cdc_write_char (char ch); -static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize); -static inline uint32_t tud_cdc_write_str (char const* str); -static inline uint32_t tud_cdc_write_flush (void); -static inline uint32_t tud_cdc_write_available (void); -static inline bool tud_cdc_write_clear (void); +TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_connected(void) { + return tud_cdc_n_connected(0); +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_cdc_get_line_state(void) { + return tud_cdc_n_get_line_state(0); +} + +TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_get_line_coding(cdc_line_coding_t* coding) { + tud_cdc_n_get_line_coding(0, coding); +} + +TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_set_wanted_char(char wanted) { + tud_cdc_n_set_wanted_char(0, wanted); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_available(void) { + return tud_cdc_n_available(0); +} + +TU_ATTR_ALWAYS_INLINE static inline int32_t tud_cdc_read_char(void) { + return tud_cdc_n_read_char(0); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_read(void* buffer, uint32_t bufsize) { + return tud_cdc_n_read(0, buffer, bufsize); +} + +TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_read_flush(void) { + tud_cdc_n_read_flush(0); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_peek(uint8_t* ui8) { + return tud_cdc_n_peek(0, ui8); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_char(char ch) { + return tud_cdc_n_write_char(0, ch); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write(void const* buffer, uint32_t bufsize) { + return tud_cdc_n_write(0, buffer, bufsize); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_str(char const* str) { + return tud_cdc_n_write_str(0, str); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_flush(void) { + return tud_cdc_n_write_flush(0); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_available(void) { + return tud_cdc_n_write_available(0); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_write_clear(void) { + return tud_cdc_n_write_clear(0); +} //--------------------------------------------------------------------+ // Application Callback API (weak is optional) @@ -152,103 +206,6 @@ TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p // Invoked when received send break TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms); -//--------------------------------------------------------------------+ -// Inline Functions -//--------------------------------------------------------------------+ -static inline int32_t tud_cdc_n_read_char (uint8_t itf) -{ - uint8_t ch; - return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1; -} - -static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch) -{ - return tud_cdc_n_write(itf, &ch, 1); -} - -static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str) -{ - return tud_cdc_n_write(itf, str, strlen(str)); -} - -static inline bool tud_cdc_connected (void) -{ - return tud_cdc_n_connected(0); -} - -static inline uint8_t tud_cdc_get_line_state (void) -{ - return tud_cdc_n_get_line_state(0); -} - -static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding) -{ - tud_cdc_n_get_line_coding(0, coding); -} - -static inline void tud_cdc_set_wanted_char (char wanted) -{ - tud_cdc_n_set_wanted_char(0, wanted); -} - -static inline uint32_t tud_cdc_available (void) -{ - return tud_cdc_n_available(0); -} - -static inline int32_t tud_cdc_read_char (void) -{ - return tud_cdc_n_read_char(0); -} - -static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize) -{ - return tud_cdc_n_read(0, buffer, bufsize); -} - -static inline void tud_cdc_read_flush (void) -{ - tud_cdc_n_read_flush(0); -} - -static inline bool tud_cdc_peek (uint8_t* ui8) -{ - return tud_cdc_n_peek(0, ui8); -} - -static inline uint32_t tud_cdc_write_char (char ch) -{ - return tud_cdc_n_write_char(0, ch); -} - -static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize) -{ - return tud_cdc_n_write(0, buffer, bufsize); -} - -static inline uint32_t tud_cdc_write_str (char const* str) -{ - return tud_cdc_n_write_str(0, str); -} - -static inline uint32_t tud_cdc_write_flush (void) -{ - return tud_cdc_n_write_flush(0); -} - -static inline uint32_t tud_cdc_write_available(void) -{ - return tud_cdc_n_write_available(0); -} - -static inline bool tud_cdc_write_clear(void) -{ - return tud_cdc_n_write_clear(0); -} - -/** @} */ -/** @} */ - //--------------------------------------------------------------------+ // INTERNAL USBD-CLASS DRIVER API //--------------------------------------------------------------------+ diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h index 2fe922136..c2b5a8a48 100644 --- a/src/class/hid/hid.h +++ b/src/class/hid/hid.h @@ -762,7 +762,7 @@ enum { HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14, HID_USAGE_PAGE_MEDICAL = 0x40, HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION = 0x59, - HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83 + HID_USAGE_PAGE_MONITOR = 0x80, // 0x80 - 0x83 HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87 HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c, HID_USAGE_PAGE_SCALE = 0x8d, @@ -770,7 +770,7 @@ enum { HID_USAGE_PAGE_CAMERA = 0x90, HID_USAGE_PAGE_ARCADE = 0x91, HID_USAGE_PAGE_FIDO = 0xF1D0, // FIDO alliance HID usage page - HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF + HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF }; /// HID Usage Table - Table 6: Generic Desktop Page diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c index ada01582e..ef6c7f3af 100644 --- a/src/class/hid/hid_device.c +++ b/src/class/hid/hid_device.c @@ -46,43 +46,68 @@ typedef struct { uint8_t itf_protocol; // Boot mouse or keyboard uint16_t report_desc_len; - CFG_TUSB_MEM_ALIGN uint8_t protocol_mode; // Boot (0) or Report protocol (1) - CFG_TUSB_MEM_ALIGN uint8_t idle_rate; // up to application to handle idle rate - - CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE]; - CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE]; - CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_HID_EP_BUFSIZE]; + uint8_t protocol_mode; // Boot (0) or Report protocol (1) + uint8_t idle_rate; // up to application to handle idle rate // TODO save hid descriptor since host can specifically request this after enumeration // Note: HID descriptor may be not available from application after enumeration tusb_hid_descriptor_hid_t const *hid_descriptor; + + uint8_t ctrl_buf[CFG_TUD_HID_EP_BUFSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE]; } hidd_interface_t; CFG_TUD_MEM_SECTION tu_static hidd_interface_t _hidd_itf[CFG_TUD_HID]; /*------------- Helpers -------------*/ -static inline uint8_t get_index_by_itfnum(uint8_t itf_num) -{ +TU_ATTR_ALWAYS_INLINE static inline uint8_t get_index_by_itfnum(uint8_t itf_num) { for (uint8_t i = 0; i < CFG_TUD_HID; i++) { - if (itf_num == _hidd_itf[i].itf_num) + if (itf_num == _hidd_itf[i].itf_num) { return i; + } } - return 0xFF; } +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol) { + (void) instance; + (void) protocol; +} + +TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate) { + (void) instance; + (void) idle_rate; + return true; +} + +TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len) { + (void) instance; + (void) report; + (void) len; +} + +// Invoked when a transfer wasn't successful +TU_ATTR_WEAK void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, uint8_t const* report, uint16_t xferred_bytes) { + (void) instance; + (void) report_type; + (void) report; + (void) xferred_bytes; +} + //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ -bool tud_hid_n_ready(uint8_t instance) -{ +bool tud_hid_n_ready(uint8_t instance) { uint8_t const rhport = 0; uint8_t const ep_in = _hidd_itf[instance].ep_in; return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(rhport, ep_in); } -bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const *report, uint16_t len) -{ +bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const *report, uint16_t len) { uint8_t const rhport = 0; hidd_interface_t *p_hid = &_hidd_itf[instance]; @@ -101,14 +126,16 @@ bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const *report, u return usbd_edpt_xfer(rhport, p_hid->ep_in, p_hid->epin_buf, len); } -uint8_t tud_hid_n_interface_protocol(uint8_t instance) { return _hidd_itf[instance].itf_protocol; } +uint8_t tud_hid_n_interface_protocol(uint8_t instance) { + return _hidd_itf[instance].itf_protocol; +} -uint8_t tud_hid_n_get_protocol(uint8_t instance) { return _hidd_itf[instance].protocol_mode; } +uint8_t tud_hid_n_get_protocol(uint8_t instance) { + return _hidd_itf[instance].protocol_mode; +} -bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6]) -{ +bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, const uint8_t keycode[6]) { hid_keyboard_report_t report; - report.modifier = modifier; report.reserved = 0; @@ -121,8 +148,8 @@ bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modi return tud_hid_n_report(instance, report_id, &report, sizeof(report)); } -bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal) -{ +bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, + uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal) { hid_mouse_report_t report = { .buttons = buttons, .x = x, @@ -134,8 +161,8 @@ bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons return tud_hid_n_report(instance, report_id, &report, sizeof(report)); } -bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) -{ +bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, + uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) { hid_abs_mouse_report_t report = { .buttons = buttons, .x = x, @@ -146,8 +173,8 @@ bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t but return tud_hid_n_report(instance, report_id, &report, sizeof(report)); } -bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) -{ +bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, + int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) { hid_gamepad_report_t report = { .x = x, .y = y, @@ -165,28 +192,25 @@ bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int //--------------------------------------------------------------------+ // USBD-CLASS API //--------------------------------------------------------------------+ -void hidd_init(void) -{ +void hidd_init(void) { hidd_reset(0); } -bool hidd_deinit(void) -{ +bool hidd_deinit(void) { return true; } -void hidd_reset(uint8_t rhport) -{ +void hidd_reset(uint8_t rhport) { (void)rhport; tu_memclr(_hidd_itf, sizeof(_hidd_itf)); } -uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16_t max_len) -{ +uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16_t max_len) { TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0); // len = interface + hid + n*endpoints - uint16_t const drv_len = (uint16_t)(sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t)); + uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t)); TU_ASSERT(max_len >= drv_len, 0); // Find available interface @@ -211,8 +235,9 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16 p_desc = tu_desc_next(p_desc); TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in), 0); - if (desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT) + if (desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT) { p_hid->itf_protocol = desc_itf->bInterfaceProtocol; + } p_hid->protocol_mode = HID_PROTOCOL_REPORT; // Per Specs: default is report mode p_hid->itf_num = desc_itf->bInterfaceNumber; @@ -234,8 +259,7 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16 // Invoked when a control transfer occurred on an interface of this class // Driver response accordingly to the request and the transfer stage (setup/data/ack) // return false to stall control endpoint (e.g unsupported request) -bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) -{ +bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) { TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); uint8_t const hid_itf = get_index_by_itfnum((uint8_t)request->wIndex); @@ -262,90 +286,82 @@ bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { //------------- Class Specific Request -------------// switch (request->bRequest) { - case HID_REQ_CONTROL_GET_REPORT: - if (stage == CONTROL_STAGE_SETUP) { - uint8_t const report_type = tu_u16_high(request->wValue); - uint8_t const report_id = tu_u16_low(request->wValue); + case HID_REQ_CONTROL_GET_REPORT: + if (stage == CONTROL_STAGE_SETUP) { + uint8_t const report_type = tu_u16_high(request->wValue); + uint8_t const report_id = tu_u16_low(request->wValue); - uint8_t *report_buf = p_hid->ctrl_buf; - uint16_t req_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE); + uint8_t* report_buf = p_hid->ctrl_buf; + uint16_t req_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE); + uint16_t xferlen = 0; - 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++; + } - // 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 += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, req_len); + TU_ASSERT(xferlen > 0); - xferlen++; + tud_control_xfer(rhport, request, p_hid->ctrl_buf, xferlen); } + break; - xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t)report_type, report_buf, req_len); - TU_ASSERT(xferlen > 0); + case HID_REQ_CONTROL_SET_REPORT: + if (stage == CONTROL_STAGE_SETUP) { + TU_VERIFY(request->wLength <= sizeof(p_hid->ctrl_buf)); + tud_control_xfer(rhport, request, p_hid->ctrl_buf, request->wLength); + } else if (stage == CONTROL_STAGE_ACK) { + uint8_t const report_type = tu_u16_high(request->wValue); + uint8_t const report_id = tu_u16_low(request->wValue); - tud_control_xfer(rhport, request, p_hid->ctrl_buf, xferlen); - } - break; + uint8_t const* report_buf = p_hid->ctrl_buf; + uint16_t report_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE); - case HID_REQ_CONTROL_SET_REPORT: - if (stage == CONTROL_STAGE_SETUP) { - TU_VERIFY(request->wLength <= sizeof(p_hid->ctrl_buf)); - tud_control_xfer(rhport, request, p_hid->ctrl_buf, request->wLength); - } else if (stage == CONTROL_STAGE_ACK) { - uint8_t const report_type = tu_u16_high(request->wValue); - uint8_t const report_id = tu_u16_low(request->wValue); + // 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--; + } - uint8_t const *report_buf = p_hid->ctrl_buf; - uint16_t report_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE); - - // 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; - tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t)report_type, report_buf, report_len); - } - break; - - case HID_REQ_CONTROL_SET_IDLE: - if (stage == CONTROL_STAGE_SETUP) { - p_hid->idle_rate = tu_u16_high(request->wValue); - if (tud_hid_set_idle_cb) { - // stall request if callback return false - TU_VERIFY(tud_hid_set_idle_cb(hid_itf, p_hid->idle_rate)); + case HID_REQ_CONTROL_SET_IDLE: + if (stage == CONTROL_STAGE_SETUP) { + p_hid->idle_rate = tu_u16_high(request->wValue); + TU_VERIFY(tud_hid_set_idle_cb(hid_itf, p_hid->idle_rate)); // stall if false + tud_control_status(rhport, request); } + break; - tud_control_status(rhport, request); - } - break; + case HID_REQ_CONTROL_GET_IDLE: + if (stage == CONTROL_STAGE_SETUP) { + // TODO idle rate of report + tud_control_xfer(rhport, request, &p_hid->idle_rate, 1); + } + break; - case HID_REQ_CONTROL_GET_IDLE: - if (stage == CONTROL_STAGE_SETUP) { - // TODO idle rate of report - tud_control_xfer(rhport, request, &p_hid->idle_rate, 1); - } - break; + case HID_REQ_CONTROL_GET_PROTOCOL: + if (stage == CONTROL_STAGE_SETUP) { + tud_control_xfer(rhport, request, &p_hid->protocol_mode, 1); + } + break; - case HID_REQ_CONTROL_GET_PROTOCOL: - if (stage == CONTROL_STAGE_SETUP) { - tud_control_xfer(rhport, request, &p_hid->protocol_mode, 1); - } - break; - - case HID_REQ_CONTROL_SET_PROTOCOL: - if (stage == CONTROL_STAGE_SETUP) { - tud_control_status(rhport, request); - } else if (stage == CONTROL_STAGE_ACK) { - p_hid->protocol_mode = (uint8_t)request->wValue; - if (tud_hid_set_protocol_cb) { + case HID_REQ_CONTROL_SET_PROTOCOL: + if (stage == CONTROL_STAGE_SETUP) { + tud_control_status(rhport, request); + } else if (stage == CONTROL_STAGE_ACK) { + p_hid->protocol_mode = (uint8_t) request->wValue; tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode); } - } - break; + break; - default: - return false; // stall unsupported request + default: + return false; // stall unsupported request } } else { return false; // stall unsupported request @@ -354,45 +370,35 @@ bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t return true; } -bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) -{ - (void)result; - +bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { uint8_t instance = 0; hidd_interface_t *p_hid = _hidd_itf; // Identify which interface to use for (instance = 0; instance < CFG_TUD_HID; instance++) { p_hid = &_hidd_itf[instance]; - if ((ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in)) + if ((ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in)) { break; + } } TU_ASSERT(instance < CFG_TUD_HID); - // Check if there was a problem - if (XFER_RESULT_SUCCESS != result) { // Inform application about the issue - if (tud_hid_report_fail_cb) { - tud_hid_report_fail_cb(instance, ep_addr, (uint16_t)xferred_bytes); - } - - // Allow a new transfer to be received if issue happened on an OUT endpoint - if (ep_addr == p_hid->ep_out) { - // Prepare the OUT endpoint to be able to receive a new transfer - TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf))); - } - - return true; - } - - // Sent report successfully if (ep_addr == p_hid->ep_in) { - if (tud_hid_report_complete_cb) { - tud_hid_report_complete_cb(instance, p_hid->epin_buf, (uint16_t)xferred_bytes); + // Input report + if (XFER_RESULT_SUCCESS == result) { + tud_hid_report_complete_cb(instance, p_hid->epin_buf, (uint16_t) xferred_bytes); + } else { + tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_INPUT, p_hid->epin_buf, (uint16_t) xferred_bytes); } - } - // Received report successfully - else if (ep_addr == p_hid->ep_out) { - tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_OUTPUT, p_hid->epout_buf, (uint16_t)xferred_bytes); + } else { + // Output report + if (XFER_RESULT_SUCCESS == result) { + tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_OUTPUT, p_hid->epout_buf, (uint16_t)xferred_bytes); + } else { + tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_OUTPUT, p_hid->epout_buf, (uint16_t) xferred_bytes); + } + + // prepare for new transfer TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf))); } diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h index fcbf161c4..ab2e27373 100644 --- a/src/class/hid/hid_device.h +++ b/src/class/hid/hid_device.h @@ -24,8 +24,8 @@ * This file is part of the TinyUSB stack. */ -#ifndef _TUSB_HID_DEVICE_H_ -#define _TUSB_HID_DEVICE_H_ +#ifndef TUSB_HID_DEVICE_H_ +#define TUSB_HID_DEVICE_H_ #include "hid.h" @@ -48,8 +48,7 @@ #endif //--------------------------------------------------------------------+ -// Application API (Multiple Instances) -// CFG_TUD_HID > 1 +// Application API (Multiple Instances) i.e. CFG_TUD_HID > 1 //--------------------------------------------------------------------+ // Check if the interface is ready to use @@ -66,7 +65,7 @@ bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, u // KEYBOARD: convenient helper to send keyboard report if application // use template layout report as defined by hid_keyboard_report_t -bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6]); +bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, const uint8_t keycode[6]); // MOUSE: convenient helper to send mouse report if application // use template layout report as defined by hid_mouse_report_t @@ -76,12 +75,6 @@ bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons // use template layout report as defined by hid_abs_mouse_report_t bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal); - -static inline bool tud_hid_abs_mouse_report(uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) -{ - return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal); -} - // Gamepad: convenient helper to send gamepad report if application // use template layout report TUD_HID_REPORT_DESC_GAMEPAD bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons); @@ -89,16 +82,40 @@ bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int //--------------------------------------------------------------------+ // Application API (Single Port) //--------------------------------------------------------------------+ -static inline bool tud_hid_ready(void); -static inline uint8_t tud_hid_interface_protocol(void); -static inline uint8_t tud_hid_get_protocol(void); -static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len); -static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]); -static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal); -static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons); +TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_ready(void) { + return tud_hid_n_ready(0); +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_hid_interface_protocol(void) { + return tud_hid_n_interface_protocol(0); +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_hid_get_protocol(void) { + return tud_hid_n_get_protocol(0); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len) { + return tud_hid_n_report(0, report_id, report, len); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, const uint8_t keycode[6]) { + return tud_hid_n_keyboard_report(0, report_id, modifier, keycode); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal) { + return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_abs_mouse_report(uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) { + return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) { + return tud_hid_n_gamepad_report(0, report_id, x, y, z, rz, rx, ry, hat, buttons); +} //--------------------------------------------------------------------+ -// Callbacks (Weak is optional) +// Application Callbacks //--------------------------------------------------------------------+ // Invoked when received GET HID REPORT DESCRIPTOR request @@ -111,63 +128,25 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance); uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen); // Invoked when received SET_REPORT control request or -// received data on OUT endpoint ( Report ID = 0, Type = 0 ) +// received data on OUT endpoint (Report ID = 0, Type = OUTPUT) void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize); // Invoked when received SET_PROTOCOL request // protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1) -TU_ATTR_WEAK void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol); +void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol); // Invoked when received SET_IDLE request. return false will stall the request -// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication +// - Idle Rate = 0 : only send report if there is changes, i.e. skip duplication // - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms). -TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate); +bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate); // Invoked when sent REPORT successfully to host // Application can use this to send the next report // Note: For composite reports, report[0] is report ID -TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len); +void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len); // Invoked when a transfer wasn't successful -TU_ATTR_WEAK void tud_hid_report_fail_cb(uint8_t instance, uint8_t ep_addr, uint16_t len); - -//--------------------------------------------------------------------+ -// Inline Functions -//--------------------------------------------------------------------+ -static inline bool tud_hid_ready(void) -{ - return tud_hid_n_ready(0); -} - -static inline uint8_t tud_hid_interface_protocol(void) -{ - return tud_hid_n_interface_protocol(0); -} - -static inline uint8_t tud_hid_get_protocol(void) -{ - return tud_hid_n_get_protocol(0); -} - -static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len) -{ - return tud_hid_n_report(0, report_id, report, len); -} - -static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]) -{ - return tud_hid_n_keyboard_report(0, report_id, modifier, keycode); -} - -static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal) -{ - return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal); -} - -static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) -{ - return tud_hid_n_gamepad_report(0, report_id, x, y, z, rz, rx, ry, hat, buttons); -} +void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, uint8_t const* report, uint16_t xferred_bytes); /* --------------------------------------------------------------------+ * HID Report Descriptor Template @@ -645,9 +624,8 @@ uint16_t hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); - #ifdef __cplusplus } #endif -#endif /* _TUSB_HID_DEVICE_H_ */ +#endif diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index 115a8a4df..7639a8fc6 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -657,7 +657,9 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t const data8 = desc_report[0]; TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size); - for (uint32_t i = 0; i < size; i++) TU_LOG(3, "%02X ", desc_report[i]); + for (uint32_t i = 0; i < size; i++) { + TU_LOG(3, "%02X ", desc_report[i]); + } TU_LOG(3, "\r\n"); switch (type) { diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index 64aba011a..90d747185 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -3,6 +3,8 @@ * * Copyright (c) 2019 Ha Thach (tinyusb.org) * Copyright (c) 2024 Hardy Griech + * Copyright (c) 2020 Jacob Berg Potter + * Copyright (c) 2020 Peter Lawrence * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index 07cdf3ff0..259b9e002 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -195,7 +195,6 @@ #elif TU_CHECK_MCU(OPT_MCU_STM32F4) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_STM32 - #define TUP_USBIP_DWC2_TEST_MODE // For most mcu, FS has 4, HS has 6. TODO 446/469/479 HS has 9 #define TUP_DCD_ENDPOINT_MAX 6 @@ -210,7 +209,6 @@ // MCU with on-chip HS Phy #if defined(STM32F723xx) || defined(STM32F730xx) || defined(STM32F733xx) #define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS - #define TUP_USBIP_DWC2_TEST_MODE #endif #elif TU_CHECK_MCU(OPT_MCU_STM32H7) @@ -271,17 +269,23 @@ #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_STM32U5) - #define TUP_USBIP_DWC2 - #define TUP_USBIP_DWC2_STM32 + #if defined (STM32U535xx) || defined (STM32U545xx) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 - // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY - #if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \ - defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx) - #define TUP_DCD_ENDPOINT_MAX 9 - #define TUP_RHPORT_HIGHSPEED 1 - #define TUP_USBIP_DWC2_TEST_MODE #else - #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY + #if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \ + defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx) + #define TUP_DCD_ENDPOINT_MAX 9 + #define TUP_RHPORT_HIGHSPEED 1 + #else + #define TUP_DCD_ENDPOINT_MAX 6 + #endif #endif #elif TU_CHECK_MCU(OPT_MCU_STM32L5) @@ -332,8 +336,11 @@ #define TUP_USBIP_DWC2 #define TUP_DCD_ENDPOINT_MAX 6 -#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C6, OPT_MCU_ESP32H2) && (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)) +#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C6, OPT_MCU_ESP32H2) + #if (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)) #error "MCUs are only supported with CFG_TUH_MAX3421 enabled" + #endif + #define TUP_DCD_ENDPOINT_MAX 0 //--------------------------------------------------------------------+ // Dialog @@ -420,6 +427,15 @@ #define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS #define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8) +#elif TU_CHECK_MCU(OPT_MCU_CH32V103) + #define TUP_USBIP_WCH_USBFS + + #if !defined(CFG_TUD_WCH_USBIP_USBFS) + #define CFG_TUD_WCH_USBIP_USBFS 1 + #endif + + #define TUP_DCD_ENDPOINT_MAX 8 + #elif TU_CHECK_MCU(OPT_MCU_CH32V20X) // v20x support both FSDEV (USBD) and USBFS, default to FSDEV #define TUP_USBIP_WCH_USBFS diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index b571f9b72..1501a5af6 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -214,6 +214,15 @@ enum { #define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2) +// USB 2.0 Spec Table 9-7: Test Mode Selectors +typedef enum { + TUSB_FEATURE_TEST_J = 1, + TUSB_FEATURE_TEST_K, + TUSB_FEATURE_TEST_SE0_NAK, + TUSB_FEATURE_TEST_PACKET, + TUSB_FEATURE_TEST_FORCE_ENABLE, +} tusb_feature_test_mode_t; + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h index dde0550d3..4344575b7 100644 --- a/src/common/tusb_verify.h +++ b/src/common/tusb_verify.h @@ -56,8 +56,8 @@ * #define TU_VERIFY(cond) if(cond) return false; * #define TU_VERIFY(cond,ret) if(cond) return ret; * - * #define TU_ASSERT(cond) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return false;} - * #define TU_ASSERT(cond,ret) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return ret;} + * #define TU_ASSERT(cond) if(cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return false;} + * #define TU_ASSERT(cond,ret) if(cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return ret;} *------------------------------------------------------------------*/ #ifdef __cplusplus @@ -70,9 +70,9 @@ #if CFG_TUSB_DEBUG #include - #define _MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__) + #define TU_MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__) #else - #define _MESS_FAILED() do {} while (0) + #define TU_MESS_FAILED() do {} while (0) #endif // Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33. M55 @@ -119,7 +119,7 @@ *------------------------------------------------------------------*/ #define TU_ASSERT_DEFINE(_cond, _ret) \ do { \ - if ( !(_cond) ) { _MESS_FAILED(); TU_BREAKPOINT(); return _ret; } \ + if ( !(_cond) ) { TU_MESS_FAILED(); TU_BREAKPOINT(); return _ret; } \ } while(0) #define TU_ASSERT_1ARGS(_cond) TU_ASSERT_DEFINE(_cond, false) diff --git a/src/device/dcd.h b/src/device/dcd.h index f6735b077..41e0fbee3 100644 --- a/src/device/dcd.h +++ b/src/device/dcd.h @@ -40,19 +40,15 @@ //--------------------------------------------------------------------+ typedef enum { - DCD_EVENT_INVALID = 0, - DCD_EVENT_BUS_RESET, - DCD_EVENT_UNPLUGGED, - DCD_EVENT_SOF, - DCD_EVENT_SUSPEND, // TODO LPM Sleep L1 support - DCD_EVENT_RESUME, - - DCD_EVENT_SETUP_RECEIVED, - DCD_EVENT_XFER_COMPLETE, - - // Not an DCD event, just a convenient way to defer ISR function - USBD_EVENT_FUNC_CALL, - + DCD_EVENT_INVALID = 0, // 0 + DCD_EVENT_BUS_RESET, // 1 + DCD_EVENT_UNPLUGGED, // 2 + DCD_EVENT_SOF, // 3 + DCD_EVENT_SUSPEND, // 4 TODO LPM Sleep L1 support + DCD_EVENT_RESUME, // 5 + DCD_EVENT_SETUP_RECEIVED, // 6 + DCD_EVENT_XFER_COMPLETE, // 7 + USBD_EVENT_FUNC_CALL, // 8 Not an DCD event, just a convenient way to defer ISR function DCD_EVENT_COUNT } dcd_eventid_t; @@ -89,14 +85,6 @@ typedef struct TU_ATTR_ALIGNED(4) { }; } dcd_event_t; -typedef enum { - TEST_J = 1, - TEST_K, - TEST_SE0_NAK, - TEST_PACKET, - TEST_FORCE_ENABLE, -} test_mode_t; - //TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct"); //--------------------------------------------------------------------+ @@ -150,11 +138,8 @@ void dcd_disconnect(uint8_t rhport); void dcd_sof_enable(uint8_t rhport, bool en); #if CFG_TUD_TEST_MODE -// Check if the test mode is supported, returns true is test mode selector is supported -bool dcd_check_test_mode_support(test_mode_t test_selector) TU_ATTR_WEAK; - // Put device into a test mode (needs power cycle to quit) -void dcd_enter_test_mode(uint8_t rhport, test_mode_t test_selector) TU_ATTR_WEAK; +void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector); #endif //--------------------------------------------------------------------+ // Endpoint API @@ -195,7 +180,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr); TU_ATTR_WEAK bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size); // Configure and enable an ISO endpoint according to descriptor -TU_ATTR_WEAK bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc); +TU_ATTR_WEAK bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep); //--------------------------------------------------------------------+ // Event API (implemented by stack) diff --git a/src/device/usbd.c b/src/device/usbd.c index 800d9c824..7089e9cf1 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -46,13 +46,46 @@ // Weak stubs: invoked if no strong implementation is available //--------------------------------------------------------------------+ TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) { - (void)rhport; - (void)eventid; - (void)in_isr; + (void) rhport; + (void) eventid; + (void) in_isr; } TU_ATTR_WEAK void tud_sof_cb(uint32_t frame_count) { - (void)frame_count; + (void) frame_count; +} + +TU_ATTR_WEAK uint8_t const* tud_descriptor_bos_cb(void) { + return NULL; +} + +TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void) { + return NULL; +} + +TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void) index; + return NULL; +} + +TU_ATTR_WEAK void tud_mount_cb(void) { +} + +TU_ATTR_WEAK void tud_umount_cb(void) { +} + +TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en) { + (void) remote_wakeup_en; +} + +TU_ATTR_WEAK void tud_resume_cb(void) { +} + +TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) { + (void) rhport; + (void) stage; + (void) request; + return false; } TU_ATTR_WEAK bool dcd_deinit(uint8_t rhport) { @@ -87,7 +120,6 @@ typedef struct { }; volatile uint8_t cfg_num; // current active configuration (0x00 is not configured) uint8_t speed; - volatile uint8_t setup_count; volatile uint8_t sof_consumer; uint8_t itf2drv[CFG_TUD_INTERFACE_MAX]; // map interface number to driver (0xff is invalid) @@ -98,6 +130,7 @@ typedef struct { }usbd_device_t; tu_static usbd_device_t _usbd_dev; +static volatile uint8_t _usbd_queued_setup; //--------------------------------------------------------------------+ // Class Driver @@ -321,9 +354,17 @@ TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request); static bool process_set_config(uint8_t rhport, uint8_t cfg_num); static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request); + #if CFG_TUD_TEST_MODE -static bool process_test_mode_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +static bool process_test_mode_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { + TU_VERIFY(CONTROL_STAGE_ACK == stage); + uint8_t const selector = tu_u16_high(request->wIndex); + TU_LOG_USBD(" Enter Test Mode (test selector index: %d)\r\n", selector); + dcd_enter_test_mode(rhport, (tusb_feature_test_mode_t) selector); + return true; +} #endif + // from usbd_control.c void usbd_control_reset(void); void usbd_control_set_request(tusb_control_request_t const *request); @@ -418,6 +459,7 @@ bool tud_init(uint8_t rhport) { TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_edpt_stream_t)); tu_varclr(&_usbd_dev); + _usbd_queued_setup = 0; #if OSAL_MUTEX_REQUIRED // Init device mutex @@ -549,13 +591,14 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) { case DCD_EVENT_UNPLUGGED: TU_LOG_USBD("\r\n"); usbd_reset(event.rhport); - if (tud_umount_cb) tud_umount_cb(); + tud_umount_cb(); break; case DCD_EVENT_SETUP_RECEIVED: - _usbd_dev.setup_count--; + TU_ASSERT(_usbd_queued_setup > 0,); + _usbd_queued_setup--; TU_LOG_BUF(CFG_TUD_LOG_LEVEL, &event.setup_received, 8); - if (_usbd_dev.setup_count) { + if (_usbd_queued_setup) { TU_LOG_USBD(" Skipped since there is other SETUP in queue\r\n"); break; } @@ -609,7 +652,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) { // e.g suspend -> resume -> unplug/plug. Skip suspend/resume if not connected if (_usbd_dev.connected) { TU_LOG_USBD(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en); - if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en); + tud_suspend_cb(_usbd_dev.remote_wakeup_en); } else { TU_LOG_USBD(" Skipped\r\n"); } @@ -618,7 +661,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) { case DCD_EVENT_RESUME: if (_usbd_dev.connected) { TU_LOG_USBD("\r\n"); - if (tud_resume_cb) tud_resume_cb(); + tud_resume_cb(); } else { TU_LOG_USBD(" Skipped\r\n"); } @@ -667,8 +710,6 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const // Vendor request if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) { - TU_VERIFY(tud_vendor_control_xfer_cb); - usbd_control_set_complete_callback(tud_vendor_control_xfer_cb); return tud_vendor_control_xfer_cb(rhport, CONTROL_STAGE_SETUP, p_request); } @@ -695,7 +736,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const } if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) { - // Non standard request is not supported + // Non-standard request is not supported TU_BREAKPOINT(); return false; } @@ -739,17 +780,23 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const _usbd_dev.speed = speed; // restore speed } + _usbd_dev.cfg_num = cfg_num; + // Handle the new configuration and execute the corresponding callback if ( cfg_num ) { // switch to new configuration if not zero - TU_ASSERT( process_set_config(rhport, cfg_num) ); - if ( tud_mount_cb ) tud_mount_cb(); + if (!process_set_config(rhport, cfg_num)) { + TU_MESS_FAILED(); + TU_BREAKPOINT(); + _usbd_dev.cfg_num = 0; + return false; + } + tud_mount_cb(); } else { - if ( tud_umount_cb ) tud_umount_cb(); + tud_umount_cb(); } } - _usbd_dev.cfg_num = cfg_num; tud_control_status(rhport, p_request); } break; @@ -759,43 +806,27 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const break; case TUSB_REQ_SET_FEATURE: - // Handle the feature selector - switch(p_request->wValue) - { - // Support for remote wakeup + switch(p_request->wValue) { case TUSB_REQ_FEATURE_REMOTE_WAKEUP: TU_LOG_USBD(" Enable Remote Wakeup\r\n"); - // Host may enable remote wake up before suspending especially HID device _usbd_dev.remote_wakeup_en = true; tud_control_status(rhport, p_request); break; -#if CFG_TUD_TEST_MODE - // Support for TEST_MODE + #if CFG_TUD_TEST_MODE case TUSB_REQ_FEATURE_TEST_MODE: { // Only handle the test mode if supported and valid - TU_VERIFY(dcd_enter_test_mode && dcd_check_test_mode_support && 0 == tu_u16_low(p_request->wIndex)); + TU_VERIFY(0 == tu_u16_low(p_request->wIndex)); - uint8_t selector = tu_u16_high(p_request->wIndex); - - // Stall request if the selected test mode isn't supported - if (!dcd_check_test_mode_support((test_mode_t)selector)) - { - TU_LOG_USBD(" Unsupported Test Mode (test selector index: %d)\r\n", selector); - - return false; - } - - // Acknowledge request - tud_control_status(rhport, p_request); - - TU_LOG_USBD(" Enter Test Mode (test selector index: %d)\r\n", selector); + uint8_t const selector = tu_u16_high(p_request->wIndex); + TU_VERIFY(TUSB_FEATURE_TEST_J <= selector && selector <= TUSB_FEATURE_TEST_FORCE_ENABLE); usbd_control_set_complete_callback(process_test_mode_cb); + tud_control_status(rhport, p_request); break; } -#endif /* CFG_TUD_TEST_MODE */ + #endif /* CFG_TUD_TEST_MODE */ // Stall unsupported feature selector default: return false; @@ -1029,39 +1060,34 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const switch(desc_type) { - case TUSB_DESC_DEVICE: - { + case TUSB_DESC_DEVICE: { TU_LOG_USBD(" Device\r\n"); void* desc_device = (void*) (uintptr_t) tud_descriptor_device_cb(); + TU_ASSERT(desc_device); // Only response with exactly 1 Packet if: not addressed and host requested more data than device descriptor has. // This only happens with the very first get device descriptor and EP0 size = 8 or 16. if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed && - ((tusb_control_request_t const*) p_request)->wLength > sizeof(tusb_desc_device_t)) - { + ((tusb_control_request_t const*) p_request)->wLength > sizeof(tusb_desc_device_t)) { // Hack here: we modify the request length to prevent usbd_control response with zlp // since we are responding with 1 packet & less data than wLength. tusb_control_request_t mod_request = *p_request; mod_request.wLength = CFG_TUD_ENDPOINT0_SIZE; return tud_control_xfer(rhport, &mod_request, desc_device, CFG_TUD_ENDPOINT0_SIZE); - }else - { + }else { return tud_control_xfer(rhport, p_request, desc_device, sizeof(tusb_desc_device_t)); } } // break; // unreachable - case TUSB_DESC_BOS: - { + case TUSB_DESC_BOS: { TU_LOG_USBD(" BOS\r\n"); // requested by host if USB > 2.0 ( i.e 2.1 or 3.x ) - if (!tud_descriptor_bos_cb) return false; - uintptr_t desc_bos = (uintptr_t) tud_descriptor_bos_cb(); - TU_ASSERT(desc_bos); + TU_VERIFY(desc_bos); // Use offsetof to avoid pointer to the odd/misaligned address uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_bos + offsetof(tusb_desc_bos_t, wTotalLength))) ); @@ -1071,24 +1097,20 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const // break; // unreachable case TUSB_DESC_CONFIGURATION: - case TUSB_DESC_OTHER_SPEED_CONFIG: - { + case TUSB_DESC_OTHER_SPEED_CONFIG: { uintptr_t desc_config; - if ( desc_type == TUSB_DESC_CONFIGURATION ) - { + if ( desc_type == TUSB_DESC_CONFIGURATION ) { TU_LOG_USBD(" Configuration[%u]\r\n", desc_index); desc_config = (uintptr_t) tud_descriptor_configuration_cb(desc_index); - }else - { + TU_ASSERT(desc_config); + }else { // Host only request this after getting Device Qualifier descriptor TU_LOG_USBD(" Other Speed Configuration\r\n"); - TU_VERIFY( tud_descriptor_other_speed_configuration_cb ); desc_config = (uintptr_t) tud_descriptor_other_speed_configuration_cb(desc_index); + TU_VERIFY(desc_config); } - TU_ASSERT(desc_config); - // Use offsetof to avoid pointer to the odd/misaligned address uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_config + offsetof(tusb_desc_configuration_t, wTotalLength))) ); @@ -1109,16 +1131,10 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const } // break; // unreachable - case TUSB_DESC_DEVICE_QUALIFIER: - { + case TUSB_DESC_DEVICE_QUALIFIER: { TU_LOG_USBD(" Device Qualifier\r\n"); - - TU_VERIFY( tud_descriptor_device_qualifier_cb ); - uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb(); TU_VERIFY(desc_qualifier); - - // first byte of descriptor is its size return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_qualifier, tu_desc_len(desc_qualifier)); } // break; // unreachable @@ -1127,20 +1143,6 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const } } -#if CFG_TUD_TEST_MODE -static bool process_test_mode_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) -{ - // At this point it should already be ensured that dcd_enter_test_mode() is defined - - // Only enter the test mode after the request for it has completed - TU_VERIFY(CONTROL_STAGE_ACK == stage); - - dcd_enter_test_mode(rhport, (test_mode_t)tu_u16_high(request->wIndex)); - - return true; -} -#endif /* CFG_TUD_TEST_MODE */ - //--------------------------------------------------------------------+ // DCD Event Handler //--------------------------------------------------------------------+ @@ -1199,7 +1201,7 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr) break; case DCD_EVENT_SETUP_RECEIVED: - _usbd_dev.setup_count++; + _usbd_queued_setup++; send = true; break; diff --git a/src/device/usbd.h b/src/device/usbd.h index e47f674ea..7913096e3 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -109,7 +109,7 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request); //--------------------------------------------------------------------+ -// Application Callbacks (WEAK is optional) +// Application Callbacks //--------------------------------------------------------------------+ // Invoked when received GET DEVICE DESCRIPTOR request @@ -126,31 +126,31 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid); // Invoked when received GET BOS DESCRIPTOR request // Application return pointer to descriptor -TU_ATTR_WEAK uint8_t const * tud_descriptor_bos_cb(void); +uint8_t const * tud_descriptor_bos_cb(void); // Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. // device_qualifier descriptor describes information about a high-speed capable device that would // change if the device were operating at the other speed. If not highspeed capable stall this request. -TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void); +uint8_t const* tud_descriptor_device_qualifier_cb(void); // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa -TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index); +uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index); // Invoked when device is mounted (configured) -TU_ATTR_WEAK void tud_mount_cb(void); +void tud_mount_cb(void); // Invoked when device is unmounted -TU_ATTR_WEAK void tud_umount_cb(void); +void tud_umount_cb(void); // Invoked when usb bus is suspended // Within 7ms, device must draw an average of current less than 2.5 mA from bus -TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en); +void tud_suspend_cb(bool remote_wakeup_en); // Invoked when usb bus is resumed -TU_ATTR_WEAK void tud_resume_cb(void); +void tud_resume_cb(void); // Invoked when there is a new usb event, which need to be processed by tud_task()/tud_task_ext() void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr); @@ -159,7 +159,7 @@ void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr); void tud_sof_cb(uint32_t frame_count); // Invoked when received control request with VENDOR TYPE -TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); //--------------------------------------------------------------------+ // Binary Device Object Store (BOS) Descriptor Templates @@ -434,8 +434,8 @@ 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) */ #define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN 7 -#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_ep, _interval) \ - TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN, TUSB_DESC_ENDPOINT, _ep, (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_NO_SYNC | (uint8_t)TUSB_ISO_EP_ATT_EXPLICIT_FB), U16_TO_U8S_LE(4), _interval +#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_ep, _epsize, _interval) \ + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN, TUSB_DESC_ENDPOINT, _ep, (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_NO_SYNC | (uint8_t)TUSB_ISO_EP_ATT_EXPLICIT_FB), U16_TO_U8S_LE(_epsize), _interval // AUDIO simple descriptor (UAC2) for 1 microphone input // - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source @@ -553,7 +553,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\ + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN) -#define TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize, _epfb) \ +#define TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epoutsize, _epfb, _epfbsize) \ /* Standard Interface Association Descriptor (IAD) */\ TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\ /* Standard AC Interface Descriptor(4.7.1) */\ @@ -567,7 +567,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb /* Output Terminal Descriptor(4.7.2.5) */\ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ /* Feature Unit Descriptor(4.7.2.8) */\ - TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ 0 * (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ 0 * (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_stridx*/ 0x00),\ + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_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, /*_stridx*/ 0x00),\ /* Standard AS Interface Descriptor(4.9.1) */\ /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ @@ -579,11 +579,11 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ - TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epoutsize, /*_interval*/ 0x01),\ /* 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),\ /* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\ - TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_interval*/ 1)\ + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_epsize*/ _epfbsize, /*_interval*/ 1) // Calculate wMaxPacketSize of Endpoints #define TUD_AUDIO_EP_SIZE(_maxFrequency, _nBytesPerSample, _nChannels) \ diff --git a/src/host/usbh.c b/src/host/usbh.c index 7a47f5056..8c488aaa2 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -1113,7 +1113,7 @@ bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt, TU_LOG_USBH("Set Interface %u Alternate %u\r\n", itf_num, itf_alt); tusb_control_request_t const request = { .bmRequestType_bit = { - .recipient = TUSB_REQ_RCPT_DEVICE, + .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT }, @@ -1421,6 +1421,9 @@ static void process_enumeration(tuh_xfer_t* xfer) { break; case ENUM_GET_DEVICE_DESC: { + // Allow 2ms for address recovery time, Ref USB Spec 9.2.6.3 + osal_task_delay(2); + uint8_t const new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue); usbh_device_t* new_dev = get_device(new_addr); diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h index f1f05f353..a3a0f3a3f 100644 --- a/src/osal/osal_freertos.h +++ b/src/osal/osal_freertos.h @@ -78,7 +78,7 @@ typedef struct // _int_set is not used with an RTOS #define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \ static _type _name##_##buf[_depth];\ - osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, _OSAL_Q_NAME(_name) }; + osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, _OSAL_Q_NAME(_name) } //--------------------------------------------------------------------+ // TASK API diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index abce35245..1cc5c1836 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -907,9 +907,9 @@ void tusb_hal_nrf_power_event(uint32_t event) { USB_EVT_READY = 2 }; -#if CFG_TUSB_DEBUG >= 2 +#if CFG_TUSB_DEBUG >= 3 const char* const power_evt_str[] = {"Detected", "Removed", "Ready"}; - TU_LOG(2, "Power USB event: %s\r\n", power_evt_str[event]); + TU_LOG(3, "Power USB event: %s\r\n", power_evt_str[event]); #endif switch (event) { diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c index dc71117b3..ef61146aa 100644 --- a/src/portable/nxp/khci/dcd_khci.c +++ b/src/portable/nxp/khci/dcd_khci.c @@ -540,7 +540,7 @@ void dcd_int_handler(uint8_t rhport) } if (is & USB_ISTAT_SLEEP_MASK) { - // TU_LOG2("Suspend: "); TU_LOG2_HEX(is); + // TU_LOG3("Suspend: "); TU_LOG2_HEX(is); // Note Host usually has extra delay after bus reset (without SOF), which could falsely // detected as Sleep event. Though usbd has debouncing logic so we are good diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 1ca711c77..43f48da39 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -53,6 +53,14 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) { //--------------------------------------------------------------------+ // Implementation //--------------------------------------------------------------------+ +// Provide own byte by byte memcpy as not all copies are aligned +static void unaligned_memcpy(void *dst, const void *src, size_t n) { + uint8_t *dst_byte = (uint8_t*)dst; + const uint8_t *src_byte = (const uint8_t*)src; + while (n--) { + *dst_byte++ = *src_byte++; + } +} void rp2040_usb_init(void) { // Reset usb controller @@ -67,7 +75,6 @@ void rp2040_usb_init(void) { #pragma GCC diagnostic ignored "-Wstringop-overflow" #endif #endif - memset(usb_hw, 0, sizeof(*usb_hw)); memset(usb_dpram, 0, sizeof(*usb_dpram)); #ifdef __GNUC__ #pragma GCC diagnostic pop @@ -125,7 +132,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, if (!ep->rx) { // Copy data from user buffer to hw buffer - memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen); + unaligned_memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen); ep->user_buf += buflen; // Mark as full @@ -230,7 +237,7 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uin // 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); + unaligned_memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes); ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes); ep->user_buf += xferred_bytes; } diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index 9ce37f992..6eea1ab32 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -4,7 +4,6 @@ * Copyright (c) 2019 Nathan Conrad * * Portions: - * Copyright (c) 2016 STMicroelectronics * Copyright (c) 2019 Ha Thach (tinyusb.org) * Copyright (c) 2022 Simon Küppers (skuep) * Copyright (c) 2022 HiFiPhile @@ -37,14 +36,20 @@ * It also should work with minimal changes for any ST MCU with an "USB A"/"PCD"/"HCD" peripheral. This * covers: * - * F04x, F072, F078, 070x6/B 1024 byte buffer + * F04x, F072, F078, F070x6/B 1024 byte buffer * F102, F103 512 byte buffer; no internal D+ pull-up (maybe many more changes?) * F302xB/C, F303xB/C, F373 512 byte buffer; no internal D+ pull-up * F302x6/8, F302xD/E2, F303xD/E 1024 byte buffer; no internal D+ pull-up + * G0 2048 byte buffer; 32-bit bus; host mode + * G4 1024 byte buffer + * H5 2048 byte buffer; 32-bit bus; host mode * L0x2, L0x3 1024 byte buffer * L1 512 byte buffer * L4x2, L4x3 1024 byte buffer - * G0 2048 byte buffer + * L5 1024 byte buffer + * U0 1024 byte buffer; 32-bit bus + * U535, U545 2048 byte buffer; 32-bit bus; host mode + * WB35, WB55 1024 byte buffer * * To use this driver, you must: * - If you are using a device with crystal-less USB, set up the clock recovery system (CRS) @@ -108,8 +113,6 @@ #include "device/dcd.h" #if defined(TUP_USBIP_FSDEV_STM32) - // Undefine to reduce the dependence on HAL - #undef USE_HAL_DRIVER #include "fsdev_stm32.h" #elif defined(TUP_USBIP_FSDEV_CH32) #include "fsdev_ch32.h" @@ -117,27 +120,7 @@ #error "Unknown USB IP" #endif -#include "fsdev_common.h" - -//--------------------------------------------------------------------+ -// Configuration -//--------------------------------------------------------------------+ - -// hardware limit endpoint -#define FSDEV_EP_COUNT 8 - -// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it -// Both of these MUST be a multiple of 2, and are in byte units. -#ifndef DCD_STM32_BTABLE_BASE -#define DCD_STM32_BTABLE_BASE 0U -#endif - -#ifndef DCD_STM32_BTABLE_SIZE -#define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE) -#endif - -TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_SIZE)) <= (FSDEV_PMA_SIZE), "BTABLE does not fit in PMA RAM"); -TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes"); +#include "fsdev_type.h" //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF @@ -162,11 +145,7 @@ typedef struct { } ep_alloc_t; static xfer_ctl_t xfer_status[CFG_TUD_ENDPPOINT_MAX][2]; - static ep_alloc_t ep_alloc_status[FSDEV_EP_COUNT]; - -static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6]; - static uint8_t remoteWakeCountdown; // When wake is requested //--------------------------------------------------------------------+ @@ -174,99 +153,90 @@ static uint8_t remoteWakeCountdown; // When wake is requested //--------------------------------------------------------------------+ // into the stack. -static void dcd_handle_bus_reset(void); +static void handle_bus_reset(uint8_t rhport); static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix); -static bool edpt_xfer(uint8_t rhport, uint8_t ep_addr); -static void dcd_ep_ctr_handler(void); +static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir); // PMA allocation/access static uint16_t ep_buf_ptr; ///< Points to first free memory location -static uint32_t dcd_pma_alloc(uint16_t length, bool dbuf); +static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf); static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type); -static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes); -static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes); +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes); +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes); static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes); static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes); +static void edpt0_open(uint8_t rhport); + +TU_ATTR_ALWAYS_INLINE static inline void edpt0_prepare_setup(void) { + btable_set_rx_bufsize(0, BTABLE_BUF_RX, 8); +} + //--------------------------------------------------------------------+ // Inline helper //--------------------------------------------------------------------+ -TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint32_t ep_addr) -{ - uint8_t epnum = tu_edpt_number(ep_addr); - uint8_t dir = tu_edpt_dir(ep_addr); - // Fix -Werror=null-dereference - TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX, &xfer_status[0][0]); - +TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint8_t epnum, uint8_t dir) { return &xfer_status[epnum][dir]; } //--------------------------------------------------------------------+ // Controller API //--------------------------------------------------------------------+ - -void dcd_init(uint8_t rhport) -{ - /* Clocks should already be enabled */ - /* Use __HAL_RCC_USB_CLK_ENABLE(); to enable the clocks before calling this function */ - - /* The RM mentions to use a special ordering of PDWN and FRES, but this isn't done in HAL. - * Here, the RM is followed. */ - - for (uint32_t i = 0; i < 200; i++) { // should be a few us +void dcd_init(uint8_t rhport) { + // Follow the RM mentions to use a special ordering of PDWN and FRES + for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us asm("NOP"); } + // Perform USB peripheral reset - USB->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN; - for (uint32_t i = 0; i < 200; i++) { // should be a few us + FSDEV_REG->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN; + for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us asm("NOP"); } - USB->CNTR &= ~USB_CNTR_PDWN; + FSDEV_REG->CNTR &= ~USB_CNTR_PDWN; // Wait startup time, for F042 and F070, this is <= 1 us. - for (uint32_t i = 0; i < 200; i++) { // should be a few us + for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us asm("NOP"); } - USB->CNTR = 0; // Enable USB + FSDEV_REG->CNTR = 0; // Enable USB -#if !defined(STM32G0) && !defined(STM32H5) // BTABLE register does not exist any more on STM32G0, it is fixed to USB SRAM base address - USB->BTABLE = DCD_STM32_BTABLE_BASE; +#if !defined(FSDEV_BUS_32BIT) + // BTABLE register does not exist any more on 32-bit bus devices + FSDEV_REG->BTABLE = FSDEV_BTABLE_BASE; #endif - USB->ISTR = 0; // Clear pending interrupts + + FSDEV_REG->ISTR = 0; // Clear pending interrupts // Reset endpoints to disabled for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) { // This doesn't clear all bits since some bits are "toggle", but does set the type to DISABLED. - pcd_set_endpoint(USB, i, 0u); + ep_write(i, 0u, false); } - USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM; - dcd_handle_bus_reset(); + FSDEV_REG->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | + USB_CNTR_SUSPM | USB_CNTR_WKUPM | USB_CNTR_PMAOVRM; + handle_bus_reset(rhport); // Enable pull-up if supported dcd_connect(rhport); } - -void dcd_sof_enable(uint8_t rhport, bool en) -{ +void dcd_sof_enable(uint8_t rhport, bool en) { (void)rhport; - (void)en; if (en) { - USB->CNTR |= USB_CNTR_SOFM; + FSDEV_REG->CNTR |= USB_CNTR_SOFM; } else { - USB->CNTR &= ~USB_CNTR_SOFM; + FSDEV_REG->CNTR &= ~USB_CNTR_SOFM; } } // Receive Set Address request, mcu port must also include status IN response -void dcd_set_address(uint8_t rhport, uint8_t dev_addr) -{ - (void)rhport; +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { (void)dev_addr; // Respond with status @@ -276,35 +246,15 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) // do it at dcd_edpt0_status_complete() } -void dcd_remote_wakeup(uint8_t rhport) -{ +void dcd_remote_wakeup(uint8_t rhport) { (void)rhport; - USB->CNTR |= USB_CNTR_RESUME; + FSDEV_REG->CNTR |= USB_CNTR_RESUME; remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms. } -static const tusb_desc_endpoint_t ep0OUT_desc = { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = 0x00, - .bmAttributes = {.xfer = TUSB_XFER_CONTROL}, - .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, - .bInterval = 0 -}; - -static const tusb_desc_endpoint_t ep0IN_desc = { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = 0x80, - .bmAttributes = {.xfer = TUSB_XFER_CONTROL}, - .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, - .bInterval = 0 -}; - -static void dcd_handle_bus_reset(void) -{ - USB->DADDR = 0u; // disable USB peripheral by clearing the EF flag +static void handle_bus_reset(uint8_t rhport) { + FSDEV_REG->DADDR = 0u; // disable USB Function for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) { // Clear EP allocation status @@ -315,35 +265,21 @@ static void dcd_handle_bus_reset(void) } // Reset PMA allocation - ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8 * CFG_TUD_ENDPPOINT_MAX; + ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * FSDEV_EP_COUNT; - dcd_edpt_open(0, &ep0OUT_desc); - dcd_edpt_open(0, &ep0IN_desc); + edpt0_open(rhport); // open control endpoint (both IN & OUT) - USB->DADDR = USB_DADDR_EF; // Set enable flag, and leaving the device address as zero. + FSDEV_REG->DADDR = USB_DADDR_EF; // Enable USB Function } // Handle CTR interrupt for the TX/IN direction -// -// Upon call, (wIstr & USB_ISTR_DIR) == 0U -static void dcd_ep_ctr_tx_handler(uint32_t wIstr) -{ - uint32_t EPindex = wIstr & USB_ISTR_EP_ID; - uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex); - uint8_t ep_addr = (wEPRegVal & USB_EPADDR_FIELD) | TUSB_DIR_IN_MASK; +static void handle_ctr_tx(uint32_t ep_id) { + uint32_t ep_reg = ep_read(ep_id) | USB_EP_CTR_TX | USB_EP_CTR_RX; - // Verify the CTR_TX bit is set. This was in the ST Micro code, - // but I'm not sure it's actually necessary? - if ((wEPRegVal & USB_EP_CTR_TX) == 0U) { - return; - } + uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD; + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_IN); - /* clear int flag */ - pcd_clear_tx_ep_ctr(USB, EPindex); - - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); - - if ((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + if (ep_is_iso(ep_reg)) { // Ignore spurious interrupts that we don't schedule // host can send IN token while there is no data to send, since ISO does not have NAK // this will result to zero length packet --> trigger interrupt (which cannot be masked) @@ -351,189 +287,106 @@ static void dcd_ep_ctr_tx_handler(uint32_t wIstr) return; } xfer->iso_in_sending = false; - - if (wEPRegVal & USB_EP_DTOG_TX) { - pcd_set_ep_tx_dbuf0_cnt(USB, EPindex, 0); - } else { - pcd_set_ep_tx_dbuf1_cnt(USB, EPindex, 0); - } + uint8_t buf_id = (ep_reg & USB_EP_DTOG_TX) ? 0 : 1; + btable_set_count(ep_id, buf_id, 0); } - if ((xfer->total_len != xfer->queued_len)) { - dcd_transmit_packet(xfer, EPindex); + if (xfer->total_len != xfer->queued_len) { + dcd_transmit_packet(xfer, ep_id); } else { - dcd_event_xfer_complete(0, ep_addr, xfer->total_len, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(0, ep_num | TUSB_DIR_IN_MASK, xfer->queued_len, XFER_RESULT_SUCCESS, true); + } +} + +static void handle_ctr_setup(uint32_t ep_id) { + uint16_t rx_count = btable_get_count(ep_id, BTABLE_BUF_RX); + uint16_t rx_addr = btable_get_addr(ep_id, BTABLE_BUF_RX); + uint8_t setup_packet[8] TU_ATTR_ALIGNED(4); + + dcd_read_packet_memory(setup_packet, rx_addr, rx_count); + + // Clear CTR RX if another setup packet arrived before this, it will be discarded + ep_write_clear_ctr(ep_id, TUSB_DIR_OUT); + + // Setup packet should always be 8 bytes. If not, we probably missed the packet + if (rx_count == 8) { + dcd_event_setup_received(0, (uint8_t*) setup_packet, true); + // Hardware should reset EP0 RX/TX to NAK and both toggle to 1 + } else { + // Missed setup packet !!! + TU_BREAKPOINT(); + edpt0_prepare_setup(); } } // Handle CTR interrupt for the RX/OUT direction -// Upon call, (wIstr & USB_ISTR_DIR) == 0U -static void dcd_ep_ctr_rx_handler(uint32_t wIstr) -{ -#ifdef FSDEV_BUS_32BIT - /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf - * From STM32H503 errata 2.15.1: Buffer description table update completes after CTR interrupt triggers - * Description: - * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses - * have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct. - * Workaround: - * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay - * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode - * - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code - * also takes time, so we'll wait 60 cycles (count = 20). - * - Since Low Speed mode is not supported/popular, we will ignore it for now. - * - * Note: this errata also seems to apply to G0, U5, H5 etc. - */ - volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver - while (cycle_count > 0U) { - cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare) - } -#endif +static void handle_ctr_rx(uint32_t ep_id) { + uint32_t ep_reg = ep_read(ep_id) | USB_EP_CTR_TX | USB_EP_CTR_RX; + uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD; + bool const is_iso = ep_is_iso(ep_reg); + xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_OUT); - uint32_t EPindex = wIstr & USB_ISTR_EP_ID; - uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex); - uint8_t ep_addr = wEPRegVal & USB_EPADDR_FIELD; - - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); - - // Verify the CTR_RX bit is set. This was in the ST Micro code, - // but I'm not sure it's actually necessary? - if ((wEPRegVal & USB_EP_CTR_RX) == 0U) { - return; - } - - if ((ep_addr == 0U) && ((wEPRegVal & USB_EP_SETUP) != 0U)) { - /* Setup packet */ - uint32_t count = pcd_get_ep_rx_cnt(USB, EPindex); - // Setup packet should always be 8 bytes. If not, ignore it, and try again. - if (count == 8) { - // Must reset EP to NAK (in case it had been stalling) (though, maybe too late here) - pcd_set_ep_rx_status(USB, 0u, USB_EP_RX_NAK); - pcd_set_ep_tx_status(USB, 0u, USB_EP_TX_NAK); -#ifdef FSDEV_BUS_32BIT - dcd_event_setup_received(0, (uint8_t *)(USB_PMAADDR + pcd_get_ep_rx_address(USB, EPindex)), true); -#else - // The setup_received function uses memcpy, so this must first copy the setup data into - // user memory, to allow for the 32-bit access that memcpy performs. - uint8_t userMemBuf[8]; - dcd_read_packet_memory(userMemBuf, pcd_get_ep_rx_address(USB, EPindex), 8); - dcd_event_setup_received(0, (uint8_t *)userMemBuf, true); -#endif - } + uint8_t buf_id; + if (is_iso) { + buf_id = (ep_reg & USB_EP_DTOG_RX) ? 0 : 1; // ISO are double buffered } else { - // Clear RX CTR interrupt flag - if (ep_addr != 0u) { - pcd_clear_rx_ep_ctr(USB, EPindex); - } - - uint32_t count; - uint16_t addr; - /* Read from correct register when ISOCHRONOUS (double buffered) */ - if ((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { - if (wEPRegVal & USB_EP_DTOG_RX) { - count = pcd_get_ep_dbuf0_cnt(USB, EPindex); - addr = pcd_get_ep_dbuf0_address(USB, EPindex); - } else { - count = pcd_get_ep_dbuf1_cnt(USB, EPindex); - addr = pcd_get_ep_dbuf1_address(USB, EPindex); - } - } else { - count = pcd_get_ep_rx_cnt(USB, EPindex); - addr = pcd_get_ep_rx_address(USB, EPindex); - } - - TU_ASSERT(count <= xfer->max_packet_size, /**/); - - if (count != 0U) { - if (xfer->ff) { - dcd_read_packet_memory_ff(xfer->ff, addr, count); - } else { - dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]), addr, count); - } - - xfer->queued_len = (uint16_t)(xfer->queued_len + count); - } - - if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len)) { - // all bytes received or short packet - dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true); - } else { - /* Set endpoint active again for receiving more data. - * Note that isochronous endpoints stay active always */ - if ((wEPRegVal & USB_EP_TYPE_MASK) != USB_EP_ISOCHRONOUS) { - uint16_t remaining = xfer->total_len - xfer->queued_len; - uint16_t cnt = tu_min16(remaining, xfer->max_packet_size); - pcd_set_ep_rx_cnt(USB, EPindex, cnt); - } - pcd_set_ep_rx_status(USB, EPindex, USB_EP_RX_VALID); - } + buf_id = BTABLE_BUF_RX; } + uint16_t const rx_count = btable_get_count(ep_id, buf_id); + uint16_t pma_addr = (uint16_t) btable_get_addr(ep_id, buf_id); - // For EP0, prepare to receive another SETUP packet. - // Clear CTR last so that a new packet does not overwrite the packing being read. - // (Based on the docs, it seems SETUP will always be accepted after CTR is cleared) - if (ep_addr == 0u) { - // Always be prepared for a status packet... - pcd_set_ep_rx_cnt(USB, EPindex, CFG_TUD_ENDPOINT0_SIZE); - pcd_clear_rx_ep_ctr(USB, EPindex); + if (xfer->ff) { + dcd_read_packet_memory_ff(xfer->ff, pma_addr, rx_count); + } else { + dcd_read_packet_memory(xfer->buffer + xfer->queued_len, pma_addr, rx_count); + } + xfer->queued_len += rx_count; + + if ((rx_count < xfer->max_packet_size) || (xfer->queued_len >= xfer->total_len)) { + // all bytes received or short packet + + // For ch32v203: reset rx bufsize to mps to prevent race condition to cause PMAOVR (occurs with msc write10) + btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, xfer->max_packet_size); + + dcd_event_xfer_complete(0, ep_num, xfer->queued_len, XFER_RESULT_SUCCESS, true); + + // ch32 seems to unconditionally accept ZLP on EP0 OUT, which can incorrectly use queued_len of previous + // transfer. So reset total_len and queued_len to 0. + xfer->total_len = xfer->queued_len = 0; + } else { + // Set endpoint active again for receiving more data. Note that isochronous endpoints stay active always + if (!is_iso) { + uint16_t const cnt = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size); + btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, cnt); + } + ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_OUT); // will change RX Status, reserved other toggle bits + ep_change_status(&ep_reg, TUSB_DIR_OUT, EP_STAT_VALID); + ep_write(ep_id, ep_reg, false); } } -static void dcd_ep_ctr_handler(void) -{ - uint32_t wIstr; - - /* stay in loop while pending interrupts */ - while (((wIstr = USB->ISTR) & USB_ISTR_CTR) != 0U) { - if ((wIstr & USB_ISTR_DIR) == 0U) { - /* TX/IN */ - dcd_ep_ctr_tx_handler(wIstr); - } else { - /* RX/OUT*/ - dcd_ep_ctr_rx_handler(wIstr); - } - } -} - -void dcd_int_handler(uint8_t rhport) -{ - (void)rhport; - - uint32_t int_status = USB->ISTR; - // const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP - // | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF; - // unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ ) - - // The ST driver loops here on the CTR bit, but that loop has been moved into the - // dcd_ep_ctr_handler(), so less need to loop here. The other interrupts shouldn't - // be triggered repeatedly. +void dcd_int_handler(uint8_t rhport) { + uint32_t int_status = FSDEV_REG->ISTR; /* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */ if (int_status & USB_ISTR_SOF) { - USB->ISTR = (fsdev_bus_t)~USB_ISTR_SOF; - dcd_event_sof(0, USB->FNR & USB_FNR_FN, true); + FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SOF; + dcd_event_sof(0, FSDEV_REG->FNR & USB_FNR_FN, true); } if (int_status & USB_ISTR_RESET) { // USBRST is start of reset. - USB->ISTR = (fsdev_bus_t)~USB_ISTR_RESET; - dcd_handle_bus_reset(); + FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_RESET; + handle_bus_reset(rhport); dcd_event_bus_reset(0, TUSB_SPEED_FULL, true); return; // Don't do the rest of the things here; perhaps they've been cleared? } - if (int_status & USB_ISTR_CTR) { - /* servicing of the endpoint correct transfer interrupt */ - /* clear of the CTR flag into the sub */ - dcd_ep_ctr_handler(); - } - if (int_status & USB_ISTR_WKUP) { - USB->CNTR &= ~USB_CNTR_LPMODE; - USB->CNTR &= ~USB_CNTR_FSUSP; + FSDEV_REG->CNTR &= ~USB_CNTR_LPMODE; + FSDEV_REG->CNTR &= ~USB_CNTR_FSUSP; - USB->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP; + FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP; dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); } @@ -542,22 +395,70 @@ void dcd_int_handler(uint8_t rhport) * these events cannot be differentiated, so we only trigger suspend. */ /* Force low-power mode in the macrocell */ - USB->CNTR |= USB_CNTR_FSUSP; - USB->CNTR |= USB_CNTR_LPMODE; + FSDEV_REG->CNTR |= USB_CNTR_FSUSP; + FSDEV_REG->CNTR |= USB_CNTR_LPMODE; /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */ - USB->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP; + FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP; dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); } if (int_status & USB_ISTR_ESOF) { if (remoteWakeCountdown == 1u) { - USB->CNTR &= ~USB_CNTR_RESUME; + FSDEV_REG->CNTR &= ~USB_CNTR_RESUME; } if (remoteWakeCountdown > 0u) { remoteWakeCountdown--; } - USB->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF; + FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF; + } + + // loop to handle all pending CTR interrupts + while (FSDEV_REG->ISTR & USB_ISTR_CTR) { + // skip DIR bit, and use CTR TX/RX instead, since there is chance we have both TX/RX completed in one interrupt + uint32_t const ep_id = FSDEV_REG->ISTR & USB_ISTR_EP_ID; + uint32_t const ep_reg = ep_read(ep_id); + + if (ep_reg & USB_EP_CTR_RX) { + #ifdef FSDEV_BUS_32BIT + /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf + * https://www.st.com/resource/en/errata_sheet/es0587-stm32u535xx-and-stm32u545xx-device-errata-stmicroelectronics.pdf + * From H503/U535 errata: Buffer description table update completes after CTR interrupt triggers + * Description: + * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses + * have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct. + * Workaround: + * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay + * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode + * - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code + * also takes time, so we'll wait 60 cycles (count = 20). + * - Since Low Speed mode is not supported/popular, we will ignore it for now. + * + * Note: this errata may also apply to G0, U5, H5 etc. + */ + volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver + while (cycle_count > 0U) { + cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare) + } + #endif + + if (ep_reg & USB_EP_SETUP) { + handle_ctr_setup(ep_id); // CTR will be clear after copied setup packet + } else { + ep_write_clear_ctr(ep_id, TUSB_DIR_OUT); + handle_ctr_rx(ep_id); + } + } + + if (ep_reg & USB_EP_CTR_TX) { + ep_write_clear_ctr(ep_id, TUSB_DIR_IN); + handle_ctr_tx(ep_id); + } + } + + if (int_status & USB_ISTR_PMAOVR) { + TU_BREAKPOINT(); + FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_PMAOVR; } } @@ -567,19 +468,17 @@ void dcd_int_handler(uint8_t rhport) // Invoked when a control transfer's status stage is complete. // May help DCD to prepare for next control transfer, this API is optional. -void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) -{ +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) { (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) { uint8_t const dev_addr = (uint8_t)request->wValue; - - // Setting new address after the whole request is complete - USB->DADDR &= ~USB_DADDR_ADD; - USB->DADDR |= dev_addr; // leave the enable bit set + FSDEV_REG->DADDR = (USB_DADDR_EF | dev_addr); } + + edpt0_prepare_setup(); } /*** @@ -587,21 +486,19 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *req * In case of double buffering, high 16bit is the address of 2nd buffer * During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually. */ -static uint32_t dcd_pma_alloc(uint16_t length, bool dbuf) +static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf) { - // Ensure allocated buffer is aligned -#ifdef FSDEV_BUS_32BIT - length = (length + 3) & ~0x03; -#else - length = (length + 1) & ~0x01; -#endif + uint8_t blsize, num_block; + uint16_t aligned_len = pma_align_buffer_size(len, &blsize, &num_block); + (void) blsize; + (void) num_block; uint32_t addr = ep_buf_ptr; - ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer + ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer if (dbuf) { addr |= ((uint32_t)ep_buf_ptr) << 16; - ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer + ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer } // Verify packet buffer is not overflowed @@ -648,34 +545,53 @@ static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type) TU_ASSERT(0); } -// The STM32F0 doesn't seem to like |= or &= to manipulate the EP#R registers, -// so I'm using the #define from HAL here, instead. +void edpt0_open(uint8_t rhport) { + (void) rhport; -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc) -{ + dcd_ep_alloc(0x0, TUSB_XFER_CONTROL); + dcd_ep_alloc(0x80, TUSB_XFER_CONTROL); + + xfer_status[0][0].max_packet_size = CFG_TUD_ENDPOINT0_SIZE; + xfer_status[0][0].ep_idx = 0; + + xfer_status[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE; + xfer_status[0][1].ep_idx = 0; + + uint16_t pma_addr0 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false); + uint16_t pma_addr1 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false); + + btable_set_addr(0, BTABLE_BUF_RX, pma_addr0); + btable_set_addr(0, BTABLE_BUF_TX, pma_addr1); + + uint32_t ep_reg = ep_read(0) & ~USB_EPREG_MASK; // only get toggle bits + ep_reg |= USB_EP_CONTROL; + ep_change_status(&ep_reg, TUSB_DIR_IN, EP_STAT_NAK); + ep_change_status(&ep_reg, TUSB_DIR_OUT, EP_STAT_NAK); + // no need to explicitly set DTOG bits since we aren't masked DTOG bit + + edpt0_prepare_setup(); // prepare for setup packet + ep_write(0, ep_reg, false); +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) { (void)rhport; - uint8_t const ep_addr = p_endpoint_desc->bEndpointAddress; - uint8_t const ep_idx = dcd_ep_alloc(ep_addr, p_endpoint_desc->bmAttributes.xfer); - uint8_t const dir = tu_edpt_dir(ep_addr); - const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc); - const uint16_t buffer_size = pcd_aligned_buffer_size(packet_size); - uint16_t pma_addr; - uint32_t wType; - + uint8_t const ep_addr = desc_ep->bEndpointAddress; + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + const uint16_t packet_size = tu_edpt_packet_size(desc_ep); + uint8_t const ep_idx = dcd_ep_alloc(ep_addr, desc_ep->bmAttributes.xfer); TU_ASSERT(ep_idx < FSDEV_EP_COUNT); - TU_ASSERT(buffer_size <= 64); + + uint32_t ep_reg = ep_read(ep_idx) & ~USB_EPREG_MASK; + ep_reg |= tu_edpt_number(ep_addr) | USB_EP_CTR_TX | USB_EP_CTR_RX; // Set type - switch (p_endpoint_desc->bmAttributes.xfer) { - case TUSB_XFER_CONTROL: - wType = USB_EP_CONTROL; - break; + switch (desc_ep->bmAttributes.xfer) { case TUSB_XFER_BULK: - wType = USB_EP_CONTROL; + ep_reg |= USB_EP_BULK; break; - case TUSB_XFER_INTERRUPT: - wType = USB_EP_INTERRUPT; + ep_reg |= USB_EP_INTERRUPT; break; default: @@ -683,35 +599,35 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc) TU_ASSERT(false); } - pcd_set_eptype(USB, ep_idx, wType); - pcd_set_ep_address(USB, ep_idx, tu_edpt_number(ep_addr)); - /* Create a packet memory buffer area. */ - pma_addr = dcd_pma_alloc(buffer_size, false); + uint16_t pma_addr = dcd_pma_alloc(packet_size, false); + btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); + xfer->max_packet_size = packet_size; + xfer->ep_idx = ep_idx; + + ep_change_status(&ep_reg, dir, EP_STAT_NAK); + ep_change_dtog(&ep_reg, dir, 0); + + // reserve other direction toggle bits if (dir == TUSB_DIR_IN) { - pcd_set_ep_tx_address(USB, ep_idx, pma_addr); - pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK); - pcd_clear_tx_dtog(USB, ep_idx); + ep_reg &= ~(USB_EPRX_STAT | USB_EP_DTOG_RX); } else { - pcd_set_ep_rx_address(USB, ep_idx, pma_addr); - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK); - pcd_clear_rx_dtog(USB, ep_idx); + ep_reg &= ~(USB_EPTX_STAT | USB_EP_DTOG_TX); } - xfer_ctl_ptr(ep_addr)->max_packet_size = packet_size; - xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx; + ep_write(ep_idx, ep_reg, true); return true; } -void dcd_edpt_close_all(uint8_t rhport) -{ - (void)rhport; +void dcd_edpt_close_all(uint8_t rhport) { + dcd_int_disable(rhport); for (uint32_t i = 1; i < FSDEV_EP_COUNT; i++) { // Reset endpoint - pcd_set_endpoint(USB, i, 0); + ep_write(i, 0, false); // Clear EP allocation status ep_alloc_status[i].ep_num = 0xFF; ep_alloc_status[i].ep_type = 0xFF; @@ -719,508 +635,340 @@ void dcd_edpt_close_all(uint8_t rhport) ep_alloc_status[i].allocated[1] = false; } + dcd_int_enable(rhport); + // Reset PMA allocation - ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8 * CFG_TUD_ENDPPOINT_MAX + 2 * CFG_TUD_ENDPOINT0_SIZE; + ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * CFG_TUD_ENDPPOINT_MAX + 2 * CFG_TUD_ENDPOINT0_SIZE; } -/** - * Close an endpoint. - * - * This function may be called with interrupts enabled or disabled. - * - * This also clears transfers in progress, should there be any. - */ -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) -{ +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { (void)rhport; - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); - uint8_t const ep_idx = xfer->ep_idx; + uint8_t const ep_num = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - - if (dir == TUSB_DIR_IN) { - pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); - } else { - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); - } -} - -bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) -{ - (void)rhport; - uint8_t const ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS); - const uint16_t buffer_size = pcd_aligned_buffer_size(largest_packet_size); /* Create a packet memory buffer area. Enable double buffering for devices with 2048 bytes PMA, for smaller devices double buffering occupy too much space. */ #if FSDEV_PMA_SIZE > 1024u - uint32_t pma_addr = dcd_pma_alloc(buffer_size, true); + uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, true); uint16_t pma_addr2 = pma_addr >> 16; #else - uint32_t pma_addr = dcd_pma_alloc(buffer_size, true); + uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, false); uint16_t pma_addr2 = pma_addr; #endif - pcd_set_ep_tx_address(USB, ep_idx, pma_addr); - pcd_set_ep_rx_address(USB, ep_idx, pma_addr2); - pcd_set_eptype(USB, ep_idx, USB_EP_ISOCHRONOUS); + btable_set_addr(ep_idx, 0, pma_addr); + btable_set_addr(ep_idx, 1, pma_addr2); - xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx; + xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir); + xfer->ep_idx = ep_idx; return true; } -bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc) -{ +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) { (void)rhport; - uint8_t const ep_addr = p_endpoint_desc->bEndpointAddress; - uint8_t const ep_idx = xfer_ctl_ptr(ep_addr)->ep_idx; - uint8_t const dir = tu_edpt_dir(ep_addr); - const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc); + uint8_t const ep_addr = desc_ep->bEndpointAddress; + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir); - pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); + uint8_t const ep_idx = xfer->ep_idx; - pcd_set_ep_address(USB, ep_idx, tu_edpt_number(ep_addr)); + xfer->max_packet_size = tu_edpt_packet_size(desc_ep); - pcd_clear_tx_dtog(USB, ep_idx); - pcd_clear_rx_dtog(USB, ep_idx); + uint32_t ep_reg = ep_read(ep_idx) & ~USB_EPREG_MASK; + ep_reg |= tu_edpt_number(ep_addr) | USB_EP_ISOCHRONOUS | USB_EP_CTR_TX | USB_EP_CTR_RX; + ep_change_status(&ep_reg, TUSB_DIR_IN, EP_STAT_DISABLED); + ep_change_status(&ep_reg, TUSB_DIR_OUT, EP_STAT_DISABLED); + ep_change_dtog(&ep_reg, dir, 0); + ep_change_dtog(&ep_reg, (tusb_dir_t)(1 - dir), 1); - if (dir == TUSB_DIR_IN) { - pcd_rx_dtog(USB, ep_idx); - } else { - pcd_tx_dtog(USB, ep_idx); - } - - xfer_ctl_ptr(ep_addr)->max_packet_size = packet_size; + ep_write(ep_idx, ep_reg, true); return true; } // Currently, single-buffered, and only 64 bytes at a time (max) +static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) { + uint16_t len = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size); + uint32_t ep_reg = ep_read(ep_ix) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR -static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) -{ - uint16_t len = (uint16_t)(xfer->total_len - xfer->queued_len); - if (len > xfer->max_packet_size) { - len = xfer->max_packet_size; - } - - uint16_t ep_reg = pcd_get_endpoint(USB, ep_ix); - bool const is_iso = (ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS; - uint16_t addr_ptr; + bool const is_iso = ep_is_iso(ep_reg); + uint8_t buf_id; if (is_iso) { - if (ep_reg & USB_EP_DTOG_TX) { - addr_ptr = pcd_get_ep_dbuf1_address(USB, ep_ix); - pcd_set_ep_tx_dbuf1_cnt(USB, ep_ix, len); - } else { - addr_ptr = pcd_get_ep_dbuf0_address(USB, ep_ix); - pcd_set_ep_tx_dbuf0_cnt(USB, ep_ix, len); - } + buf_id = (ep_reg & USB_EP_DTOG_TX) ? 1 : 0; } else { - addr_ptr = pcd_get_ep_tx_address(USB, ep_ix); - pcd_set_ep_tx_cnt(USB, ep_ix, len); + buf_id = BTABLE_BUF_TX; } + uint16_t addr_ptr = (uint16_t) btable_get_addr(ep_ix, buf_id); if (xfer->ff) { dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len); } else { dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len); } - xfer->queued_len = (uint16_t)(xfer->queued_len + len); + xfer->queued_len += len; + + btable_set_count(ep_ix, buf_id, len); + ep_change_status(&ep_reg, TUSB_DIR_IN, EP_STAT_VALID); - dcd_int_disable(0); - pcd_set_ep_tx_status(USB, ep_ix, USB_EP_TX_VALID); if (is_iso) { xfer->iso_in_sending = true; } - dcd_int_enable(0); + ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_IN); // only change TX Status, reserve other toggle bits + ep_write(ep_ix, ep_reg, true); } -static bool edpt_xfer(uint8_t rhport, uint8_t ep_addr) -{ - (void)rhport; +static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir) { + (void) rhport; - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); uint8_t const ep_idx = xfer->ep_idx; - uint8_t const dir = tu_edpt_dir(ep_addr); if (dir == TUSB_DIR_IN) { dcd_transmit_packet(xfer, ep_idx); } else { - // A setup token can occur immediately after an OUT STATUS packet so make sure we have a valid - // buffer for the control endpoint. - if (ep_idx == 0 && xfer->buffer == NULL) { - xfer->buffer = (uint8_t *)_setup_packet; - } + uint32_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR + ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir); - uint32_t cnt = (uint32_t ) tu_min16(xfer->total_len, xfer->max_packet_size); - uint16_t ep_reg = pcd_get_endpoint(USB, ep_idx); + uint16_t cnt = tu_min16(xfer->total_len, xfer->max_packet_size); - if ((ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { - pcd_set_ep_rx_dbuf0_cnt(USB, ep_idx, cnt); - pcd_set_ep_rx_dbuf1_cnt(USB, ep_idx, cnt); + if (ep_is_iso(ep_reg)) { + btable_set_rx_bufsize(ep_idx, 0, cnt); + btable_set_rx_bufsize(ep_idx, 1, cnt); } else { - pcd_set_ep_rx_cnt(USB, ep_idx, cnt); + btable_set_rx_bufsize(ep_idx, BTABLE_BUF_RX, cnt); } - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_VALID); + ep_change_status(&ep_reg, dir, EP_STAT_VALID); + ep_write(ep_idx, ep_reg, true); } return true; } -bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) -{ - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) { + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); xfer->buffer = buffer; xfer->ff = NULL; xfer->total_len = total_bytes; xfer->queued_len = 0; - return edpt_xfer(rhport, ep_addr); + return edpt_xfer(rhport, ep_num, dir); } -bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) -{ - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) { + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); + xfer->buffer = NULL; xfer->ff = ff; xfer->total_len = total_bytes; xfer->queued_len = 0; - return edpt_xfer(rhport, ep_addr); + return edpt_xfer(rhport, ep_num, dir); } -void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) -{ +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + (void)rhport; + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); + uint8_t const ep_idx = xfer->ep_idx; + + uint32_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR bits + ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir); + ep_change_status(&ep_reg, dir, EP_STAT_STALL); + + ep_write(ep_idx, ep_reg, true); +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { (void)rhport; - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); uint8_t const ep_idx = xfer->ep_idx; - uint8_t const dir = tu_edpt_dir(ep_addr); - if (dir == TUSB_DIR_IN) { - pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_STALL); - } else { - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_STALL); + uint32_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR bits + ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir) | EP_DTOG_MASK(dir); + + if (!ep_is_iso(ep_reg)) { + ep_change_status(&ep_reg, dir, EP_STAT_NAK); } + ep_change_dtog(&ep_reg, dir, 0); // Reset to DATA0 + ep_write(ep_idx, ep_reg, true); } -void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void)rhport; +//--------------------------------------------------------------------+ +// PMA read/write +//--------------------------------------------------------------------+ - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); - uint8_t const ep_idx = xfer->ep_idx; - uint8_t const dir = tu_edpt_dir(ep_addr); +// Write to packet memory area (PMA) from user memory +// - Packet memory must be either strictly 16-bit or 32-bit depending on FSDEV_BUS_32BIT +// - Uses unaligned for RAM (since M0 cannot access unaligned address) +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes) { + if (nbytes == 0) return true; + uint32_t n_write = nbytes / FSDEV_BUS_SIZE; - if (dir == TUSB_DIR_IN) { // IN - if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) { - pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK); - } + fsdev_pma_buf_t* pma_buf = PMA_BUF_AT(dst); + const uint8_t *src8 = src; - /* Reset to DATA0 if clearing stall condition. */ - pcd_clear_tx_dtog(USB, ep_idx); - } else { // OUT - if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) { - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK); - } - /* Reset to DATA0 if clearing stall condition. */ - pcd_clear_rx_dtog(USB, ep_idx); - } -} - -#ifdef FSDEV_BUS_32BIT -static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) -{ - const uint8_t *srcVal = src; - volatile uint32_t *dst32 = (volatile uint32_t *)(USB_PMAADDR + dst); - - for (uint32_t n = wNBytes / 4; n > 0; --n) { - *dst32++ = tu_unaligned_read32(srcVal); - srcVal += 4; + while (n_write--) { + pma_buf->value = fsdevbus_unaligned_read(src8); + src8 += FSDEV_BUS_SIZE; + pma_buf++; } - wNBytes = wNBytes & 0x03; - if (wNBytes) { - uint32_t wrVal = *srcVal; - wNBytes--; - - if (wNBytes) { - wrVal |= *++srcVal << 8; - wNBytes--; - - if (wNBytes) { - wrVal |= *++srcVal << 16; - } + // odd bytes e.g 1 for 16-bit or 1-3 for 32-bit + uint16_t odd = nbytes & (FSDEV_BUS_SIZE - 1); + if (odd) { + fsdev_bus_t temp = 0; + for(uint16_t i = 0; i < odd; i++) { + temp |= *src8++ << (i * 8); } - - *dst32 = wrVal; + pma_buf->value = temp; } return true; } -#else -// Packet buffer access can only be 8- or 16-bit. -/** - * @brief Copy a buffer from user memory area to packet memory area (PMA). - * This uses byte-access for user memory (so support non-aligned buffers) - * and 16-bit access for packet memory. - * @param dst, byte address in PMA; must be 16-bit aligned - * @param src pointer to user memory area. - * @param wPMABufAddr address into PMA. - * @param wNBytes no. of bytes to be copied. - * @retval None - */ -static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) -{ - uint32_t n = (uint32_t)wNBytes >> 1U; - uint16_t temp1, temp2; - const uint8_t *srcVal; - // The GCC optimizer will combine access to 32-bit sizes if we let it. Force - // it volatile so that it won't do that. - __IO uint16_t *pdwVal; +// Read from packet memory area (PMA) to user memory. +// - Packet memory must be either strictly 16-bit or 32-bit depending on FSDEV_BUS_32BIT +// - Uses unaligned for RAM (since M0 cannot access unaligned address) +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes) { + if (nbytes == 0) return true; + uint32_t n_read = nbytes / FSDEV_BUS_SIZE; - srcVal = src; - pdwVal = &pma[FSDEV_PMA_STRIDE * (dst >> 1)]; + fsdev_pma_buf_t* pma_buf = PMA_BUF_AT(src); + uint8_t *dst8 = (uint8_t *)dst; - while (n--) { - temp1 = (uint16_t)*srcVal; - srcVal++; - temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)); - *pdwVal = temp2; - pdwVal += FSDEV_PMA_STRIDE; - srcVal++; + while (n_read--) { + fsdevbus_unaligned_write(dst8, (fsdev_bus_t ) pma_buf->value); + dst8 += FSDEV_BUS_SIZE; + pma_buf++; } - if (wNBytes) { - temp1 = (uint16_t) *srcVal; - *pdwVal = temp1; + // odd bytes e.g 1 for 16-bit or 1-3 for 32-bit + uint16_t odd = nbytes & (FSDEV_BUS_SIZE - 1); + if (odd) { + fsdev_bus_t temp = pma_buf->value; + while (odd--) { + *dst8++ = (uint8_t) (temp & 0xfful); + temp >>= 8; + } } return true; } -#endif -/** - * @brief Copy from FIFO to packet memory area (PMA). - * Uses byte-access of system memory and 16-bit access of packet memory - * @param wNBytes no. of bytes to be copied. - * @retval None - */ -static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes) -{ +// Write to PMA from FIFO +static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes) { + if (wNBytes == 0) return true; + // Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies tu_fifo_buffer_info_t info; tu_fifo_get_read_info(ff, &info); - uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); - uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap); + uint16_t cnt_lin = tu_min16(wNBytes, info.len_lin); + uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.len_wrap); + uint16_t const cnt_total = cnt_lin + cnt_wrap; // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part, - // last lin byte will be combined with wrapped part - // To ensure PMA is always access aligned (dst aligned to 16 or 32 bit) -#ifdef FSDEV_BUS_32BIT - if ((cnt_lin & 0x03) && cnt_wrap) { - // Copy first linear part - dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x03); - dst += cnt_lin & ~0x03; + // last lin byte will be combined with wrapped part To ensure PMA is always access aligned + uint16_t lin_even = cnt_lin & ~(FSDEV_BUS_SIZE - 1); + uint16_t lin_odd = cnt_lin & (FSDEV_BUS_SIZE - 1); + uint8_t const *src8 = (uint8_t const*) info.ptr_lin; - // Copy last linear bytes & first wrapped bytes to buffer - uint32_t i; - uint8_t tmp[4]; - for (i = 0; i < (cnt_lin & 0x03); i++) { - tmp[i] = ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i]; - } - uint32_t wCnt = cnt_wrap; - for (; i < 4 && wCnt > 0; i++, wCnt--) { - tmp[i] = *(uint8_t *)info.ptr_wrap; - info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1; + // write even linear part + dcd_write_packet_memory(dst, src8, lin_even); + dst += lin_even; + src8 += lin_even; + + if (lin_odd == 0) { + src8 = (uint8_t const*) info.ptr_wrap; + } else { + // Combine last linear bytes + first wrapped bytes to form fsdev bus width data + fsdev_bus_t temp = 0; + uint16_t i; + for(i = 0; i < lin_odd; i++) { + temp |= *src8++ << (i * 8); } - // Write unaligned buffer - dcd_write_packet_memory(dst, &tmp, 4); - dst += 4; - - // Copy rest of wrapped byte - if (wCnt) - dcd_write_packet_memory(dst, info.ptr_wrap, wCnt); - } -#else - if ((cnt_lin & 0x01) && cnt_wrap) { - // Copy first linear part - dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x01); - dst += cnt_lin & ~0x01; - - // Copy last linear byte & first wrapped byte - uint16_t tmp = ((uint8_t *)info.ptr_lin)[cnt_lin - 1] | ((uint16_t)(((uint8_t *)info.ptr_wrap)[0]) << 8U); - dcd_write_packet_memory(dst, &tmp, 2); - dst += 2; - - // Copy rest of wrapped byte - dcd_write_packet_memory(dst, ((uint8_t *)info.ptr_wrap) + 1, cnt_wrap - 1); - } -#endif - else { - // Copy linear part - dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin); - dst += info.len_lin; - - if (info.len_wrap) { - // Copy wrapped byte - dcd_write_packet_memory(dst, info.ptr_wrap, cnt_wrap); + src8 = (uint8_t const*) info.ptr_wrap; + for(; i < FSDEV_BUS_SIZE && cnt_wrap > 0; i++, cnt_wrap--) { + temp |= *src8++ << (i * 8); } + + dcd_write_packet_memory(dst, &temp, FSDEV_BUS_SIZE); + dst += FSDEV_BUS_SIZE; } - tu_fifo_advance_read_pointer(ff, cnt_lin + cnt_wrap); + // write the rest of the wrapped part + dcd_write_packet_memory(dst, src8, cnt_wrap); + tu_fifo_advance_read_pointer(ff, cnt_total); return true; } -#ifdef FSDEV_BUS_32BIT -static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) -{ - uint8_t *dstVal = dst; - volatile uint32_t *src32 = (volatile uint32_t *)(USB_PMAADDR + src); +// Read from PMA to FIFO +static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes) { + if (wNBytes == 0) return true; - for (uint32_t n = wNBytes / 4; n > 0; --n) { - tu_unaligned_write32(dstVal, *src32++); - dstVal += 4; - } - - wNBytes = wNBytes & 0x03; - if (wNBytes) { - uint32_t rdVal = *src32; - - *dstVal = tu_u32_byte0(rdVal); - wNBytes--; - - if (wNBytes) { - *++dstVal = tu_u32_byte1(rdVal); - wNBytes--; - - if (wNBytes) { - *++dstVal = tu_u32_byte2(rdVal); - } - } - } - - return true; -} -#else -/** - * @brief Copy a buffer from packet memory area (PMA) to user memory area. - * Uses byte-access of system memory and 16-bit access of packet memory - * @param wNBytes no. of bytes to be copied. - * @retval None - */ -static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) -{ - uint32_t n = (uint32_t)wNBytes >> 1U; - // The GCC optimizer will combine access to 32-bit sizes if we let it. Force - // it volatile so that it won't do that. - __IO const uint16_t *pdwVal; - uint32_t temp; - - pdwVal = &pma[FSDEV_PMA_STRIDE * (src >> 1)]; - uint8_t *dstVal = (uint8_t *)dst; - - while (n--) { - temp = *pdwVal; - pdwVal += FSDEV_PMA_STRIDE; - *dstVal++ = ((temp >> 0) & 0xFF); - *dstVal++ = ((temp >> 8) & 0xFF); - } - - if (wNBytes & 0x01) { - temp = *pdwVal; - pdwVal += FSDEV_PMA_STRIDE; - *dstVal++ = ((temp >> 0) & 0xFF); - } - return true; -} -#endif - -/** - * @brief Copy a buffer from user packet memory area (PMA) to FIFO. - * Uses byte-access of system memory and 16-bit access of packet memory - * @param wNBytes no. of bytes to be copied. - * @retval None - */ -static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes) -{ // Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies // Check for first linear part tu_fifo_buffer_info_t info; tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO - uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); - uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap); + uint16_t cnt_lin = tu_min16(wNBytes, info.len_lin); + uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.len_wrap); + uint16_t cnt_total = cnt_lin + cnt_wrap; - // We want to read from PMA and write it into the FIFO, if LIN part is ODD and has WRAPPED part, - // last lin byte will be combined with wrapped part - // To ensure PMA is always access aligned (src aligned to 16 or 32 bit) -#ifdef FSDEV_BUS_32BIT - if ((cnt_lin & 0x03) && cnt_wrap) { - // Copy first linear part - dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x03); - src += cnt_lin & ~0x03; + // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part, + // last lin byte will be combined with wrapped part To ensure PMA is always access aligned - // Copy last linear bytes & first wrapped bytes - uint8_t tmp[4]; - dcd_read_packet_memory(tmp, src, 4); - src += 4; + uint16_t lin_even = cnt_lin & ~(FSDEV_BUS_SIZE - 1); + uint16_t lin_odd = cnt_lin & (FSDEV_BUS_SIZE - 1); + uint8_t *dst8 = (uint8_t *) info.ptr_lin; - uint32_t i; - for (i = 0; i < (cnt_lin & 0x03); i++) { - ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i] = tmp[i]; - } - uint32_t wCnt = cnt_wrap; - for (; i < 4 && wCnt > 0; i++, wCnt--) { - *(uint8_t *)info.ptr_wrap = tmp[i]; - info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1; + // read even linear part + dcd_read_packet_memory(dst8, src, lin_even); + dst8 += lin_even; + src += lin_even; + + if (lin_odd == 0) { + dst8 = (uint8_t *) info.ptr_wrap; + } else { + // Combine last linear bytes + first wrapped bytes to form fsdev bus width data + fsdev_bus_t temp; + dcd_read_packet_memory(&temp, src, FSDEV_BUS_SIZE); + src += FSDEV_BUS_SIZE; + + uint16_t i; + for (i = 0; i < lin_odd; i++) { + *dst8++ = (uint8_t) (temp & 0xfful); + temp >>= 8; } - // Copy rest of wrapped byte - if (wCnt) - dcd_read_packet_memory(info.ptr_wrap, src, wCnt); - } -#else - if ((cnt_lin & 0x01) && cnt_wrap) { - // Copy first linear part - dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x01); - src += cnt_lin & ~0x01; - - // Copy last linear byte & first wrapped byte - uint8_t tmp[2]; - dcd_read_packet_memory(tmp, src, 2); - src += 2; - - ((uint8_t *)info.ptr_lin)[cnt_lin - 1] = tmp[0]; - ((uint8_t *)info.ptr_wrap)[0] = tmp[1]; - - // Copy rest of wrapped byte - dcd_read_packet_memory(((uint8_t *)info.ptr_wrap) + 1, src, cnt_wrap - 1); - } -#endif - else { - // Copy linear part - dcd_read_packet_memory(info.ptr_lin, src, cnt_lin); - src += cnt_lin; - - if (info.len_wrap) { - // Copy wrapped byte - dcd_read_packet_memory(info.ptr_wrap, src, cnt_wrap); + dst8 = (uint8_t *) info.ptr_wrap; + for (; i < FSDEV_BUS_SIZE && cnt_wrap > 0; i++, cnt_wrap--) { + *dst8++ = (uint8_t) (temp & 0xfful); + temp >>= 8; } } - tu_fifo_advance_write_pointer(ff, cnt_lin + cnt_wrap); + // read the rest of the wrapped part + dcd_read_packet_memory(dst8, src, cnt_wrap); + tu_fifo_advance_write_pointer(ff, cnt_total); return true; } diff --git a/src/portable/st/stm32_fsdev/fsdev_ch32.h b/src/portable/st/stm32_fsdev/fsdev_ch32.h index 85fe3266c..518197c47 100644 --- a/src/portable/st/stm32_fsdev/fsdev_ch32.h +++ b/src/portable/st/stm32_fsdev/fsdev_ch32.h @@ -36,52 +36,26 @@ #include "common/tusb_compiler.h" -#if CFG_TUSB_MCU == OPT_MCU_CH32V20X - #include +// https://github.com/openwch/ch32v307/pull/90 +// https://github.com/openwch/ch32v20x/pull/12 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif -#elif CFG_TUSB_MCU == OPT_MCU_CH32F20X +#if CFG_TUSB_MCU == OPT_MCU_CH32F20X #include +#elif CFG_TUSB_MCU == OPT_MCU_CH32V20X + #include +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop #endif #define FSDEV_PMA_SIZE (512u) - -// volatile 32-bit aligned -#define _va32 volatile TU_ATTR_ALIGNED(4) - -typedef struct { - _va32 uint16_t EP0R; // 00: USB Endpoint 0 register - _va32 uint16_t EP1R; // 04: USB Endpoint 1 register - _va32 uint16_t EP2R; // 08: USB Endpoint 2 register - _va32 uint16_t EP3R; // 0C: USB Endpoint 3 register - _va32 uint16_t EP4R; // 10: USB Endpoint 4 register - _va32 uint16_t EP5R; // 14: USB Endpoint 5 register - _va32 uint16_t EP6R; // 18: USB Endpoint 6 register - _va32 uint16_t EP7R; // 1C: USB Endpoint 7 register - _va32 uint16_t RESERVED7[16]; // Reserved - _va32 uint16_t CNTR; // 40: Control register - _va32 uint16_t ISTR; // 44: Interrupt status register - _va32 uint16_t FNR; // 48: Frame number register - _va32 uint16_t DADDR; // 4C: Device address register - _va32 uint16_t BTABLE; // 50: Buffer Table address register -} USB_TypeDef; - -TU_VERIFY_STATIC(sizeof(USB_TypeDef) == 0x54, "Size is not correct"); -TU_VERIFY_STATIC(offsetof(USB_TypeDef, CNTR) == 0x40, "Wrong offset"); - -#define USB_BASE (APB1PERIPH_BASE + 0x00005C00UL) /*!< USB_IP Peripheral Registers base address */ -#define USB_PMAADDR (APB1PERIPH_BASE + 0x00006000UL) /*!< USB_IP Packet Memory Area base address */ -#define USB ((USB_TypeDef *)USB_BASE) - -/******************************************************************************/ -/* */ -/* USB Device General registers */ -/* */ -/******************************************************************************/ -#define USB_CNTR (USB_BASE + 0x40U) /*!< Control register */ -#define USB_ISTR (USB_BASE + 0x44U) /*!< Interrupt status register */ -#define USB_FNR (USB_BASE + 0x48U) /*!< Frame number register */ -#define USB_DADDR (USB_BASE + 0x4CU) /*!< Device address register */ -#define USB_BTABLE (USB_BASE + 0x50U) /*!< Buffer Table address register */ +#define FSDEV_REG_BASE (APB1PERIPH_BASE + 0x00005C00UL) +#define FSDEV_PMA_BASE (APB1PERIPH_BASE + 0x00006000UL) /**************************** ISTR interrupt events *************************/ #define USB_ISTR_CTR ((uint16_t)0x8000U) /*!< Correct TRansfer (clear-only bit) */ diff --git a/src/portable/st/stm32_fsdev/fsdev_common.h b/src/portable/st/stm32_fsdev/fsdev_common.h deleted file mode 100644 index af5d8afab..000000000 --- a/src/portable/st/stm32_fsdev/fsdev_common.h +++ /dev/null @@ -1,391 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright(c) 2016 STMicroelectronics - * Copyright(c) N Conrad - * Copyright (c) 2024, hathach (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. - * - */ - -#ifndef TUSB_FSDEV_COMMON_H -#define TUSB_FSDEV_COMMON_H - -#ifdef __cplusplus - extern "C" { -#endif - -#include "stdint.h" - -// FSDEV_PMA_SIZE is PMA buffer size in bytes. -// On 512-byte devices, access with a stride of two words (use every other 16-bit address) -// On 1024-byte devices, access with a stride of one word (use every 16-bit address) - -// For purposes of accessing the packet -#if ((FSDEV_PMA_SIZE) == 512u) - #define FSDEV_PMA_STRIDE (2u) -#elif ((FSDEV_PMA_SIZE) == 1024u) - #define FSDEV_PMA_STRIDE (1u) -#endif - -// The fsdev_bus_t type can be used for both register and PMA access necessities -// For type-safety create a new macro for the volatile address of PMAADDR -// The compiler should warn us if we cast it to a non-volatile type? -#ifdef FSDEV_BUS_32BIT -typedef uint32_t fsdev_bus_t; -static volatile uint32_t * const pma32 = (volatile uint32_t*)USB_PMAADDR; - -#else -typedef uint16_t fsdev_bus_t; -// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden) -static volatile uint16_t * const pma = (volatile uint16_t*)USB_PMAADDR; - -TU_ATTR_ALWAYS_INLINE static inline volatile uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x) { - size_t total_word_offset = (((USBx)->BTABLE)>>1) + x; - total_word_offset *= FSDEV_PMA_STRIDE; - return &(pma[total_word_offset]); -} - -TU_ATTR_ALWAYS_INLINE static inline volatile uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) { - return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 1u); -} - -TU_ATTR_ALWAYS_INLINE static inline volatile uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) { - return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 3u); -} -#endif - -/* Aligned buffer size according to hardware */ -TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t size) { - /* The STM32 full speed USB peripheral supports only a limited set of - * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */ - uint16_t blocksize = (size > 62) ? 32 : 2; - - // Round up while dividing requested size by blocksize - uint16_t numblocks = (size + blocksize - 1) / blocksize ; - - return numblocks * blocksize; -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - volatile uint32_t *reg = (volatile uint32_t *)(USB_DRD_BASE + bEpIdx*4); - *reg = wRegValue; -#else - volatile uint16_t *reg = (volatile uint16_t *)((&USBx->EP0R) + bEpIdx*2u); - *reg = (uint16_t)wRegValue; -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - volatile const uint32_t *reg = (volatile const uint32_t *)(USB_DRD_BASE + bEpIdx*4); -#else - volatile const uint16_t *reg = (volatile const uint16_t *)((&USBx->EP0R) + bEpIdx*2u); -#endif - return *reg; -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wType) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= (uint32_t)USB_EP_T_MASK; - regVal |= wType; - regVal |= USB_EP_CTR_RX | USB_EP_CTR_TX; // These clear on write0, so must set high - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EP_T_FIELD; - return regVal; -} - -/** - * @brief Clears bit CTR_RX / CTR_TX in the endpoint register. - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @retval None - */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPREG_MASK; - regVal &= ~USB_EP_CTR_RX; - regVal |= USB_EP_CTR_TX; // preserve CTR_TX (clears on writing 0) - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPREG_MASK; - regVal &= ~USB_EP_CTR_TX; - regVal |= USB_EP_CTR_RX; // preserve CTR_RX (clears on writing 0) - pcd_set_endpoint(USBx, bEpIdx,regVal); -} - -/** - * @brief gets counter of the tx buffer. - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @retval Counter value - */ -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - return (pma32[2*bEpIdx] & 0x03FF0000) >> 16; -#else - volatile const uint16_t *regPtr = pcd_ep_tx_cnt_ptr(USBx, bEpIdx); - return *regPtr & 0x3ffU; -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16; -#else - volatile const uint16_t *regPtr = pcd_ep_rx_cnt_ptr(USBx, bEpIdx); - return *regPtr & 0x3ffU; -#endif -} - -#define pcd_get_ep_dbuf0_cnt pcd_get_ep_tx_cnt -#define pcd_get_ep_dbuf1_cnt pcd_get_ep_rx_cnt - -/** - * @brief Sets address in an endpoint register. - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @param bAddr Address. - * @retval None - */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t bAddr) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPREG_MASK; - regVal |= bAddr; - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; - pcd_set_endpoint(USBx, bEpIdx,regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - return pma32[2*bEpIdx] & 0x0000FFFFu ; -#else - return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u); -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - return pma32[2*bEpIdx + 1] & 0x0000FFFFu; -#else - return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u); -#endif -} - -#define pcd_get_ep_dbuf0_address pcd_get_ep_tx_address -#define pcd_get_ep_dbuf1_address pcd_get_ep_rx_address - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu); -#else - *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u) = addr; -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu); -#else - *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u) = addr; -#endif -} - -#define pcd_set_ep_dbuf0_address pcd_set_ep_tx_address -#define pcd_set_ep_dbuf1_address pcd_set_ep_rx_address - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16); -#else - volatile uint16_t * reg = pcd_ep_tx_cnt_ptr(USBx, bEpIdx); - *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU); -#endif -} - -#define pcd_set_ep_tx_dbuf0_cnt pcd_set_ep_tx_cnt - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_dbuf1_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16); -#else - volatile uint16_t * reg = pcd_ep_rx_cnt_ptr(USBx, bEpIdx); - *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU); -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx, - uint32_t blocksize, uint32_t numblocks) { - /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ -#ifdef FSDEV_BUS_32BIT - (void) USBx; - pma32[rxtx_idx] = (pma32[rxtx_idx] & 0x0000FFFFu) | (blocksize << 31) | ((numblocks - blocksize) << 26); -#else - volatile uint16_t *pdwReg = pcd_btable_word_ptr(USBx, rxtx_idx*2u + 1u); - *pdwReg = (blocksize << 15) | ((numblocks - blocksize) << 10); -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t wCount) { - wCount = pcd_aligned_buffer_size(wCount); - - /* We assume that the buffer size is already aligned to hardware requirements. */ - uint16_t blocksize = (wCount > 62) ? 1 : 0; - uint16_t numblocks = wCount / (blocksize ? 32 : 2); - - /* There should be no remainder in the above calculation */ - TU_ASSERT((wCount - (numblocks * (blocksize ? 32 : 2))) == 0, /**/); - - /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ - pcd_set_ep_blsize_num_blocks(USBx, rxtx_idx, blocksize, numblocks); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_dbuf0_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { - pcd_set_ep_bufsize(USBx, 2*bEpIdx, wCount); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { - pcd_set_ep_bufsize(USBx, 2*bEpIdx + 1, wCount); -} - -#define pcd_set_ep_rx_dbuf1_cnt pcd_set_ep_rx_cnt - -/** - * @brief sets the status for tx transfer (bits STAT_TX[1:0]). - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @param wState new state - * @retval None - */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPTX_DTOGMASK; - - /* toggle first bit ? */ - if((USB_EPTX_DTOG1 & (wState))!= 0U) - { - regVal ^= USB_EPTX_DTOG1; - } - /* toggle second bit ? */ - if((USB_EPTX_DTOG2 & ((uint32_t)(wState)))!= 0U) - { - regVal ^= USB_EPTX_DTOG2; - } - - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -/** - * @brief sets the status for rx transfer (bits STAT_TX[1:0]) - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @param wState new state - * @retval None - */ - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPRX_DTOGMASK; - - /* toggle first bit ? */ - if((USB_EPRX_DTOG1 & wState)!= 0U) { - regVal ^= USB_EPRX_DTOG1; - } - /* toggle second bit ? */ - if((USB_EPRX_DTOG2 & wState)!= 0U) { - regVal ^= USB_EPRX_DTOG2; - } - - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - return (regVal & USB_EPRX_STAT) >> (12u); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPREG_MASK; - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_RX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPREG_MASK; - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_TX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - if((regVal & USB_EP_DTOG_RX) != 0) { - pcd_rx_dtog(USBx,bEpIdx); - } -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - if((regVal & USB_EP_DTOG_TX) != 0) { - pcd_tx_dtog(USBx,bEpIdx); - } -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal |= USB_EP_KIND; - regVal &= USB_EPREG_MASK; - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPKIND_MASK; - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index b3fa11b88..99fe8d55f 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -35,6 +35,7 @@ #if CFG_TUSB_MCU == OPT_MCU_STM32F0 #include "stm32f0xx.h" #define FSDEV_PMA_SIZE (1024u) + #define FSDEV_REG_BASE USB_BASE // F0x2 models are crystal-less // All have internal D+ pull-up // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support @@ -81,12 +82,9 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32G0 #include "stm32g0xx.h" - #define FSDEV_BUS_32BIT #define FSDEV_PMA_SIZE (2048u) - #undef USB_PMAADDR - #define USB_PMAADDR USB_DRD_PMAADDR - #define USB_TypeDef USB_DRD_TypeDef - #define EP0R CHEP0R + #define USB USB_DRD_FS + #define USB_EP_CTR_RX USB_EP_VTRX #define USB_EP_CTR_TX USB_EP_VTTX #define USB_EP_T_FIELD USB_CHEP_UTYPE @@ -99,7 +97,6 @@ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 #define USB_EPRX_STAT USB_CH_RX_VALID #define USB_EPKIND_MASK USB_EP_KIND_MASK - #define USB USB_DRD_FS #define USB_CNTR_FRES USB_CNTR_USBRST #define USB_CNTR_RESUME USB_CNTR_L2RES #define USB_ISTR_EP_ID USB_ISTR_IDN @@ -109,17 +106,9 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32H5 #include "stm32h5xx.h" - #define FSDEV_BUS_32BIT - - #if !defined(USB_DRD_BASE) && defined(USB_DRD_FS_BASE) - #define USB_DRD_BASE USB_DRD_FS_BASE - #endif - #define FSDEV_PMA_SIZE (2048u) - #undef USB_PMAADDR - #define USB_PMAADDR USB_DRD_PMAADDR - #define USB_TypeDef USB_DRD_TypeDef - #define EP0R CHEP0R + #define USB USB_DRD_FS + #define USB_EP_CTR_RX USB_EP_VTRX #define USB_EP_CTR_TX USB_EP_VTTX #define USB_EP_T_FIELD USB_CHEP_UTYPE @@ -132,7 +121,6 @@ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 #define USB_EPRX_STAT USB_CH_RX_VALID #define USB_EPKIND_MASK USB_EP_KIND_MASK - #define USB USB_DRD_FS #define USB_CNTR_FRES USB_CNTR_USBRST #define USB_CNTR_RESUME USB_CNTR_L2RES #define USB_ISTR_EP_ID USB_ISTR_IDN @@ -143,9 +131,8 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32WB #include "stm32wbxx.h" #define FSDEV_PMA_SIZE (1024u) - /* ST provided header has incorrect value */ - #undef USB_PMAADDR - #define USB_PMAADDR USB1_PMAADDR + /* ST provided header has incorrect value of USB_PMAADDR */ + #define FSDEV_PMA_BASE USB1_PMAADDR #elif CFG_TUSB_MCU == OPT_MCU_STM32L4 #include "stm32l4xx.h" @@ -159,9 +146,58 @@ #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS)) #endif +#elif CFG_TUSB_MCU == OPT_MCU_STM32U5 + #include "stm32u5xx.h" + #define FSDEV_PMA_SIZE (2048u) + #define USB USB_DRD_FS + + #define USB_EP_CTR_RX USB_EP_VTRX + #define USB_EP_CTR_TX USB_EP_VTTX + #define USB_EP_T_FIELD USB_CHEP_UTYPE + #define USB_EPREG_MASK USB_CHEP_REG_MASK + #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK + #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK + #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 + #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 + #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 + #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 + #define USB_EPRX_STAT USB_CH_RX_VALID + #define USB_EPKIND_MASK USB_EP_KIND_MASK + #define USB_CNTR_FRES USB_CNTR_USBRST + #define USB_CNTR_RESUME USB_CNTR_L2RES + #define USB_ISTR_EP_ID USB_ISTR_IDN + #define USB_EPADDR_FIELD USB_CHEP_ADDR + #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY + #define USB_CNTR_FSUSP USB_CNTR_SUSPEN + #else #error You are using an untested or unimplemented STM32 variant. Please update the driver. - // This includes L1x0, L1x1, L1x2, L4x2 and L4x3, G1x1, G1x3, and G1x4 + // This includes U0 +#endif + +//--------------------------------------------------------------------+ +// Register and PMA Base Address +//--------------------------------------------------------------------+ +#ifndef FSDEV_REG_BASE +#if defined(USB_BASE) + #define FSDEV_REG_BASE USB_BASE +#elif defined(USB_DRD_BASE) + #define FSDEV_REG_BASE USB_DRD_BASE +#elif defined(USB_DRD_FS_BASE) + #define FSDEV_REG_BASE USB_DRD_FS_BASE +#else + #error "FSDEV_REG_BASE not defined" +#endif +#endif + +#ifndef FSDEV_PMA_BASE +#if defined(USB_PMAADDR) + #define FSDEV_PMA_BASE USB_PMAADDR +#elif defined(USB_DRD_PMAADDR) + #define FSDEV_PMA_BASE USB_DRD_PMAADDR +#else + #error "FSDEV_PMA_BASE not defined" +#endif #endif // This checks if the device has "LPM" @@ -211,6 +247,8 @@ static const IRQn_Type fsdev_irq[] = { #elif CFG_TUSB_MCU == OPT_MCU_STM32WB USB_HP_IRQn, USB_LP_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32U5 + USB_IRQn, #else #error Unknown arch in USB driver #endif diff --git a/src/portable/st/stm32_fsdev/fsdev_type.h b/src/portable/st/stm32_fsdev/fsdev_type.h new file mode 100644 index 000000000..cf36576bb --- /dev/null +++ b/src/portable/st/stm32_fsdev/fsdev_type.h @@ -0,0 +1,307 @@ +/* + * The MIT License (MIT) + * + * Copyright(c) N Conrad + * Copyright(c) 2024, hathach (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 TUSB_FSDEV_TYPE_H +#define TUSB_FSDEV_TYPE_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include "stdint.h" + +// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it +// Both of these MUST be a multiple of 2, and are in byte units. +#ifndef FSDEV_BTABLE_BASE +#define FSDEV_BTABLE_BASE 0U +#endif + +TU_VERIFY_STATIC(FSDEV_BTABLE_BASE % 8 == 0, "BTABLE base must be aligned to 8 bytes"); + +// FSDEV_PMA_SIZE is PMA buffer size in bytes. +// - 512-byte devices, access with a stride of two words (use every other 16-bit address) +// - 1024-byte devices, access with a stride of one word (use every 16-bit address) +// - 2048-byte devices, access with 32-bit address + +// For purposes of accessing the packet +#if FSDEV_PMA_SIZE == 512 + // 1x16 bit / word access scheme + #define FSDEV_PMA_STRIDE 2 + #define pma_access_scheme TU_ATTR_ALIGNED(4) +#elif FSDEV_PMA_SIZE == 1024 + // 2x16 bit / word access scheme + #define FSDEV_PMA_STRIDE 1 + #define pma_access_scheme +#elif FSDEV_PMA_SIZE == 2048 + // 32 bit access scheme + #define FSDEV_BUS_32BIT + #define FSDEV_PMA_STRIDE 1 + #define pma_access_scheme +#endif + +// The fsdev_bus_t type can be used for both register and PMA access necessities +#ifdef FSDEV_BUS_32BIT + typedef uint32_t fsdev_bus_t; + #define fsdevbus_unaligned_read(_addr) tu_unaligned_read32(_addr) + #define fsdevbus_unaligned_write(_addr, _value) tu_unaligned_write32(_addr, _value) +#else + typedef uint16_t fsdev_bus_t; + #define fsdevbus_unaligned_read(_addr) tu_unaligned_read16(_addr) + #define fsdevbus_unaligned_write(_addr, _value) tu_unaligned_write16(_addr, _value) +#endif + +enum { + FSDEV_BUS_SIZE = sizeof(fsdev_bus_t), +}; + +//--------------------------------------------------------------------+ +// BTable Typedef +//--------------------------------------------------------------------+ +enum { + BTABLE_BUF_TX = 0, + BTABLE_BUF_RX = 1 +}; + +// hardware limit endpoint +#define FSDEV_EP_COUNT 8 + +// Buffer Table is located in Packet Memory Area (PMA) and therefore its address access is forced to either +// 16-bit or 32-bit depending on FSDEV_BUS_32BIT. +// 0: TX (IN), 1: RX (OUT) +typedef union { + // data is strictly 16-bit access (address could be 32-bit aligned) + struct { + volatile pma_access_scheme uint16_t addr; + volatile pma_access_scheme uint16_t count; + } ep16[FSDEV_EP_COUNT][2]; + + // strictly 32-bit access + struct { + volatile uint32_t count_addr; + } ep32[FSDEV_EP_COUNT][2]; +} fsdev_btable_t; + +TU_VERIFY_STATIC(sizeof(fsdev_btable_t) == FSDEV_EP_COUNT*8*FSDEV_PMA_STRIDE, "size is not correct"); +TU_VERIFY_STATIC(FSDEV_BTABLE_BASE + FSDEV_EP_COUNT*8 <= FSDEV_PMA_SIZE, "BTABLE does not fit in PMA RAM"); + +#define FSDEV_BTABLE ((volatile fsdev_btable_t*) (FSDEV_PMA_BASE + FSDEV_PMA_STRIDE*(FSDEV_BTABLE_BASE))) + +typedef struct { + volatile pma_access_scheme fsdev_bus_t value; +} fsdev_pma_buf_t; + +#define PMA_BUF_AT(_addr) ((fsdev_pma_buf_t*) (FSDEV_PMA_BASE + FSDEV_PMA_STRIDE*(_addr))) + +//--------------------------------------------------------------------+ +// Registers Typedef +//--------------------------------------------------------------------+ + +// volatile 32-bit aligned +#define _va32 volatile TU_ATTR_ALIGNED(4) + +typedef struct { + struct { + _va32 fsdev_bus_t reg; + }ep[FSDEV_EP_COUNT]; + + _va32 uint32_t RESERVED7[8]; // Reserved + _va32 fsdev_bus_t CNTR; // 40: Control register + _va32 fsdev_bus_t ISTR; // 44: Interrupt status register + _va32 fsdev_bus_t FNR; // 48: Frame number register + _va32 fsdev_bus_t DADDR; // 4C: Device address register + _va32 fsdev_bus_t BTABLE; // 50: Buffer Table address register (16-bit only) + _va32 fsdev_bus_t LPMCSR; // 54: LPM Control and Status Register (32-bit only) + _va32 fsdev_bus_t BCDR; // 58: Battery Charging Detector Register (32-bit only) +} fsdev_regs_t; + +TU_VERIFY_STATIC(offsetof(fsdev_regs_t, CNTR) == 0x40, "Wrong offset"); +TU_VERIFY_STATIC(sizeof(fsdev_regs_t) == 0x5C, "Size is not correct"); + +#define FSDEV_REG ((fsdev_regs_t*) FSDEV_REG_BASE) + + +#ifndef USB_EPTX_STAT +#define USB_EPTX_STAT 0x0030U +#endif + +#ifndef USB_EPRX_STAT +#define USB_EPRX_STAT 0x3000U +#endif + +#ifndef USB_EPTX_STAT_Pos +#define USB_EPTX_STAT_Pos 4u +#endif + +#ifndef USB_EP_DTOG_TX_Pos +#define USB_EP_DTOG_TX_Pos 6u +#endif + +#ifndef USB_EP_CTR_TX_Pos +#define USB_EP_CTR_TX_Pos 7u +#endif + +typedef enum { + EP_STAT_DISABLED = 0, + EP_STAT_STALL = 1, + EP_STAT_NAK = 2, + EP_STAT_VALID = 3 +}ep_stat_t; + +#define EP_STAT_MASK(_dir) (3u << (USB_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8))) +#define EP_DTOG_MASK(_dir) (1u << (USB_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8))) + +//--------------------------------------------------------------------+ +// Endpoint Helper +// - CTR is write 0 to clear +// - DTOG and STAT are write 1 to toggle +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_read(uint32_t ep_id) { + return FSDEV_REG->ep[ep_id].reg; +} + +TU_ATTR_ALWAYS_INLINE static inline void ep_write(uint32_t ep_id, uint32_t value, bool need_exclusive) { + if (need_exclusive) { + dcd_int_disable(0); + } + + FSDEV_REG->ep[ep_id].reg = (fsdev_bus_t) value; + + if (need_exclusive) { + dcd_int_enable(0); + } +} + +TU_ATTR_ALWAYS_INLINE static inline void ep_write_clear_ctr(uint32_t ep_id, tusb_dir_t dir) { + uint32_t reg = FSDEV_REG->ep[ep_id].reg; + reg |= USB_EP_CTR_TX | USB_EP_CTR_RX; + reg &= USB_EPREG_MASK; + reg &= ~(1 << (USB_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); + ep_write(ep_id, reg, false); +} + +TU_ATTR_ALWAYS_INLINE static inline void ep_change_status(uint32_t* reg, tusb_dir_t dir, ep_stat_t state) { + *reg ^= (state << (USB_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); +} + +TU_ATTR_ALWAYS_INLINE static inline void ep_change_dtog(uint32_t* reg, tusb_dir_t dir, uint8_t state) { + *reg ^= (state << (USB_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); +} + +TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(uint32_t reg) { + return (reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS; +} + +//--------------------------------------------------------------------+ +// BTable Helper +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_addr(uint32_t ep_id, uint8_t buf_id) { +#ifdef FSDEV_BUS_32BIT + return FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr & 0x0000FFFFu; +#else + return FSDEV_BTABLE->ep16[ep_id][buf_id].addr; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void btable_set_addr(uint32_t ep_id, uint8_t buf_id, uint16_t addr) { +#ifdef FSDEV_BUS_32BIT + uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; + count_addr = (count_addr & 0xFFFF0000u) | (addr & 0x0000FFFCu); + FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; +#else + FSDEV_BTABLE->ep16[ep_id][buf_id].addr = addr; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t btable_get_count(uint32_t ep_id, uint8_t buf_id) { + uint16_t count; +#ifdef FSDEV_BUS_32BIT + count = (FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr >> 16); +#else + count = FSDEV_BTABLE->ep16[ep_id][buf_id].count; +#endif + return count & 0x3FFU; +} + +TU_ATTR_ALWAYS_INLINE static inline void btable_set_count(uint32_t ep_id, uint8_t buf_id, uint16_t byte_count) { +#ifdef FSDEV_BUS_32BIT + uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; + count_addr = (count_addr & ~0x03FF0000u) | ((byte_count & 0x3FFu) << 16); + FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; +#else + uint16_t cnt = FSDEV_BTABLE->ep16[ep_id][buf_id].count; + cnt = (cnt & ~0x3FFU) | (byte_count & 0x3FFU); + FSDEV_BTABLE->ep16[ep_id][buf_id].count = cnt; +#endif +} + +/* Aligned buffer size according to hardware */ +TU_ATTR_ALWAYS_INLINE static inline uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_block) { + /* The STM32 full speed USB peripheral supports only a limited set of + * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */ + uint16_t block_in_bytes; + if (size > 62) { + block_in_bytes = 32; + *blsize = 1; + *num_block = tu_div_ceil(size, 32); + } else { + block_in_bytes = 2; + *blsize = 0; + *num_block = tu_div_ceil(size, 2); + } + + return (*num_block) * block_in_bytes; +} + +TU_ATTR_ALWAYS_INLINE static inline void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount) { + uint8_t blsize, num_block; + (void) pma_align_buffer_size(wCount, &blsize, &num_block); + + /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ + uint16_t bl_nb = (blsize << 15) | ((num_block - blsize) << 10); + if (bl_nb == 0) { + // zlp but 0 is invalid value, set blsize to 1 (32 bytes) + // Note: lower value can cause PMAOVR on setup with ch32v203 + bl_nb = 1 << 15; + } + +#ifdef FSDEV_BUS_32BIT + uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; + count_addr = (bl_nb << 16) | (count_addr & 0x0000FFFFu); + FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; +#else + FSDEV_BTABLE->ep16[ep_id][buf_id].count = bl_nb; +#endif + +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index dbcd586c5..9a38b46dc 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -1195,25 +1195,13 @@ void dcd_int_handler(uint8_t rhport) { // } } -#if defined(TUP_USBIP_DWC2_TEST_MODE) && CFG_TUD_TEST_MODE - -bool dcd_check_test_mode_support(test_mode_t test_selector) { - // Check if test mode selector is unsupported - if (TEST_FORCE_ENABLE < test_selector || TEST_J > test_selector) { - return false; - } - - return true; -} - -void dcd_enter_test_mode(uint8_t rhport, test_mode_t test_selector) { - // Get port address... +#if CFG_TUD_TEST_MODE +void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); // Enable the test mode - dwc2->dctl = (dwc2->dctl & ~DCTL_TCTL_Msk) | (test_selector << DCTL_TCTL_Pos); + dwc2->dctl = (dwc2->dctl & ~DCTL_TCTL_Msk) | (((uint8_t) test_selector) << DCTL_TCTL_Pos); } - -#endif /* TUP_USBIP_DWC2_TEST_MODE && CFG_TUD_TEST_MODE */ +#endif #endif diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h index c50dd66b8..932f52f40 100644 --- a/src/portable/synopsys/dwc2/dwc2_esp32.h +++ b/src/portable/synopsys/dwc2/dwc2_esp32.h @@ -35,6 +35,7 @@ #include "esp_intr_alloc.h" #include "soc/periph_defs.h" //#include "soc/usb_periph.h" +#include "freertos/task.h" #define DWC2_REG_BASE 0x60080000UL #define DWC2_EP_MAX 6 // USB_OUT_EP_NUM. TODO ESP32Sx only has 5 tx fifo (5 endpoint IN) diff --git a/src/portable/wch/ch32_usbfs_reg.h b/src/portable/wch/ch32_usbfs_reg.h index 0a50a6169..68be64f5e 100644 --- a/src/portable/wch/ch32_usbfs_reg.h +++ b/src/portable/wch/ch32_usbfs_reg.h @@ -28,15 +28,79 @@ #ifndef USB_CH32_USBFS_REG_H #define USB_CH32_USBFS_REG_H -#if CFG_TUSB_MCU == OPT_MCU_CH32V307 - #include - #define USBHD_IRQn OTG_FS_IRQn +// https://github.com/openwch/ch32v307/pull/90 +// https://github.com/openwch/ch32v20x/pull/12 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif +#if CFG_TUSB_MCU == OPT_MCU_CH32F20X + #include +#elif CFG_TUSB_MCU == OPT_MCU_CH32V103 + #include + typedef struct + { + __IO uint8_t BASE_CTRL; + __IO uint8_t UDEV_CTRL; + __IO uint8_t INT_EN; + __IO uint8_t DEV_ADDR; + __IO uint8_t Reserve0; + __IO uint8_t MIS_ST; + __IO uint8_t INT_FG; + __IO uint8_t INT_ST; + __IO uint32_t RX_LEN; + __IO uint8_t UEP4_1_MOD; + __IO uint8_t UEP2_3_MOD; + __IO uint8_t UEP5_6_MOD; + __IO uint8_t UEP7_MOD; + __IO uint32_t UEP0_DMA; + __IO uint32_t UEP1_DMA; + __IO uint32_t UEP2_DMA; + __IO uint32_t UEP3_DMA; + __IO uint32_t UEP4_DMA; + __IO uint32_t UEP5_DMA; + __IO uint32_t UEP6_DMA; + __IO uint32_t UEP7_DMA; + __IO uint16_t UEP0_TX_LEN; + __IO uint8_t UEP0_TX_CTRL; + __IO uint8_t UEP0_RX_CTRL; + __IO uint16_t UEP1_TX_LEN; + __IO uint8_t UEP1_TX_CTRL; + __IO uint8_t UEP1_RX_CTRL; + __IO uint16_t UEP2_TX_LEN; + __IO uint8_t UEP2_TX_CTRL; + __IO uint8_t UEP2_RX_CTRL; + __IO uint16_t UEP3_TX_LEN; + __IO uint8_t UEP3_TX_CTRL; + __IO uint8_t UEP3_RX_CTRL; + __IO uint16_t UEP4_TX_LEN; + __IO uint8_t UEP4_TX_CTRL; + __IO uint8_t UEP4_RX_CTRL; + __IO uint16_t UEP5_TX_LEN; + __IO uint8_t UEP5_TX_CTRL; + __IO uint8_t UEP5_RX_CTRL; + __IO uint16_t UEP6_TX_LEN; + __IO uint8_t UEP6_TX_CTRL; + __IO uint8_t UEP6_RX_CTRL; + __IO uint16_t UEP7_TX_LEN; + __IO uint8_t UEP7_TX_CTRL; + __IO uint8_t UEP7_RX_CTRL; + __IO uint32_t Reserve1; + __IO uint32_t OTG_CR; + __IO uint32_t OTG_SR; + } USBOTG_FS_TypeDef; + + #define USBOTG_FS ((USBOTG_FS_TypeDef *) 0x40023400) #elif CFG_TUSB_MCU == OPT_MCU_CH32V20X #include +#elif CFG_TUSB_MCU == OPT_MCU_CH32V307 + #include + #define USBHD_IRQn OTG_FS_IRQn +#endif -#elif CFG_TUSB_MCU == OPT_MCU_CH32F20X - #include +#ifdef __GNUC__ +#pragma GCC diagnostic pop #endif // CTRL diff --git a/src/portable/wch/ch32_usbhs_reg.h b/src/portable/wch/ch32_usbhs_reg.h index 130e4f223..87300b497 100644 --- a/src/portable/wch/ch32_usbhs_reg.h +++ b/src/portable/wch/ch32_usbhs_reg.h @@ -28,12 +28,24 @@ #ifndef USB_CH32_USBHS_REG_H #define USB_CH32_USBHS_REG_H +// https://github.com/openwch/ch32v307/pull/90 +// https://github.com/openwch/ch32v20x/pull/12 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + #if CFG_TUSB_MCU == OPT_MCU_CH32V307 #include #elif CFG_TUSB_MCU == OPT_MCU_CH32F20X #include #endif +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + /******************* GLOBAL ******************/ // USB CONTROL diff --git a/src/tusb.c b/src/tusb.c index 0092267a1..860e8ac35 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -398,7 +398,7 @@ static void dump_str_line(uint8_t const* buf, uint16_t count) { tu_printf(" |"); // each line is 16 bytes for (uint16_t i = 0; i < count; i++) { - const char ch = buf[i]; + int ch = buf[i]; tu_printf("%c", isprint(ch) ? ch : '.'); } tu_printf("|\r\n"); diff --git a/src/tusb_option.h b/src/tusb_option.h index 1290d605c..fb0209023 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -182,7 +182,7 @@ #define OPT_MCU_CH32V307 2200 ///< WCH CH32V307 #define OPT_MCU_CH32F20X 2210 ///< WCH CH32F20x #define OPT_MCU_CH32V20X 2220 ///< WCH CH32V20X - +#define OPT_MCU_CH32V103 2230 ///< WCH CH32V103 // NXP LPC MCX #define OPT_MCU_MCXN9 2300 ///< NXP MCX N9 Series @@ -387,7 +387,7 @@ #error "CFG_TUD_ENDPPOINT_MAX must be less than or equal to TUP_DCD_ENDPOINT_MAX" #endif -// USB 2.0 compliance test mode support +// USB 2.0 7.1.20: compliance test mode support #ifndef CFG_TUD_TEST_MODE #define CFG_TUD_TEST_MODE 0 #endif diff --git a/src/typec/tcd.h b/src/typec/tcd.h index 86499c951..bcbdab8ed 100644 --- a/src/typec/tcd.h +++ b/src/typec/tcd.h @@ -63,7 +63,7 @@ typedef struct TU_ATTR_PACKED { } xfer_complete; }; -} tcd_event_t;; +} tcd_event_t; //--------------------------------------------------------------------+ // diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index c2699cc6f..486f0d2eb 100644 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -33,17 +33,10 @@ import serial import subprocess import json import glob -import platform +from multiprocessing import Pool +import fs -# for RPI double reset -if platform.machine() == 'aarch64': - try: - import gpiozero - except ImportError: - pass - - -ENUM_TIMEOUT = 10 +ENUM_TIMEOUT = 30 # get usb serial by id @@ -60,9 +53,8 @@ def get_serial_dev(id, vendor_str, product_str, ifnum): return port_list[0] -# Currently not used, left as reference +# get usb disk by id def get_disk_dev(id, vendor_str, lun): - # get usb disk by id return f'/dev/disk/by-id/usb-{vendor_str}_Mass_Storage_{id}-0:{lun}' @@ -79,34 +71,35 @@ def open_serial_dev(port): # slight delay since kernel may occupy the port briefly time.sleep(0.5) timeout = timeout - 0.5 - ser = serial.Serial(port, timeout=1) + ser = serial.Serial(port, timeout=5) break except serial.SerialException: pass time.sleep(0.5) timeout = timeout - 0.5 - assert timeout, 'Device not available or Cannot open port' + + assert timeout, f'Cannot open port f{port}' if os.path.exists(port) else f'Port {port} not existed' return ser -def read_disk_file(id, fname): - # on different self-hosted, the mount point is different - file_list = [ - f'/media/blkUSB_{id[-8:]}.02/{fname}', - f'/media/{os.getenv("USER")}/TinyUSB MSC/{fname}' - ] +def read_disk_file(uid, lun, fname): + # open_fs("fat://{dev}) require 'pip install pyfatfs' + dev = get_disk_dev(uid, 'TinyUSB', lun) timeout = ENUM_TIMEOUT while timeout: - for file in file_list: - if os.path.isfile(file): - with open(file, 'rb') as f: + if os.path.exists(dev): + fat = fs.open_fs(f'fat://{dev}?read_only=true') + try: + with fat.open(fname, 'rb') as f: data = f.read() - return data - + finally: + fat.close() + assert data, f'Cannot read file {fname} from {dev}' + return data time.sleep(1) - timeout = timeout - 1 + timeout -= 1 - assert timeout, 'Device not available' + assert timeout, f'Storage {dev} not existed' return None @@ -114,11 +107,11 @@ def read_disk_file(id, fname): # Flashing firmware # ------------------------------------------------------------- def run_cmd(cmd): - # print(cmd) + #print(cmd) r = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - title = 'command error' if r.returncode != 0: - # print build output if failed + title = f'COMMAND FAILED: {cmd}' + print() if os.getenv('CI'): print(f"::group::{title}") print(r.stdout.decode("utf-8")) @@ -131,10 +124,21 @@ def run_cmd(cmd): def flash_jlink(board, firmware): script = ['halt', 'r', f'loadfile {firmware}.elf', 'r', 'go', 'exit'] - with open('flash.jlink', 'w') as f: + f_jlink = f'{board["name"]}_{os.path.basename(firmware)}.jlink' + with open(f_jlink, 'w') as f: f.writelines(f'{s}\n' for s in script) - ret = run_cmd(f'JLinkExe -USB {board["flasher_sn"]} {board["flasher_args"]} -if swd -JTAGConf -1,-1 -speed auto -NoGui 1 -ExitOnError 1 -CommandFile flash.jlink') - os.remove('flash.jlink') + ret = run_cmd(f'JLinkExe -USB {board["flasher_sn"]} {board["flasher_args"]} -if swd -JTAGConf -1,-1 -speed auto -NoGui 1 -ExitOnError 1 -CommandFile {f_jlink}') + os.remove(f_jlink) + return ret + + +def flash_stlink(board, firmware): + ret = run_cmd(f'STM32_Programmer_CLI --connect port=swd sn={board["flasher_sn"]} --write {firmware}.elf --go') + return ret + + +def flash_stflash(board, firmware): + ret = run_cmd(f'st-flash --serial {board["flasher_sn"]} write {firmware}.bin 0x8000000') return ret @@ -143,6 +147,42 @@ def flash_openocd(board, firmware): return ret +def flash_openocd_wch(board, firmware): + # Content of the wch-riscv.cfg file + cfg_content = """ +adapter driver wlinke +adapter speed 6000 +transport select sdi + +wlink_set_address 0x00000000 +set _CHIPNAME wch_riscv +sdi newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001 + +set _TARGETNAME $_CHIPNAME.cpu + +target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME +$_TARGETNAME.0 configure -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1 +set _FLASHNAME $_CHIPNAME.flash + +flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0 + +echo "Ready for Remote Connections" +""" + f_wch = f"wch-riscv_{board['uid']}.cfg" + if not os.path.exists(f_wch): + with open(f_wch, 'w') as file: + file.write(cfg_content) + + ret = run_cmd(f'openocd_wch -c "adapter serial {board["flasher_sn"]}" -f {f_wch} -c "program {firmware}.elf reset exit"') + return ret + + +def flash_wlink_rs(board, firmware): + # wlink use index for probe selection and lacking usb serial support + ret = run_cmd(f'wlink flash {firmware}.elf') + return ret + + def flash_esptool(board, firmware): port = get_serial_dev(board["flasher_sn"], None, None, 0) dir = os.path.dirname(f'{firmware}.bin') @@ -156,49 +196,18 @@ def flash_esptool(board, firmware): return ret -def doublereset_with_rpi_gpio(board): - # Off = 0 = Reset - led = gpiozero.LED(board["flasher_reset_pin"]) - - led.off() - time.sleep(0.1) - led.on() - time.sleep(0.1) - led.off() - time.sleep(0.1) - led.on() - - -def flash_bossac(board, firmware): - # double reset to enter bootloader - if platform.machine() == 'aarch64': - doublereset_with_rpi_gpio(board) - - port = get_serial_dev(board["uid"], board["flashser_vendor"], board["flasher_product"], 0) - timeout = ENUM_TIMEOUT - while timeout: - if os.path.exists(port): - break - else: - time.sleep(0.5) - timeout = timeout - 0.5 - assert timeout, 'bossac bootloader is not available' - # sleep a bit more for bootloader to be ready - time.sleep(0.5) - ret = run_cmd(f'bossac --port {port} {board["flasher_args"]} -U -i -R -e -w {firmware}.bin') - return ret - # ------------------------------------------------------------- # Tests # ------------------------------------------------------------- -def test_board_test(id): +def test_board_test(board): # Dummy test pass -def test_cdc_dual_ports(id): - port1 = get_serial_dev(id, 'TinyUSB', "TinyUSB_Device", 0) - port2 = get_serial_dev(id, 'TinyUSB', "TinyUSB_Device", 2) +def test_cdc_dual_ports(board): + uid = board['uid'] + port1 = get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 0) + port2 = get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 2) ser1 = open_serial_dev(port1) ser2 = open_serial_dev(port2) @@ -217,9 +226,10 @@ def test_cdc_dual_ports(id): assert ser2.read(100) == str2.upper(), 'Port2 wrong data' -def test_cdc_msc(id): +def test_cdc_msc(board): + uid = board['uid'] # Echo test - port = get_serial_dev(id, 'TinyUSB', "TinyUSB_Device", 0) + port = get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 0) ser = open_serial_dev(port) str = b"test_str" @@ -228,7 +238,7 @@ def test_cdc_msc(id): assert ser.read(100) == str, 'CDC wrong data' # Block test - data = read_disk_file(id, 'README.TXT') + data = read_disk_file(uid,0,'README.TXT') readme = \ b"This is tinyusb's MassStorage Class demo.\r\n\r\n\ If you find any bugs or get any questions, feel free to file an\r\n\ @@ -237,57 +247,62 @@ issue at github.com/hathach/tinyusb" assert data == readme, 'MSC wrong data' -def test_cdc_msc_freertos(id): - test_cdc_msc(id) +def test_cdc_msc_freertos(board): + test_cdc_msc(board) -def test_dfu(id): +def test_dfu(board): + uid = board['uid'] + # Wait device enum timeout = ENUM_TIMEOUT while timeout: ret = subprocess.run(f'dfu-util -l', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout = ret.stdout.decode() - if f'serial="{id}"' in stdout and 'Found DFU: [cafe:4000]' in stdout: + if f'serial="{uid}"' in stdout and 'Found DFU: [cafe:4000]' in stdout: break time.sleep(1) timeout = timeout - 1 assert timeout, 'Device not available' + f_dfu0 = f'dfu0_{uid}' + f_dfu1 = f'dfu1_{uid}' + # Test upload try: - os.remove('dfu0') - os.remove('dfu1') + os.remove(f_dfu0) + os.remove(f_dfu1) except OSError: pass - ret = subprocess.run(f'dfu-util -S {id} -a 0 -U dfu0', - shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + ret = run_cmd(f'dfu-util -S {uid} -a 0 -U {f_dfu0}') assert ret.returncode == 0, 'Upload failed' - ret = subprocess.run(f'dfu-util -S {id} -a 1 -U dfu1', - shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + ret = run_cmd(f'dfu-util -S {uid} -a 1 -U {f_dfu1}') assert ret.returncode == 0, 'Upload failed' - with open('dfu0') as f: + with open(f_dfu0) as f: assert 'Hello world from TinyUSB DFU! - Partition 0' in f.read(), 'Wrong uploaded data' - with open('dfu1') as f: + with open(f_dfu1) as f: assert 'Hello world from TinyUSB DFU! - Partition 1' in f.read(), 'Wrong uploaded data' - os.remove('dfu0') - os.remove('dfu1') + os.remove(f_dfu0) + os.remove(f_dfu1) -def test_dfu_runtime(id): +def test_dfu_runtime(board): + uid = board['uid'] + # Wait device enum timeout = ENUM_TIMEOUT while timeout: ret = subprocess.run(f'dfu-util -l', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout = ret.stdout.decode() - if f'serial="{id}"' in stdout and 'Found Runtime: [cafe:4000]' in stdout: + if f'serial="{uid}"' in stdout and 'Found Runtime: [cafe:4000]' in stdout: break time.sleep(1) timeout = timeout - 1 @@ -295,10 +310,11 @@ def test_dfu_runtime(id): assert timeout, 'Device not available' -def test_hid_boot_interface(id): - kbd = get_hid_dev(id, 'TinyUSB', 'TinyUSB_Device', 'event-kbd') - mouse1 = get_hid_dev(id, 'TinyUSB', 'TinyUSB_Device', 'if01-event-mouse') - mouse2 = get_hid_dev(id, 'TinyUSB', 'TinyUSB_Device', 'if01-mouse') +def test_hid_boot_interface(board): + uid = board['uid'] + kbd = get_hid_dev(uid, 'TinyUSB', 'TinyUSB_Device', 'event-kbd') + mouse1 = get_hid_dev(uid, 'TinyUSB', 'TinyUSB_Device', 'if01-event-mouse') + mouse2 = get_hid_dev(uid, 'TinyUSB', 'TinyUSB_Device', 'if01-mouse') # Wait device enum timeout = ENUM_TIMEOUT while timeout: @@ -307,7 +323,7 @@ def test_hid_boot_interface(id): time.sleep(1) timeout = timeout - 1 - assert timeout, 'Device not available' + assert timeout, 'HID device not available' def test_hid_composite_freertos(id): @@ -318,6 +334,70 @@ def test_hid_composite_freertos(id): # ------------------------------------------------------------- # Main # ------------------------------------------------------------- +# all possible tests: board_test is added last to disable board's usb +all_tests = [ + 'cdc_dual_ports', + 'cdc_msc', + 'dfu', + 'cdc_msc_freertos', # dont test 2 cdc_msc next to each other, since they have same vid/pid. Can be confused by host + 'dfu_runtime', + 'hid_boot_interface', + 'board_test' +] + + +def test_board(board): + name = board['name'] + flasher = board['flasher'].lower() + + # default to all tests + if 'tests' in board: + test_list = board['tests'] + ['board_test'] + else: + test_list = list(all_tests) + + # remove skip_tests + if 'tests_skip' in board: + for skip in board['tests_skip']: + if skip in test_list: + test_list.remove(skip) + + err_count = 0 + for test in test_list: + fw_dir = f'cmake-build/cmake-build-{name}/device/{test}' + if not os.path.exists(fw_dir): + fw_dir = f'examples/cmake-build-{name}/device/{test}' + fw_name = f'{fw_dir}/{test}' + print(f'{name:30} {test:20} ... ', end='') + + if not os.path.exists(fw_dir): + print('Skip') + continue + + # flash firmware. It may fail randomly, retry a few times + for i in range(3): + ret = globals()[f'flash_{flasher}'](board, fw_name) + if ret.returncode == 0: + break + else: + print(f'Flashing failed, retry {i+1}') + time.sleep(1) + + if ret.returncode == 0: + try: + ret = globals()[f'test_{test}'](board) + print('OK') + except Exception as e: + err_count += 1 + print('Failed') + print(f' {e}') + else: + err_count += 1 + print('Flash failed') + + return err_count + + def main(): """ Hardware test on specified boards @@ -330,59 +410,22 @@ def main(): config_file = args.config_file boards = args.board - config_file = os.path.join(os.path.dirname(__file__), config_file) + # if config file is not found, try to find it in the same directory as this script + if not os.path.exists(config_file): + config_file = os.path.join(os.path.dirname(__file__), config_file) with open(config_file) as f: config = json.load(f) - # all possible tests - all_tests = [ - 'cdc_dual_ports', 'cdc_msc', 'dfu', 'dfu_runtime', 'hid_boot_interface', - ] - if len(boards) == 0: config_boards = config['boards'] else: config_boards = [e for e in config['boards'] if e['name'] in boards] - for item in config_boards: - name = item['name'] - print(f'Testing board:{name}') - flasher = item['flasher'].lower() + err_count_list = 0 + with Pool(processes=os.cpu_count()) as pool: + err_count_list = pool.map(test_board, config_boards) - # default to all tests - if 'tests' in item: - test_list = item['tests'] - else: - test_list = all_tests - - # board_test is added last to disable board's usb - test_list.append('board_test') - - # remove skip_tests - if 'tests_skip' in item: - for skip in item['tests_skip']: - if skip in test_list: - test_list.remove(skip) - - for test in test_list: - fw_name = f'cmake-build/cmake-build-{name}/device/{test}/{test}' - print(f' {test} ...', end='') - - # flash firmware. It may fail randomly, retry a few times - for i in range(3): - ret = globals()[f'flash_{flasher}'](item, fw_name) - if ret.returncode == 0: - break - else: - print(f'Flashing failed, retry {i+1}') - time.sleep(1) - - assert ret.returncode == 0, 'Flash failed\n' + ret.stdout.decode() - - # run test - globals()[f'test_{test}'](item['uid']) - - print('OK') + sys.exit(sum(err_count_list)) if __name__ == '__main__': diff --git a/test/hil/pi4.json b/test/hil/pi4.json deleted file mode 100644 index bdb8a5fa5..000000000 --- a/test/hil/pi4.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "boards": [ - { - "name": "raspberry_pi_pico", - "uid": "E6614C311B764A37", - "flasher": "openocd", - "flasher_sn": "E6614103E72C1D2F", - "flasher_args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\"" - }, - { - "name": "feather_nrf52840_express", - "uid": "1F0479CD0F764471", - "flasher": "jlink", - "flasher_sn": "000682804350", - "flasher_args": "-device nrf52840_xxaa" - }, - { - "name": "itsybitsy_m4", - "uid": "D784B28C5338533335202020FF044726", - "flasher": "bossac", - "flashser_vendor": "Adafruit Industries", - "flasher_product": "ItsyBitsy M4 Express", - "flasher_reset_pin": "2", - "flasher_args": "--offset 0x4000" - }, - { - "name": "espressif_s3_devkitm", - "uid": "84F703C084E4", - "tests": [ - "cdc_msc_freertos", "hid_composite_freertos" - ], - "flasher": "esptool", - "flasher_sn": "3ea619acd1cdeb11a0a0b806e93fd3f1", - "flasher_args": "-b 1500000" - } - ] -} diff --git a/test/hil/rpi.json b/test/hil/rpi.json new file mode 100644 index 000000000..688ea3822 --- /dev/null +++ b/test/hil/rpi.json @@ -0,0 +1,82 @@ +{ + "boards": [ + { + "name": "feather_nrf52840_express", + "uid": "1F0479CD0F764471", + "flasher": "jlink", + "flasher_sn": "000682804350", + "flasher_args": "-device nrf52840_xxaa" + }, + { + "name": "itsybitsy_m4", + "uid": "D784B28C5338533335202020FF044726", + "flasher": "openocd", + "flasher_sn": "E6614C311B597D32", + "flasher_args": "-f interface/cmsis-dap.cfg -f target/atsame5x.cfg -c \"adapter speed 5000\"" + }, + { + "name": "lpcxpresso11u37", + "uid": "17121919", + "flasher": "jlink", + "flasher_sn": "000724441579", + "flasher_args": "-device LPC11U37/401" + }, + { + "name": "ra4m1_ek", + "uid": "152E163038303131393346E46F26574B", + "tests_skip": ["cdc_msc", "cdc_msc_freertos"], + "comment": "MSC is slow to enumerated #2602", + "flasher": "jlink", + "flasher_sn": "000831174392", + "flasher_args": "-device R7FA4M1AB" + }, + { + "name": "raspberry_pi_pico", + "uid": "E6614C311B764A37", + "flasher": "openocd", + "flasher_sn": "E6614103E72C1D2F", + "flasher_args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\"" + }, + { + "name": "stm32f072disco", + "uid": "3A001A001357364230353532", + "flasher": "jlink", + "flasher_sn": "779541626", + "flasher_args": "-device stm32f072rb" + }, + { + "name": "stm32g0b1nucleo", + "uid": "4D0038000450434E37343120", + "flasher": "openocd", + "flasher_sn": "066FFF495087534867063844", + "flasher_args": "-f interface/stlink.cfg -f target/stm32g0x.cfg" + }, + { + "name": "nanoch32v203", + "uid": "CDAB277B0FBC03E339E339E3", + "flasher": "openocd_wch", + "flasher_sn": "EBCA8F0670AF", + "flasher_args": "" + } + ], + "boards-skip": [ + { + "name": "metro_m7_1011", + "uid": "9CE8715DD71137363E00005002004200", + "flasher": "jlink", + "flasher_sn": "000611000000", + "flasher_args": "-device MIMXRT1011xxx5A", + "comment": "not running reliably in bulk with other boards, probably power, flashing etc .." + }, + { + "name": "espressif_s3_devkitm", + "uid": "84F703C084E4", + "tests": [ + "cdc_msc_freertos", "hid_composite_freertos" + ], + "flasher": "esptool", + "flasher_sn": "3ea619acd1cdeb11a0a0b806e93fd3f1", + "flasher_args": "-b 921600" + } + ] +} diff --git a/tools/build_utils.py b/tools/build_utils.py index b66b64b97..32aca95dd 100644 --- a/tools/build_utils.py +++ b/tools/build_utils.py @@ -81,43 +81,6 @@ def skip_example(example, board): return False -def build_example(example, board, make_option): - start_time = time.monotonic() - flash_size = "-" - sram_size = "-" - - # succeeded, failed, skipped - ret = [0, 0, 0] - - make_cmd = "make -j -C examples/{} BOARD={} {}".format(example, board, make_option) - - # Check if board is skipped - if skip_example(example, board): - status = SKIPPED - ret[2] = 1 - print(build_format.format(example, board, status, '-', flash_size, sram_size)) - else: - #subprocess.run(make_cmd + " clean", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - build_result = subprocess.run(make_cmd + " all", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - - if build_result.returncode == 0: - status = SUCCEEDED - ret[0] = 1 - (flash_size, sram_size) = build_size(make_cmd) - #subprocess.run(make_cmd + " copy-artifact", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - else: - status = FAILED - ret[1] = 1 - - build_duration = time.monotonic() - start_time - print(build_format.format(example, board, status, "{:.2f}s".format(build_duration), flash_size, sram_size)) - - if build_result.returncode != 0: - print(build_result.stdout.decode("utf-8")) - - return ret - - def build_size(make_cmd): size_output = subprocess.run(make_cmd + ' size', shell=True, stdout=subprocess.PIPE).stdout.decode("utf-8").splitlines() for i, l in enumerate(size_output): @@ -129,3 +92,38 @@ def build_size(make_cmd): return (flash_size, sram_size) return (0, 0) + + +def build_example(example, board, make_option): + start_time = time.monotonic() + flash_size = "-" + sram_size = "-" + + # succeeded, failed, skipped + ret = [0, 0, 0] + + make_cmd = f"make -j -C examples/{example} BOARD={board} {make_option}" + + # Check if board is skipped + if skip_example(example, board): + status = SKIPPED + ret[2] = 1 + print(build_format.format(example, board, status, '-', flash_size, sram_size)) + else: + build_result = subprocess.run(f"{make_cmd} all", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + if build_result.returncode == 0: + status = SUCCEEDED + ret[0] = 1 + (flash_size, sram_size) = build_size(make_cmd) + else: + status = FAILED + ret[1] = 1 + + build_duration = time.monotonic() - start_time + print(build_format.format(example, board, status, "{:.2f}s".format(build_duration), flash_size, sram_size)) + + if build_result.returncode != 0: + print(build_result.stdout.decode("utf-8")) + + return ret diff --git a/tools/get_deps.py b/tools/get_deps.py index fe548f4ab..51d6304ae 100644 --- a/tools/get_deps.py +++ b/tools/get_deps.py @@ -52,13 +52,13 @@ deps_optional = { '2204191ec76283371419fbcec207da02e1bc22fa', 'nuc'], 'hw/mcu/nxp/lpcopen': ['https://github.com/hathach/nxp_lpcopen.git', - '04bfe7a5f6ee74a89a28ad618d3367dcfcfb7d83', + 'b41cf930e65c734d8ec6de04f1d57d46787c76ae', 'lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43'], 'hw/mcu/nxp/mcux-sdk': ['https://github.com/hathach/mcux-sdk.git', '144f1eb7ea8c06512e12f12b27383601c0272410', 'kinetis_k kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx imxrt'], 'hw/mcu/raspberry_pi/Pico-PIO-USB': ['https://github.com/sekigon-gonnoc/Pico-PIO-USB.git', - '0f747aaa0c16f750bdfa2ba37ec25d6c8e1bc117', + '7902e9fa8ed4a271d8d1d5e7e50516c2292b7bc2', 'rp2040'], 'hw/mcu/renesas/fsp': ['https://github.com/renesas/fsp.git', 'd52e5a6a59b7c638da860c2bb309b6e78e752ff8', @@ -171,11 +171,14 @@ deps_optional = { 'hw/mcu/ti': ['https://github.com/hathach/ti_driver.git', '143ed6cc20a7615d042b03b21e070197d473e6e5', 'msp430 msp432e4 tm4c'], + 'hw/mcu/wch/ch32v103': ['https://github.com/openwch/ch32v103.git', + '7578cae0b21f86dd053a1f781b2fc6ab99d0ec17', + 'ch32v10x'], 'hw/mcu/wch/ch32v20x': ['https://github.com/openwch/ch32v20x.git', - 'de6d68c654340d7f27b00cebbfc9aa2740a1abc2', + 'c4c38f507e258a4e69b059ccc2dc27dde33cea1b', 'ch32v20x'], 'hw/mcu/wch/ch32v307': ['https://github.com/openwch/ch32v307.git', - '17761f5cf9dbbf2dcf665b7c04934188add20082', + '184f21b852cb95eed58e86e901837bc9fff68775', 'ch32v307'], 'hw/mcu/wch/ch32f20x': ['https://github.com/openwch/ch32f20x.git', '77c4095087e5ed2c548ec9058e655d0b8757663b',