diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 00405c4ba..ade7e7fa5 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -1,144 +1,46 @@ -name: Cron Build +name: Cron Deploy -on: +on: schedule: # ┌───────────── minute (0 - 59) # │ ┌───────────── hour (0 - 23) # │ │ ┌───────────── day of the month (1 - 31) # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) -# │ │ │ │ │ +# │ │ │ │ │ # │ │ │ │ │ # │ │ │ │ │ # * * * * * - cron: '0 */6 * * *' + workflow_dispatch: # For manually rebuilding the libraries defaults: run: shell: bash jobs: - run: + build-libs: name: Build with IDF ${{ matrix.idf_branch }} - runs-on: ubuntu-latest - + if: github.repository_owner == 'espressif' + uses: ./.github/workflows/cron_build.yml + with: + idf_branch: ${{ matrix.idf_branch }} + lib_builder_branch: ${{ matrix.lib_builder_branch }} + targets: ${{ matrix.targets }} + secrets: inherit strategy: fail-fast: false matrix: - idf_branch: [release/v5.1, release/v4.4] #, release/v3.3] - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Install dependencies - run: bash ./tools/prepare-ci.sh - - name: Build - env: - GITHUB_TOKEN: ${{ secrets.PUSH_TOKEN }} - GIT_AUTHOR_EMAIL: ${{ secrets.PUSH_EMAIL }} - GIT_COMMITTER_EMAIL: ${{ secrets.PUSH_EMAIL }} - IDF_BRANCH: ${{ matrix.idf_branch }} - run: | - git checkout ${{ matrix.idf_branch }} || echo "Using master branch" - bash ./tools/cron.sh - - name: Upload build - if: failure() - uses: actions/upload-artifact@v4 - with: - name: build - path: build - - name: Upload archive - uses: actions/upload-artifact@v4 - with: - name: artifacts - path: dist - - - # check: - # name: Check if result should be deployed - # runs-on: ubuntu-latest - # strategy: - # matrix: - # branch: [release/v5.1, release/v4.4] #, release/v3.3] - # outputs: - # idf_branch: ${{ steps.check.outputs.idf_branch }} - # idf_commit: ${{ steps.check.outputs.idf_commit }} - # ar_branch: ${{ steps.check.outputs.ar_branch }} - # ar_new_commit_message: ${{ steps.check.outputs.ar_new_commit_message }} - # ar_new_branch_name: ${{ steps.check.outputs.ar_new_branch_name }} - # ar_new_pr_title: ${{ steps.check.outputs.ar_new_pr_title }} - # ar_has_commit: ${{ steps.check.outputs.ar_has_commit }} - # ar_has_branch: ${{ steps.check.outputs.ar_has_branch }} - # ar_has_pr: ${{ steps.check.outputs.ar_has_pr }} - # libs_version: ${{ steps.check.outputs.libs_version }} - # libs_has_commit: ${{ steps.check.outputs.libs_has_commit }} - # libs_has_branch: ${{ steps.check.outputs.libs_has_branch }} - # steps: - # - uses: actions/checkout@v3 - # - id: check - # env: - # GITHUB_TOKEN: ${{ secrets.PUSH_TOKEN }} - # GIT_AUTHOR_EMAIL: ${{ secrets.PUSH_EMAIL }} - # GIT_COMMITTER_EMAIL: ${{ secrets.PUSH_EMAIL }} - # IDF_BRANCH: ${{ matrix.idf_branch }} - # run: bash ./tools/check-deploy-needed.sh - - # build: - # name: Build Libs for ${{ matrix.target }} - # runs-on: ubuntu-latest - # needs: check - # if: needs.check.outputs.libs_has_commit == '0' || needs.check.outputs.ar_has_commit == '0' - # strategy: - # matrix: - # target: [esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2] - # fail-fast: false - # steps: - # - uses: actions/checkout@v3 - # # - name: Install dependencies - # # run: bash ./tools/prepare-ci.sh - # - shell: bash - # name: Build Libs for ${{ matrix.target }} - # run: echo ${{ matrix.target }} - # # run: bash ./build.sh -t ${{ matrix.target }} - # # - name: Upload archive - # # uses: actions/upload-artifact@v3 - # # with: - # # name: artifacts - # # path: dist - - # deploy: - # name: Deploy build - # runs-on: ubuntu-latest - # needs: [check, build] - # steps: - # - uses: actions/checkout@v3 - # - shell: bash - # env: - # GITHUB_TOKEN: ${{ secrets.PUSH_TOKEN }} - # GIT_AUTHOR_EMAIL: ${{ secrets.PUSH_EMAIL }} - # GIT_COMMITTER_EMAIL: ${{ secrets.PUSH_EMAIL }} - # IDF_BRANCH: ${{ needs.check.outputs.idf_branch }} - # IDF_COMMIT: ${{ needs.check.outputs.idf_commit }} - # AR_BRANCH: ${{ needs.check.outputs.ar_branch }} - # AR_NEW_COMMIT_MESSAGE: ${{ needs.check.outputs.ar_new_commit_message }} - # AR_NEW_BRANCH_NAME: ${{ needs.check.outputs.ar_new_branch_name }} - # AR_NEW_PR_TITLE: ${{ needs.check.outputs.ar_new_pr_title }} - # AR_HAS_COMMIT: ${{ needs.check.outputs.ar_has_commit }} - # AR_HAS_BRANCH: ${{ needs.check.outputs.ar_has_branch }} - # AR_HAS_PR: ${{ needs.check.outputs.ar_has_pr }} - # LIBS_VERSION: ${{ needs.check.outputs.libs_version }} - # LIBS_HAS_COMMIT: ${{ needs.check.outputs.libs_has_commit }} - # LIBS_HAS_BRANCH: ${{ needs.check.outputs.libs_has_branch }} - # run: | - # echo "IDF_COMMIT: $IDF_COMMIT" - # echo "AR_BRANCH: $AR_BRANCH" - # echo "AR_NEW_COMMIT_MESSAGE: $AR_NEW_COMMIT_MESSAGE" - # echo "AR_NEW_BRANCH_NAME: $AR_NEW_BRANCH_NAME" - # echo "AR_NEW_PR_TITLE: $AR_NEW_PR_TITLE" - # echo "AR_HAS_COMMIT: $AR_HAS_COMMIT" - # echo "AR_HAS_BRANCH: $AR_HAS_BRANCH" - # echo "AR_HAS_PR: $AR_HAS_PR" - # echo "LIBS_VERSION: $LIBS_VERSION" - # echo "LIBS_HAS_COMMIT: $LIBS_HAS_COMMIT" - # echo "LIBS_HAS_BRANCH: $LIBS_HAS_BRANCH" - + include: + # - idf_branch: "release/v5.1" + # lib_builder_branch: "release/v5.1" + # targets: "esp32,esp32s2,esp32s3,esp32c3,esp32c6,esp32h2" + # - idf_branch: "release/v5.3" + # lib_builder_branch: "release/v5.3" + # targets: "esp32,esp32s2,esp32s3,esp32c3,esp32c6,esp32h2,esp32p4" + - idf_branch: "release/v5.4" + lib_builder_branch: "master" + targets: "esp32,esp32s2,esp32s3,esp32c3,esp32c6,esp32h2,esp32p4" + - idf_branch: "release/v5.5" + lib_builder_branch: "release/v5.5" + targets: "esp32,esp32s2,esp32s3,esp32c3,esp32c6,esp32h2,esp32p4,esp32c5" diff --git a/.github/workflows/cron_build.yml b/.github/workflows/cron_build.yml new file mode 100644 index 000000000..fd9634928 --- /dev/null +++ b/.github/workflows/cron_build.yml @@ -0,0 +1,163 @@ +name: Cron Build Matrix + +on: + workflow_call: + inputs: + idf_branch: + type: string + required: true + description: 'IDF branch to build' + lib_builder_branch: + type: string + required: true + description: 'Branch of the lib-builder to use' + targets: + type: string + required: true + description: 'Targets to build' + +env: + IDF_BRANCH: ${{ inputs.idf_branch }} + +jobs: + check-if-needed: + name: Check if deploy is needed for ${{ inputs.idf_branch }} + runs-on: ubuntu-latest + outputs: + idf_commit: ${{ steps.check.outputs.idf_commit }} + ar_branch: ${{ steps.check.outputs.ar_branch }} + ar_new_commit_message: ${{ steps.check.outputs.ar_new_commit_message }} + ar_new_branch_name: ${{ steps.check.outputs.ar_new_branch_name }} + ar_new_pr_title: ${{ steps.check.outputs.ar_new_pr_title }} + ar_has_commit: ${{ steps.check.outputs.ar_has_commit }} + ar_has_branch: ${{ steps.check.outputs.ar_has_branch }} + ar_has_pr: ${{ steps.check.outputs.ar_has_pr }} + libs_release_tag: ${{ steps.check.outputs.libs_release_tag }} + libs_version: ${{ steps.check.outputs.libs_version }} + libs_release_id: ${{ steps.check.outputs.libs_release_id }} + libs_has_release: ${{ steps.check.outputs.libs_has_release }} + libs_asset_id: ${{ steps.check.outputs.libs_asset_id }} + libs_has_asset: ${{ steps.check.outputs.libs_has_asset }} + deploy_needed: ${{ steps.check.outputs.deploy_needed }} + targets_list: ${{ steps.check.outputs.targets_list }} + steps: + - name: Checkout ${{ inputs.lib_builder_branch }} + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ inputs.lib_builder_branch }} + + - name: Check deploy and generate variables + id: check + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + source ./tools/check-deploy-needed.sh + targets_list=$(echo "${{ inputs.targets }}" | sed 's/ *, */,/g' | sed 's/^/["/' | sed 's/$/"]/' | sed 's/,/","/g') + echo "Targets list: $targets_list" + echo "targets_list=$targets_list" >> $GITHUB_OUTPUT + + build-libs: + name: Build for ${{ matrix.target }} (${{ inputs.idf_branch }}) + runs-on: ubuntu-latest + if: needs.check-if-needed.outputs.deploy_needed == '1' + needs: check-if-needed + strategy: + fail-fast: false + matrix: + target: ${{ fromJson(needs.check-if-needed.outputs.targets_list) }} + steps: + - name: Checkout ${{ inputs.lib_builder_branch }} + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ inputs.lib_builder_branch }} + + - name: Install dependencies + run: bash ./tools/prepare-ci.sh + + - name: Build + env: + GITHUB_TOKEN: ${{ secrets.PUSH_TOKEN || secrets.GITHUB_TOKEN }} + GIT_AUTHOR_EMAIL: ${{ secrets.PUSH_EMAIL }} + GIT_COMMITTER_EMAIL: ${{ secrets.PUSH_EMAIL }} + TARGET: ${{ matrix.target }} + run: | + bash ./tools/cron.sh + + - name: Replace invalid characters in the artifact name + run: | + branch=${{ inputs.idf_branch }} + echo "libs_branch=${branch//\//_}" >> $GITHUB_ENV + + - name: Upload build + if: failure() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: build-${{ env.libs_branch }}-${{ matrix.target }} + path: build + + - name: Upload library files + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: libs-${{ env.libs_branch }}-${{ matrix.target }} + path: dist + + combine-artifacts: + name: Combine artifacts and push changes for IDF ${{ inputs.idf_branch }} + runs-on: ubuntu-latest + needs: [check-if-needed, build-libs] + if: needs.check-if-needed.outputs.deploy_needed == '1' + steps: + - name: Checkout ${{ inputs.lib_builder_branch }} + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ inputs.lib_builder_branch }} + + - name: Replace invalid characters in the artifact name + run: | + branch=${{ inputs.idf_branch }} + echo "libs_branch=${branch//\//_}" >> $GITHUB_ENV + + - name: Download artifacts + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + with: + path: dist + pattern: libs-${{ env.libs_branch }}-* + merge-multiple: true + + - name: Combine artifacts + run: bash ./tools/combine-artifacts.sh + + - name: Upload full esp32-arduino-libs archive + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: esp32-arduino-libs-${{ env.libs_branch }} + path: dist/esp32-arduino-libs.zip + compression-level: 0 + + - name: Push changes + env: + GITHUB_TOKEN: ${{ secrets.PUSH_TOKEN }} + GIT_AUTHOR_EMAIL: ${{ secrets.PUSH_EMAIL }} + GIT_COMMITTER_EMAIL: ${{ secrets.PUSH_EMAIL }} + IDF_COMMIT: ${{ needs.check-if-needed.outputs.idf_commit }} + AR_BRANCH: ${{ needs.check-if-needed.outputs.ar_branch }} + AR_NEW_COMMIT_MESSAGE: ${{ needs.check-if-needed.outputs.ar_new_commit_message }} + AR_NEW_BRANCH_NAME: ${{ needs.check-if-needed.outputs.ar_new_branch_name }} + AR_NEW_PR_TITLE: ${{ needs.check-if-needed.outputs.ar_new_pr_title }} + AR_HAS_COMMIT: ${{ needs.check-if-needed.outputs.ar_has_commit }} + AR_HAS_BRANCH: ${{ needs.check-if-needed.outputs.ar_has_branch }} + AR_HAS_PR: ${{ needs.check-if-needed.outputs.ar_has_pr }} + LIBS_RELEASE_TAG: ${{ needs.check-if-needed.outputs.libs_release_tag }} + LIBS_VERSION: ${{ needs.check-if-needed.outputs.libs_version }} + LIBS_RELEASE_ID: ${{ needs.check-if-needed.outputs.libs_release_id }} + LIBS_HAS_RELEASE: ${{ needs.check-if-needed.outputs.libs_has_release }} + LIBS_ASSET_ID: ${{ needs.check-if-needed.outputs.libs_asset_id }} + LIBS_HAS_ASSET: ${{ needs.check-if-needed.outputs.libs_has_asset }} + run: | + bash ./tools/push-to-arduino.sh + + - name: Upload package_esp32_index.template.json + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: package-esp32-index-json-${{ env.libs_branch }} + path: out/package_esp32_index.template.json diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..c89352150 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,102 @@ +name: Build and push Docker image + +on: + push: + branches: + - 'master' + - 'release/*' + tags: + - 'v*.*' + pull_request: + paths: + - ".github/workflows/docker.yml" + - "tools/config_editor/requirements.txt" + - "tools/docker/Dockerfile" + - "tools/docker/entrypoint.sh" + +env: + # Build the image for amd64 and arm64 + BUILD_PLATFORMS: linux/amd64,linux/arm64 + DOCKERHUB_REPO: ${{ github.repository_owner }}/esp32-arduino-lib-builder + +jobs: + docker: + # Disable the job in forks + if: ${{ github.event_name == 'pull_request' || github.repository_owner == 'espressif' }} + name: Build docker image and push if needed + runs-on: ubuntu-latest + steps: + # Depending on the branch/tag, set CLONE_BRANCH_OR_TAG variable (used in the Dockerfile + # as a build arg) and TAG_NAME (used when tagging the image). + # + # The following 3 steps cover the alternatives (tag, release branch, master branch): + - name: Set variables (tags) + if: ${{ github.ref_type == 'tag' }} + run: | + echo "CLONE_BRANCH_OR_TAG=$GITHUB_REF_NAME" >> $GITHUB_ENV + echo "TAG_NAME=$GITHUB_REF_NAME" >> $GITHUB_ENV + echo "URL=${{ github.server_url }}/${{ github.repository }}.git" >> $GITHUB_ENV + + - name: Set variables (release branches) + if: ${{ github.ref_type == 'branch' && startsWith(github.ref_name, 'release/') }} + run: | + echo "CLONE_BRANCH_OR_TAG=$GITHUB_REF_NAME" >> $GITHUB_ENV + echo "TAG_NAME=release-${GITHUB_REF_NAME##release/}" >> $GITHUB_ENV + echo "URL=${{ github.server_url }}/${{ github.repository }}.git" >> $GITHUB_ENV + + - name: Set variables (main branch) + if: ${{ github.ref_type == 'branch' && github.ref_name == 'master' }} + run: | + echo "CLONE_BRANCH_OR_TAG=master" >> $GITHUB_ENV + echo "TAG_NAME=latest" >> $GITHUB_ENV + echo "URL=${{ github.server_url }}/${{ github.repository }}.git" >> $GITHUB_ENV + + - name: Set variables (pull requests) + if: ${{ github.event_name == 'pull_request' }} + run: | + echo "CLONE_BRANCH_OR_TAG=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV + echo "TAG_NAME=PR_${{ github.event.number }}" >> $GITHUB_ENV + echo "URL=${{ github.server_url }}/${{ github.event.pull_request.head.repo.full_name }}.git" >> $GITHUB_ENV + + # Display the variables set above, just in case. + - name: Check variables + run: | + echo "CLONE_BRANCH_OR_TAG: $CLONE_BRANCH_OR_TAG" + echo "TAG_NAME: $TAG_NAME" + echo "URL: $URL" + + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Login to Docker Hub + if: ${{ github.event_name == 'push' }} + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up QEMU for multiarch builds + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + + - name: Build and push + uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + with: + context: tools/docker + push: ${{ github.event_name == 'push' }} + tags: ${{ env.DOCKERHUB_REPO }}:${{ env.TAG_NAME }} + platforms: ${{ env.BUILD_PLATFORMS }} + build-args: | + LIBBUILDER_CLONE_URL=${{ env.URL }} + LIBBUILDER_CLONE_BRANCH_OR_TAG=${{ env.CLONE_BRANCH_OR_TAG }} + + - name: Update Docker Hub repository description (master branch) + if: ${{ github.event_name == 'push' && github.ref_type == 'branch' && github.ref_name == 'master' }} + uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae # v4.0.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + repository: ${{ env.DOCKERHUB_REPO }} + readme-filepath: ./tools/docker/README.md diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index c3edd52d3..756ab0991 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -5,34 +5,47 @@ on: branches: - master pull_request: + paths: + - "**" + - "!**.md" + - "!.github/workflows/cron_build.yml" + - "!.github/workflows/cron.yml" + - "!.github/workflows/docker.yml" + - "!.github/workflows/repository_dispatch.yml" + - "!tools/config_editor/**" + - "!tools/docker/**" concurrency: group: esp-idf-libs-${{github.event.pull_request.number || github.ref}} cancel-in-progress: true jobs: - build-libs: name: Build Libs for ${{ matrix.target }} runs-on: ubuntu-latest strategy: matrix: - target: [esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2] + target: [esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2, esp32p4] fail-fast: false steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Install dependencies run: bash ./tools/prepare-ci.sh + - name: Build Libs for ${{ matrix.target }} run: bash ./build.sh -e -t ${{ matrix.target }} + - name: Upload build if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: build-${{ matrix.target }} path: build + - name: Upload archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: artifacts-${{ matrix.target }} path: dist @@ -43,24 +56,27 @@ jobs: runs-on: ubuntu-latest steps: - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 with: path: dist pattern: artifacts-* merge-multiple: true + - shell: bash run: | mkdir -p out find dist -name 'arduino-esp32-libs-esp*.tar.gz' -exec tar zxvf {} -C out \; cd out/tools/esp32-arduino-libs && tar zcf ../../../dist/esp32-arduino-libs.tar.gz * && cd ../../.. cp out/package_esp32_index.template.json dist/package_esp32_index.template.json + - name: Upload full esp32-arduino-libs archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: esp32-arduino-libs path: dist/esp32-arduino-libs.tar.gz + - name: Upload package_esp32_index.template.json - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: package-esp32-index-json path: dist/package_esp32_index.template.json diff --git a/.github/workflows/repository_dispatch.yml b/.github/workflows/repository_dispatch.yml index a18412a3d..62837976b 100644 --- a/.github/workflows/repository_dispatch.yml +++ b/.github/workflows/repository_dispatch.yml @@ -7,25 +7,30 @@ jobs: name: Dispatch Event runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 + - name: Install dependencies run: bash ./tools/prepare-ci.sh + - name: Handle Event env: GITHUB_TOKEN: ${{ secrets.PUSH_TOKEN }} GIT_AUTHOR_EMAIL: ${{ secrets.PUSH_EMAIL }} GIT_COMMITTER_EMAIL: ${{ secrets.PUSH_EMAIL }} run: bash ./tools/repository_dispatch.sh + - name: Upload build if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: build path: build + - name: Upload archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: artifacts path: dist diff --git a/CMakeLists.txt b/CMakeLists.txt index 88cede199..b79d85c0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,3 +33,10 @@ add_custom_command( VERBATIM ) add_custom_target(mem-variant DEPENDS "mem_variant") + +idf_build_set_property(COMPILE_DEFINITIONS "-DESP32_ARDUINO_LIB_BUILDER" APPEND) + +################## +### ESP Matter ### +################## +idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++2a;-DCHIP_HAVE_CONFIG_H" APPEND) diff --git a/README.md b/README.md index 8133c0d7a..16452f7e1 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,33 @@ git clone https://github.com/espressif/esp32-arduino-lib-builder cd esp32-arduino-lib-builder ./build.sh ``` + +### Using the User Interface + +You can more easily build the libraries using the user interface found in the `tools/config_editor/` folder. +It is a Python script that allows you to select and edit the options for the libraries you want to build. +The script has mouse support and can also be pre-configured using the same command line arguments as the `build.sh` script. +For more information and troubleshooting, please refer to the [UI README](tools/config_editor/README.md). + +To use it, follow these steps: + +1. Make sure you have the following prerequisites: + - Python 3.9 or later + - All the dependencies listed in the previous section + +2. Install the required UI packages using `pip install -r tools/config_editor/requirements.txt`. + +3. Execute the script `tools/config_editor/app.py` from any folder. It will automatically detect the path to the root of the repository. + +4. Configure the compilation and ESP-IDF options as desired. + +5. Click on the "Compile Static Libraries" button to start the compilation process. + +6. The script will show the compilation output in a new screen. Note that the compilation process can take many hours, depending on the number of libraries selected and the options chosen. + +7. If the compilation is successful and the option to copy the libraries to the Arduino Core folder is enabled, it will already be available for use in the Arduino IDE. Otherwise, you can find the compiled libraries in the `esp32-arduino-libs` folder alongside this repository. + - Note that the copy operation doesn't currently support the core downloaded from the Arduino IDE Boards Manager, only the manual installation from the [`arduino-esp32`](https://github.com/espressif/arduino-esp32) repository. + ### Documentation For more information about how to use the Library builder, please refer to this [Documentation page](https://docs.espressif.com/projects/arduino-esp32/en/latest/lib_builder.html?highlight=lib%20builder) diff --git a/build.sh b/build.sh index 39e0dc70f..50e376c2a 100755 --- a/build.sh +++ b/build.sh @@ -1,15 +1,25 @@ #!/bin/bash if ! [ -x "$(command -v python3)" ]; then - echo "ERROR: python is not installed! Please install python first." + echo "ERROR: python is not installed or not in PATH! Please install python first." exit 1 fi if ! [ -x "$(command -v git)" ]; then - echo "ERROR: git is not installed! Please install git first." + echo "ERROR: git is not installed or not in PATH! Please install git first." exit 1 fi +if ! [ -x "$(command -v ninja)" ]; then + echo "ERROR: ninja is not installed or not in PATH! Please install ninja first." + exit 1 +fi + +# Fixes building some components. See https://github.com/espressif/arduino-esp32/issues/10167 +export IDF_COMPONENT_OVERWRITE_MANAGED_COMPONENTS=1 + +CCACHE_ENABLE=1 + TARGET="all" BUILD_TYPE="all" BUILD_DEBUG="default" @@ -21,8 +31,9 @@ if [ -z $DEPLOY_OUT ]; then fi function print_help() { - echo "Usage: build.sh [-s] [-A ] [-I ] [-D ] [-i ] [-c ] [-t ] [-b ] [config ...]" + echo "Usage: build.sh [-s] [-n] [-A ] [-I ] [-D ] [-i ] [-c ] [-t ] [-b ] [config ...]" echo " -s Skip installing/updating of ESP-IDF and all components" + echo " -n Disable ccache" echo " -A Set which branch of arduino-esp32 to be used for compilation" echo " -I Set which branch of ESP-IDF to be used for compilation" echo " -i Set which commit of ESP-IDF to be used for compilation" @@ -30,7 +41,7 @@ function print_help() { echo " -d Deploy the build to github arduino-esp32" echo " -D Debug level to be set to ESP-IDF. One of default,none,error,warning,info,debug or verbose" echo " -c Set the arduino-esp32 folder to copy the result to. ex. '$HOME/Arduino/hardware/espressif/esp32'" - echo " -t Set the build target(chip). ex. 'esp32s3'" + echo " -t Set the build target(chip) ex. 'esp32s3' or select multiple targets(chips) by separating them with comma ex. 'esp32,esp32s3,esp32c3'" echo " -b Set the build type. ex. 'build' to build the project and prepare for uploading to a board" echo " ... Specify additional configs to be applied. ex. 'qio 80m' to compile for QIO Flash@80MHz. Requires -b" exit 1 @@ -41,6 +52,9 @@ while getopts ":A:I:i:c:t:b:D:sde" opt; do s ) SKIP_ENV=1 ;; + n ) + CCACHE_ENABLE=0 + ;; d ) DEPLOY_OUT=1 ;; @@ -64,7 +78,7 @@ while getopts ":A:I:i:c:t:b:D:sde" opt; do BUILD_DEBUG="$OPTARG" ;; t ) - TARGET=$OPTARG + IFS=',' read -ra TARGET <<< "$OPTARG" ;; b ) b=$OPTARG @@ -91,6 +105,11 @@ done shift $((OPTIND -1)) CONFIGS=$@ +export IDF_CCACHE_ENABLE=$CCACHE_ENABLE + +# Output the TARGET array +echo "TARGET(s): ${TARGET[@]}" + mkdir -p dist if [ $SKIP_ENV -eq 0 ]; then @@ -112,36 +131,47 @@ else source ./tools/config.sh fi -if [ -f "$AR_MANAGED_COMPS/espressif__esp-sr/.component_hash" ]; then - rm -rf $AR_MANAGED_COMPS/espressif__esp-sr/.component_hash -fi - if [ "$BUILD_TYPE" != "all" ]; then if [ "$TARGET" = "all" ]; then echo "ERROR: You need to specify target for non-default builds" print_help fi - configs="configs/defconfig.common;configs/defconfig.$TARGET;configs/defconfig.debug_$BUILD_DEBUG" - + # Target Features Configs for target_json in `jq -c '.targets[]' configs/builds.json`; do target=$(echo "$target_json" | jq -c '.target' | tr -d '"') - if [ "$TARGET" == "$target" ]; then - for defconf in `echo "$target_json" | jq -c '.features[]' | tr -d '"'`; do - configs="$configs;configs/defconfig.$defconf" - done + + # Check if $target is in the $TARGET array + target_in_array=false + for item in "${TARGET[@]}"; do + if [ "$item" = "$target" ]; then + target_in_array=true + break + fi + done + + if [ "$target_in_array" = false ]; then + # Skip building for targets that are not in the $TARGET array + continue fi - done - # Configs From Arguments - for conf in $CONFIGS; do - configs="$configs;configs/defconfig.$conf" - done + configs="configs/defconfig.common;configs/defconfig.$target;configs/defconfig.debug_$BUILD_DEBUG" + for defconf in `echo "$target_json" | jq -c '.features[]' | tr -d '"'`; do + configs="$configs;configs/defconfig.$defconf" + done - echo "idf.py -DIDF_TARGET=\"$TARGET\" -DSDKCONFIG_DEFAULTS=\"$configs\" $BUILD_TYPE" - rm -rf build sdkconfig - idf.py -DIDF_TARGET="$TARGET" -DSDKCONFIG_DEFAULTS="$configs" $BUILD_TYPE - if [ $? -ne 0 ]; then exit 1; fi + echo "* Building for $target" + + # Configs From Arguments + for conf in $CONFIGS; do + configs="$configs;configs/defconfig.$conf" + done + + echo "idf.py -DIDF_TARGET=\"$target\" -DSDKCONFIG_DEFAULTS=\"$configs\" $BUILD_TYPE" + rm -rf build sdkconfig + idf.py -DIDF_TARGET="$target" -DSDKCONFIG_DEFAULTS="$configs" $BUILD_TYPE + if [ $? -ne 0 ]; then exit 1; fi + done exit 0 fi @@ -153,9 +183,21 @@ for target_json in `jq -c '.targets[]' configs/builds.json`; do target=$(echo "$target_json" | jq -c '.target' | tr -d '"') target_skip=$(echo "$target_json" | jq -c '.skip // 0') - if [ "$TARGET" != "all" ] && [ "$TARGET" != "$target" ]; then - echo "* Skipping Target: $target" - continue + # Check if $target is in the $TARGET array if not "all" + if [ "$TARGET" != "all" ]; then + target_in_array=false + for item in "${TARGET[@]}"; do + if [ "$item" = "$target" ]; then + target_in_array=true + break + fi + done + + # If $target is not in the $TARGET array, skip processing + if [ "$target_in_array" = false ]; then + echo "* Skipping Target: $target" + continue + fi fi # Skip chips that should not be a part of the final libs @@ -179,10 +221,6 @@ for target_json in `jq -c '.targets[]' configs/builds.json`; do idf_libs_configs="$idf_libs_configs;configs/defconfig.$defconf" done - if [ -f "$AR_MANAGED_COMPS/espressif__esp-sr/.component_hash" ]; then - rm -rf $AR_MANAGED_COMPS/espressif__esp-sr/.component_hash - fi - echo "* Build IDF-Libs: $idf_libs_configs" rm -rf build sdkconfig idf.py -DIDF_TARGET="$target" -DSDKCONFIG_DEFAULTS="$idf_libs_configs" idf-libs @@ -208,10 +246,6 @@ for target_json in `jq -c '.targets[]' configs/builds.json`; do bootloader_configs="$bootloader_configs;configs/defconfig.$defconf"; done - if [ -f "$AR_MANAGED_COMPS/espressif__esp-sr/.component_hash" ]; then - rm -rf $AR_MANAGED_COMPS/espressif__esp-sr/.component_hash - fi - echo "* Build BootLoader: $bootloader_configs" rm -rf build sdkconfig idf.py -DIDF_TARGET="$target" -DSDKCONFIG_DEFAULTS="$bootloader_configs" copy-bootloader @@ -225,10 +259,6 @@ for target_json in `jq -c '.targets[]' configs/builds.json`; do mem_configs="$mem_configs;configs/defconfig.$defconf"; done - if [ -f "$AR_MANAGED_COMPS/espressif__esp-sr/.component_hash" ]; then - rm -rf $AR_MANAGED_COMPS/espressif__esp-sr/.component_hash - fi - echo "* Build Memory Variant: $mem_configs" rm -rf build sdkconfig idf.py -DIDF_TARGET="$target" -DSDKCONFIG_DEFAULTS="$mem_configs" mem-variant @@ -269,35 +299,40 @@ done # update package_esp32_index.template.json if [ "$BUILD_TYPE" = "all" ]; then + echo "* Generating package_esp32_index.template.json..." python3 ./tools/gen_tools_json.py -i "$IDF_PATH" -j "$AR_COMPS/arduino/package/package_esp32_index.template.json" -o "$AR_OUT/" python3 ./tools/gen_tools_json.py -i "$IDF_PATH" -o "$TOOLS_JSON_OUT/" if [ $? -ne 0 ]; then exit 1; fi fi -# Generate PlatformIO manifest file +# Generate pioarduino manifest file if [ "$BUILD_TYPE" = "all" ]; then + echo "* Generating pioarduino manifest file..." pushd $IDF_PATH - ibr=$(git describe --all --exact-match 2>/dev/null) + ibr=$(git describe --all 2>/dev/null) ic=$(git -C "$IDF_PATH" rev-parse --short HEAD) popd - python3 ./tools/gen_platformio_manifest.py -o "$TOOLS_JSON_OUT/" -s "$ibr" -c "$ic" + python3 ./tools/gen_pioarduino_manifest.py -o "$TOOLS_JSON_OUT/" -s "$ibr" -c "$ic" if [ $? -ne 0 ]; then exit 1; fi fi # copy everything to arduino-esp32 installation if [ $COPY_OUT -eq 1 ] && [ -d "$ESP32_ARDUINO" ]; then + echo "* Copying to Arduino..." ./tools/copy-to-arduino.sh if [ $? -ne 0 ]; then exit 1; fi fi # push changes to esp32-arduino-libs and create pull request into arduino-esp32 if [ $DEPLOY_OUT -eq 1 ]; then + echo "* Pushing to Arduino..." ./tools/push-to-arduino.sh if [ $? -ne 0 ]; then exit 1; fi fi # archive the build if [ $ARCHIVE_OUT -eq 1 ]; then + echo "* Archiving build..." ./tools/archive-build.sh "$TARGET" if [ $? -ne 0 ]; then exit 1; fi fi diff --git a/components/arduino_tinyusb/CMakeLists.txt b/components/arduino_tinyusb/CMakeLists.txt index 792b2a39f..fd6f5983d 100755 --- a/components/arduino_tinyusb/CMakeLists.txt +++ b/components/arduino_tinyusb/CMakeLists.txt @@ -15,15 +15,20 @@ if(CONFIG_TINYUSB_ENABLED) "-DCFG_TUSB_DEBUG=${CONFIG_TINYUSB_DEBUG_LEVEL}" "-Wno-type-limits" # needed for the vanila tinyusb with turned off classes ) + elseif(IDF_TARGET STREQUAL "esp32p4") + set(compile_options + "-DCFG_TUSB_MCU=OPT_MCU_ESP32P4" + "-DCFG_TUSB_DEBUG=${CONFIG_TINYUSB_DEBUG_LEVEL}" + "-Wno-type-limits" # needed for the vanila tinyusb with turned off classes + ) endif() set(srcs # espressif: - "${COMPONENT_DIR}/src/dcd_esp32sx.c" - #"${COMPONENT_DIR}/src/dcd_dwc2.c" + "${COMPONENT_DIR}/src/dcd_dwc2.c" # tusb: - #"${COMPONENT_DIR}/tinyusb/src/portable/espressif/esp32sx/dcd_esp32sx.c" - #"{COMPONENT_DIR}/tinyusb/src/portable/synopsys/dwc2/dcd_dwc2.c" + #"${COMPONENT_DIR}/tinyusb/src/portable/synopsys/dwc2/dcd_dwc2.c" + "${COMPONENT_DIR}/tinyusb/src/portable/synopsys/dwc2/dwc2_common.c" "${COMPONENT_DIR}/tinyusb/src/class/cdc/cdc_device.c" "${COMPONENT_DIR}/tinyusb/src/class/hid/hid_device.c" "${COMPONENT_DIR}/tinyusb/src/class/midi/midi_device.c" @@ -32,6 +37,7 @@ if(CONFIG_TINYUSB_ENABLED) "${COMPONENT_DIR}/tinyusb/src/class/dfu/dfu_rt_device.c" "${COMPONENT_DIR}/tinyusb/src/class/dfu/dfu_device.c" "${COMPONENT_DIR}/tinyusb/src/class/vendor/vendor_device.c" + "${COMPONENT_DIR}/tinyusb/src/class/net/ncm_device.c" "${COMPONENT_DIR}/tinyusb/src/common/tusb_fifo.c" "${COMPONENT_DIR}/tinyusb/src/device/usbd_control.c" "${COMPONENT_DIR}/tinyusb/src/device/usbd.c" @@ -56,16 +62,15 @@ if(CONFIG_TINYUSB_ENABLED) set(requires esp_rom freertos soc) set(priv_requires arduino main) - ### tinyusb lib ### - ################### - idf_component_register(INCLUDE_DIRS ${includes_public} PRIV_INCLUDE_DIRS ${includes_private} SRCS ${srcs} REQUIRES ${requires} PRIV_REQUIRES ${priv_requires}) - # add_library(${COMPONENT_TARGET} STATIC ${srcs}) - # target_include_directories( - # ${COMPONENT_TARGET} - # PUBLIC ${includes_public} - # PRIVATE ${includes_private}) + + idf_component_register( + INCLUDE_DIRS ${includes_public} + PRIV_INCLUDE_DIRS ${includes_private} + SRCS ${srcs} + REQUIRES ${requires} + PRIV_REQUIRES ${priv_requires} + ) target_compile_options(${COMPONENT_TARGET} PRIVATE ${compile_options}) - #target_link_libraries(${COMPONENT_TARGET} INTERFACE ${COMPONENT_TARGET}) else() diff --git a/components/arduino_tinyusb/Kconfig.projbuild b/components/arduino_tinyusb/Kconfig.projbuild index 80983657f..f0a1b4dc6 100755 --- a/components/arduino_tinyusb/Kconfig.projbuild +++ b/components/arduino_tinyusb/Kconfig.projbuild @@ -1,9 +1,10 @@ menu "Arduino TinyUSB" + depends on ENABLE_ARDUINO_DEPENDS && SOC_USB_OTG_SUPPORTED config TINYUSB_ENABLED bool "Enable TinyUSB driver" default y - depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4 select FREERTOS_SUPPORT_STATIC_ALLOCATION select FREERTOS_USE_AUTHENTIC_INCLUDE_PATHS help @@ -27,18 +28,28 @@ menu "Arduino TinyUSB" config TINYUSB_CDC_RX_BUFSIZE int "CDC FIFO size of RX" - default 64 + default 64 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 512 if IDF_TARGET_ESP32P4 depends on TINYUSB_CDC_ENABLED help CDC FIFO size of RX config TINYUSB_CDC_TX_BUFSIZE int "CDC FIFO size of TX" - default 64 + default 64 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 512 if IDF_TARGET_ESP32P4 depends on TINYUSB_CDC_ENABLED help CDC FIFO size of TX + config TINYUSB_CDC_MAX_PORTS + int "Maximum enabled CDC ports" + range 1 2 + default 1 + depends on TINYUSB_CDC_ENABLED + help + Maximum enabled CDC ports + endmenu menu "Mass Storage (MSC) driver" @@ -85,7 +96,8 @@ menu "Arduino TinyUSB" config TINYUSB_HID_BUFSIZE int "HID Buffer size" - default 64 + default 64 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 512 if IDF_TARGET_ESP32P4 depends on TINYUSB_HID_ENABLED help HID Buffer size. Should be sufficient to hold ID (if any) + Data @@ -110,14 +122,16 @@ menu "Arduino TinyUSB" config TINYUSB_MIDI_RX_BUFSIZE int "MIDI FIFO size of RX" - default 64 + default 64 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 512 if IDF_TARGET_ESP32P4 depends on TINYUSB_MIDI_ENABLED help MIDI FIFO size of RX config TINYUSB_MIDI_TX_BUFSIZE int "MIDI FIFO size of TX" - default 64 + default 64 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 512 if IDF_TARGET_ESP32P4 depends on TINYUSB_MIDI_ENABLED help MIDI FIFO size of TX @@ -142,8 +156,9 @@ menu "Arduino TinyUSB" config TINYUSB_VIDEO_STREAMING_BUFSIZE int "VIDEO streaming endpoint size" - range 0 64 - default 64 + range 0 512 + default 64 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 512 if IDF_TARGET_ESP32P4 depends on TINYUSB_VIDEO_ENABLED help VIDEO streaming endpoint size @@ -218,20 +233,33 @@ menu "Arduino TinyUSB" config TINYUSB_VENDOR_RX_BUFSIZE int "VENDOR FIFO size of RX" - default 64 + default 64 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 512 if IDF_TARGET_ESP32P4 depends on TINYUSB_VENDOR_ENABLED help VENDOR FIFO size of RX config TINYUSB_VENDOR_TX_BUFSIZE int "VENDOR FIFO size of TX" - default 64 + default 64 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 512 if IDF_TARGET_ESP32P4 depends on TINYUSB_VENDOR_ENABLED help VENDOR FIFO size of TX endmenu + menu "NCM driver" + depends on TINYUSB_ENABLED + + config TINYUSB_NCM_ENABLED + bool "Enable USB NCM TinyUSB driver" + default y + help + Enable USB NCM TinyUSB driver. + + endmenu + config TINYUSB_DEBUG_LEVEL int "TinyUSB log level (0-3)" default 0 diff --git a/components/arduino_tinyusb/include/tusb_config.h b/components/arduino_tinyusb/include/tusb_config.h index ee1e5d270..7802bea8f 100755 --- a/components/arduino_tinyusb/include/tusb_config.h +++ b/components/arduino_tinyusb/include/tusb_config.h @@ -72,11 +72,16 @@ extern "C" { # define CONFIG_TINYUSB_VENDOR_ENABLED 0 #endif +#ifndef CONFIG_TINYUSB_NCM_ENABLED +# define CONFIG_TINYUSB_NCM_ENABLED 0 +#endif + /* */ /* COMMON CONFIGURATION */ /* */ - +#ifndef CFG_TUSB_MCU #define CFG_TUSB_MCU OPT_MCU_ESP32S2 +#endif #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE #define CFG_TUSB_OS OPT_OS_FREERTOS @@ -95,15 +100,26 @@ extern "C" { # define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4) #endif +#if CONFIG_IDF_TARGET_ESP32P4 +#define CFG_TUD_MAX_SPEED OPT_MODE_HIGH_SPEED +#else +#define CFG_TUD_MAX_SPEED OPT_MODE_FULL_SPEED +#endif + /* */ /* DRIVER CONFIGURATION */ /* */ #define CFG_TUD_MAINTASK_SIZE 4096 +#define CFG_TUD_ENDOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_ENDOINT0_SIZE 64 // Enabled Drivers -#define CFG_TUD_CDC CONFIG_TINYUSB_CDC_ENABLED +#ifdef CONFIG_TINYUSB_CDC_MAX_PORTS +#define CFG_TUD_CDC CONFIG_TINYUSB_CDC_MAX_PORTS +#else +#define CFG_TUD_CDC 0 +#endif #define CFG_TUD_MSC CONFIG_TINYUSB_MSC_ENABLED #define CFG_TUD_HID CONFIG_TINYUSB_HID_ENABLED #define CFG_TUD_MIDI CONFIG_TINYUSB_MIDI_ENABLED @@ -112,6 +128,7 @@ extern "C" { #define CFG_TUD_DFU_RUNTIME CONFIG_TINYUSB_DFU_RT_ENABLED #define CFG_TUD_DFU CONFIG_TINYUSB_DFU_ENABLED #define CFG_TUD_VENDOR CONFIG_TINYUSB_VENDOR_ENABLED +#define CFG_TUD_NCM CONFIG_TINYUSB_NCM_ENABLED // CDC FIFO size of TX and RX #define CFG_TUD_CDC_RX_BUFSIZE CONFIG_TINYUSB_CDC_RX_BUFSIZE diff --git a/components/arduino_tinyusb/patches/dcd_dwc2.patch b/components/arduino_tinyusb/patches/dcd_dwc2.patch new file mode 100644 index 000000000..14e6975f0 --- /dev/null +++ b/components/arduino_tinyusb/patches/dcd_dwc2.patch @@ -0,0 +1,84 @@ +--- a/components/arduino_tinyusb/src/dcd_dwc2.c 2024-10-02 12:17:40.000000000 +0300 ++++ b/components/arduino_tinyusb/src/dcd_dwc2.c 2024-10-02 12:19:48.000000000 +0300 +@@ -243,6 +243,17 @@ + //-------------------------------------------------------------------- + // Endpoint + //-------------------------------------------------------------------- ++#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) ++// Keep count of how many FIFOs are in use ++static uint8_t _allocated_fifos = 1; //FIFO0 is always in use ++ ++// Will either return an unused FIFO number, or 0 if all are used. ++static uint8_t get_free_fifo(void) { ++ if (_allocated_fifos < 5) return _allocated_fifos++; ++ return 0; ++} ++#endif ++ + static void edpt_activate(uint8_t rhport, const tusb_desc_endpoint_t* p_endpoint_desc) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + const uint8_t epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); +@@ -266,7 +277,18 @@ + depctl.set_data0_iso_even = 1; + } + if (dir == TUSB_DIR_IN) { +- depctl.tx_fifo_num = epnum; ++ //depctl.tx_fifo_num = epnum; ++ uint8_t fifo_num = epnum; ++#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) ++ // Special Case for EP5, which is used by CDC but not actually called by the driver ++ // we can give it a fake FIFO ++ if (epnum == 5) { ++ fifo_num = epnum; ++ } else { ++ fifo_num = get_free_fifo(); ++ } ++#endif ++ depctl.tx_fifo_num = fifo_num; + } + + dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum]; +@@ -557,6 +579,10 @@ + } + } + ++#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) ++ _allocated_fifos = 1; ++#endif ++ + dfifo_flush_tx(dwc2, 0x10); // all tx fifo + dfifo_flush_rx(dwc2); + +@@ -997,6 +1023,9 @@ + if (gintsts & GINTSTS_USBRST) { + // USBRST is start of reset. + dwc2->gintsts = GINTSTS_USBRST; ++#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) ++ _allocated_fifos = 1; ++#endif + handle_bus_reset(rhport); + } + +@@ -1008,7 +1037,11 @@ + + if (gintsts & GINTSTS_USBSUSP) { + dwc2->gintsts = GINTSTS_USBSUSP; +- dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); ++ //dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); ++ dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); ++#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) ++ _allocated_fifos = 1; ++#endif + } + + if (gintsts & GINTSTS_WKUINT) { +@@ -1025,6 +1058,9 @@ + + if (otg_int & GOTGINT_SEDET) { + dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); ++#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) ++ _allocated_fifos = 1; ++#endif + } + + dwc2->gotgint = otg_int; diff --git a/components/arduino_tinyusb/src/dcd_dwc2.c b/components/arduino_tinyusb/src/dcd_dwc2.c index 67653fda0..ea931ab90 100644 --- a/components/arduino_tinyusb/src/dcd_dwc2.c +++ b/components/arduino_tinyusb/src/dcd_dwc2.c @@ -31,59 +31,22 @@ #if CFG_TUD_ENABLED && defined(TUP_USBIP_DWC2) -#include "device/dcd.h" -#include "dwc2_type.h" - -// Following symbols must be defined by port header -// - _dwc2_controller[]: array of controllers -// - DWC2_EP_MAX: largest EP counts of all controllers -// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset -// - dwc2_dcd_int_enable/dwc2_dcd_int_disable -// - dwc2_remote_wakeup_delay - -#if defined(TUP_USBIP_DWC2_STM32) - #include "dwc2_stm32.h" -#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) - #include "dwc2_esp32.h" -#elif TU_CHECK_MCU(OPT_MCU_GD32VF103) - #include "dwc2_gd32.h" -#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837) - #include "dwc2_bcm.h" -#elif TU_CHECK_MCU(OPT_MCU_EFM32GG) - #include "dwc2_efm32.h" -#elif TU_CHECK_MCU(OPT_MCU_XMC4000) - #include "dwc2_xmc.h" -#else - #error "Unsupported MCUs" +#if !(CFG_TUD_DWC2_SLAVE_ENABLE || CFG_TUD_DWC2_DMA_ENABLE) +#error DWC2 require either CFG_TUD_DWC2_SLAVE_ENABLE or CFG_TUD_DWC2_DMA_ENABLE to be enabled #endif -//--------------------------------------------------------------------+ -// MACRO TYPEDEF CONSTANT ENUM -//--------------------------------------------------------------------+ - -// DWC2 registers -#define DWC2_REG(_port) ((dwc2_regs_t*) _dwc2_controller[_port].reg_base) - // Debug level for DWC2 #define DWC2_DEBUG 2 -#ifndef dcache_clean -#define dcache_clean(_addr, _size) -#endif - -#ifndef dcache_invalidate -#define dcache_invalidate(_addr, _size) -#endif - -#ifndef dcache_clean_invalidate -#define dcache_clean_invalidate(_addr, _size) -#endif - -static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2]; +#include "device/dcd.h" +#include "dwc2_common.h" +//--------------------------------------------------------------------+ +// MACRO TYPEDEF CONSTANT ENUM +//--------------------------------------------------------------------+ typedef struct { - uint8_t * buffer; - tu_fifo_t * ff; + uint8_t* buffer; + tu_fifo_t* ff; uint16_t total_len; uint16_t max_size; uint8_t interval; @@ -92,431 +55,386 @@ typedef struct { static xfer_ctl_t xfer_status[DWC2_EP_MAX][2]; #define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir]) -// EP0 transfers are limited to 1 packet - larger sizes has to be split -static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type - -// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from dwc2->grxfsiz -static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs) -static bool _out_ep_closed; // Flag to check if RX FIFO size needs an update (reduce its size) - -// SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by -static bool _sof_en; - -// Calculate the RX FIFO size according to recommendations from reference manual -static inline uint16_t calc_grxfsiz(uint16_t max_ep_size, uint8_t ep_count) -{ - return 15 + 2*(max_ep_size/4) + 2*ep_count; +typedef struct { + // EP0 transfers are limited to 1 packet - larger sizes has to be split + uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type + uint16_t dfifo_top; // top free location in DFIFO in words + + // Number of IN endpoints active + uint8_t allocated_epin_count; + + // SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by + bool sof_en; +} dcd_data_t; + +static dcd_data_t _dcd_data; + +CFG_TUD_MEM_SECTION static struct { + TUD_EPBUF_DEF(setup_packet, 8); +} _dcd_usbbuf; + +TU_ATTR_ALWAYS_INLINE static inline uint8_t dwc2_ep_count(const dwc2_regs_t* dwc2) { + #if TU_CHECK_MCU(OPT_MCU_GD32VF103) + return DWC2_EP_MAX; + #else + const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2}; + return ghwcfg2.num_dev_ep + 1; + #endif } -static void update_grxfsiz(uint8_t rhport) -{ - dwc2_regs_t * dwc2 = DWC2_REG(rhport); - uint8_t const ep_count = _dwc2_controller[rhport].ep_count; - // Determine largest EP size for RX FIFO - uint16_t max_epsize = 0; - for (uint8_t epnum = 0; epnum < ep_count; epnum++) - { - max_epsize = tu_max16(max_epsize, xfer_status[epnum][TUSB_DIR_OUT].max_size); - } - - // Update size of RX FIFO - dwc2->grxfsiz = calc_grxfsiz(max_epsize, ep_count); +//-------------------------------------------------------------------- +// DMA +//-------------------------------------------------------------------- +#if CFG_TUD_MEM_DCACHE_ENABLE +bool dcd_dcache_clean(const void* addr, uint32_t data_size) { + TU_VERIFY(addr && data_size); + return dwc2_dcache_clean(addr, data_size); } -// Start of Bus Reset -static void bus_reset(uint8_t rhport) -{ - dwc2_regs_t * dwc2 = DWC2_REG(rhport); - uint8_t const ep_count = _dwc2_controller[rhport].ep_count; +bool dcd_dcache_invalidate(const void* addr, uint32_t data_size) { + TU_VERIFY(addr && data_size); + return dwc2_dcache_invalidate(addr, data_size); +} - tu_memclr(xfer_status, sizeof(xfer_status)); - _out_ep_closed = false; +bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) { + TU_VERIFY(addr && data_size); + return dwc2_dcache_clean_invalidate(addr, data_size); +} +#endif - _sof_en = false; +TU_ATTR_ALWAYS_INLINE static inline bool dma_device_enabled(const dwc2_regs_t* dwc2) { + (void) dwc2; + // Internal DMA only + const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2}; + return CFG_TUD_DWC2_DMA_ENABLE && ghwcfg2.arch == GHWCFG2_ARCH_INTERNAL_DMA; +} - // clear device address - dwc2->dcfg &= ~DCFG_DAD_Msk; +static void dma_setup_prepare(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); - // 1. NAK for all OUT endpoints - for ( uint8_t n = 0; n < ep_count; n++ ) - { - dwc2->epout[n].doepctl |= DOEPCTL_SNAK; + if (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a) { + if(dwc2->epout[0].doepctl & DOEPCTL_EPENA) { + return; + } } - // 2. Set up interrupt mask - dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos); - dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM; - dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM; - - // "USB Data FIFOs" section in reference manual - // Peripheral FIFO architecture - // - // The FIFO is split up in a lower part where the RX FIFO is located and an upper part where the TX FIFOs start. - // We do this to allow the RX FIFO to grow dynamically which is possible since the free space is located - // between the RX and TX FIFOs. This is required by ISO OUT EPs which need a bigger FIFO than the standard - // configuration done below. - // - // Dynamically FIFO sizes are of interest only for ISO EPs since all others are usually not opened and closed. - // All EPs other than ISO are opened as soon as the driver starts up i.e. when the host sends a - // configure interface command. Hence, all IN EPs other the ISO will be located at the top. IN ISO EPs are usually - // opened when the host sends an additional command: setInterface. At this point in time - // the ISO EP will be located next to the free space and can change its size. In case more IN EPs change its size - // an additional memory - // - // --------------- 320 or 1024 ( 1280 or 4096 bytes ) - // | IN FIFO 0 | - // --------------- (320 or 1024) - 16 - // | IN FIFO 1 | - // --------------- (320 or 1024) - 16 - x - // | . . . . | - // --------------- (320 or 1024) - 16 - x - y - ... - z - // | IN FIFO MAX | - // --------------- - // | FREE | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits): - // - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN - // - // - All EP OUT shared a unique OUT FIFO which uses - // - 13 for setup packets + control words (up to 3 setup packets). - // - 1 for global NAK (not required/used here). - // - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1" - // - 2 for each used OUT endpoint - // - // Therefore GRXFSIZ = 13 + 1 + 1 + 2 x (Largest-EPsize/4) + 2 x EPOUTnum - // - FullSpeed (64 Bytes ): GRXFSIZ = 15 + 2 x 16 + 2 x ep_count = 47 + 2 x ep_count - // - Highspeed (512 bytes): GRXFSIZ = 15 + 2 x 128 + 2 x ep_count = 271 + 2 x ep_count - // - // NOTE: Largest-EPsize & EPOUTnum is actual used endpoints in configuration. Since DCD has no knowledge - // of the overall picture yet. We will use the worst scenario: largest possible + ep_count - // - // For Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO - // are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended. Maybe provide a macro for application to - // overwrite this. - - // EP0 out max is 64 - dwc2->grxfsiz = calc_grxfsiz(64, ep_count); - - // Setup the control endpoint 0 - _allocated_fifo_words_tx = 16; - - // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) - dwc2->dieptxf0 = (16 << DIEPTXF0_TX0FD_Pos) | (_dwc2_controller[rhport].ep_fifo_size/4 - _allocated_fifo_words_tx); - - // Fixed control EP0 size to 64 bytes - dwc2->epin[0].diepctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos); - xfer_status[0][TUSB_DIR_OUT].max_size = 64; - xfer_status[0][TUSB_DIR_IN ].max_size = 64; - - dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); - - dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT; + // Receive only 1 packet + dwc2->epout[0].doeptsiz = (1 << DOEPTSIZ_STUPCNT_Pos) | (1 << DOEPTSIZ_PKTCNT_Pos) | (8 << DOEPTSIZ_XFRSIZ_Pos); + dwc2->epout[0].doepdma = (uintptr_t) _dcd_usbbuf.setup_packet; + dwc2->epout[0].doepctl |= DOEPCTL_EPENA | DOEPCTL_USBAEP; } -static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t const dir, uint16_t const num_packets, uint16_t total_bytes) -{ - (void) rhport; +//--------------------------------------------------------------------+ +// Data FIFO +//--------------------------------------------------------------------+ - dwc2_regs_t * dwc2 = DWC2_REG(rhport); - // EP0 is limited to one packet each xfer - // We use multiple transaction of xfer->max_size length to get a whole transfer done - if ( epnum == 0 ) - { - xfer_ctl_t *const xfer = XFER_CTL_BASE(epnum, dir); - total_bytes = tu_min16(ep0_pending[dir], xfer->max_size); - ep0_pending[dir] -= total_bytes; - } +/* Device Data FIFO scheme + + The FIFO is split up into + - EPInfo: for storing DMA metadata, only required when use DMA. Maximum size is called + EP_LOC_CNT = ep_fifo_size - ghwcfg3.dfifo_depth. For value less than EP_LOC_CNT, gdfifocfg must be configured before + gahbcfg.dmaen is set + - Buffer mode: 1 word per endpoint direction + - Scatter/Gather DMA: 4 words per endpoint direction + - TX FIFO: one fifo for each IN endpoint. Size is dynamic depending on packet size, starting from top with EP0 IN. + - Shared RX FIFO: a shared fifo for all OUT endpoints. Typically, can hold up to 2 packets of the largest EP size. + + We allocated TX FIFO from top to bottom (using top pointer), this to allow the RX FIFO to grow dynamically which is + possible since the free space is located between the RX and TX FIFOs. + + ---------------- ep_fifo_size + | DxEPIDMAn | + |-------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth) + | IN FIFO 0 | control EP + |-------------| + | IN FIFO 1 | + |-------------| + | . . . . | + |-------------| + | IN FIFO n | + |-------------| + | FREE | + |-------------|-- GRXFSIZ (expandable) + | OUT FIFO | + | ( Shared ) | + --------------- 0 + + According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits): + - Each EP IN needs at least max packet size + - All EP OUT shared a unique OUT FIFO which uses (for Slave or Buffer DMA, Scatt/Gather DMA use different formula): + - 13 for setup packets + control words (up to 3 setup packets). + - 1 for global NAK (not required/used here). + - Largest-EPsize/4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4 + 1)" + - 2 for each used OUT endpoint + + Therefore GRXFSIZ = 13 + 1 + 2 x (Largest-EPsize/4 + 1) + 2 x EPOUTnum +*/ + +TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_device_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) { + return 13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count; +} - // IN and OUT endpoint xfers are interrupt-driven, we just schedule them here. - if ( dir == TUSB_DIR_IN ) - { - dwc2_epin_t* epin = dwc2->epin; +static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; + const uint8_t ep_count = dwc2_controller->ep_count; + const uint8_t epnum = tu_edpt_number(ep_addr); + const uint8_t dir = tu_edpt_dir(ep_addr); - // A full IN transfer (multiple packets, possibly) triggers XFRC. - epin[epnum].dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) | - ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk); + TU_ASSERT(epnum < ep_count); - epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK; + uint16_t fifo_size = tu_div_ceil(packet_size, 4); + if (dir == TUSB_DIR_OUT) { + // Calculate required size of RX FIFO + const uint16_t new_sz = calc_device_grxfsiz(4 * fifo_size, ep_count); - // For ISO endpoint set correct odd/even bit for next frame. - if ( (epin[epnum].diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1 ) - { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); - epin[epnum].diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk); - } - // Enable fifo empty interrupt only if there are something to put in the fifo. - if ( total_bytes != 0 ) - { - dwc2->diepempmsk |= (1 << epnum); + // If size_rx needs to be extended check if there is enough free space + if (dwc2->grxfsiz < new_sz) { + TU_ASSERT(new_sz <= _dcd_data.dfifo_top); + dwc2->grxfsiz = new_sz; // Enlarge RX FIFO } - } - else - { - dwc2_epout_t* epout = dwc2->epout; - - // A full OUT transfer (multiple packets, possibly) triggers XFRC. - epout[epnum].doeptsiz &= ~(DOEPTSIZ_PKTCNT_Msk | DOEPTSIZ_XFRSIZ); - epout[epnum].doeptsiz |= (num_packets << DOEPTSIZ_PKTCNT_Pos) | - ((total_bytes << DOEPTSIZ_XFRSIZ_Pos) & DOEPTSIZ_XFRSIZ_Msk); - - epout[epnum].doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK; - if ( (epout[epnum].doepctl & DOEPCTL_EPTYP) == DOEPCTL_EPTYP_0 && - XFER_CTL_BASE(epnum, dir)->interval == 1 ) - { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); - epout[epnum].doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk); + } else { + // Check IN endpoints concurrently active limit + if(dwc2_controller->ep_in_count) { + TU_ASSERT(_dcd_data.allocated_epin_count < dwc2_controller->ep_in_count); + _dcd_data.allocated_epin_count++; } - } -} - -/*------------------------------------------------------------------*/ -/* Controller API - *------------------------------------------------------------------*/ -#if CFG_TUSB_DEBUG >= DWC2_DEBUG -void print_dwc2_info(dwc2_regs_t * dwc2) -{ - dwc2_ghwcfg2_t const * hw_cfg2 = &dwc2->ghwcfg2_bm; - dwc2_ghwcfg3_t const * hw_cfg3 = &dwc2->ghwcfg3_bm; - dwc2_ghwcfg4_t const * hw_cfg4 = &dwc2->ghwcfg4_bm; - -// TU_LOG_HEX(DWC2_DEBUG, dwc2->gotgctl); -// TU_LOG_HEX(DWC2_DEBUG, dwc2->gusbcfg); -// TU_LOG_HEX(DWC2_DEBUG, dwc2->dcfg); - TU_LOG_HEX(DWC2_DEBUG, dwc2->guid); - TU_LOG_HEX(DWC2_DEBUG, dwc2->gsnpsid); - TU_LOG_HEX(DWC2_DEBUG, dwc2->ghwcfg1); - - // HW configure 2 - TU_LOG(DWC2_DEBUG, "\r\n"); - TU_LOG_HEX(DWC2_DEBUG, dwc2->ghwcfg2); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->op_mode ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->arch ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->point2point ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->hs_phy_type ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->fs_phy_type ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->num_dev_ep ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->num_host_ch ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->period_channel_support ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->enable_dynamic_fifo ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->mul_cpu_int ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->nperiod_tx_q_depth ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->host_period_tx_q_depth ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->dev_token_q_depth ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg2->otg_enable_ic_usb ); - - // HW configure 3 - TU_LOG(DWC2_DEBUG, "\r\n"); - TU_LOG_HEX(DWC2_DEBUG, dwc2->ghwcfg3); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->xfer_size_width ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->packet_size_width ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->otg_enable ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->i2c_enable ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->vendor_ctrl_itf ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->optional_feature_removed ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->synch_reset ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->otg_adp_support ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->otg_enable_hsic ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->battery_charger_support ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->lpm_mode ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg3->total_fifo_size ); - - // HW configure 4 - TU_LOG(DWC2_DEBUG, "\r\n"); - TU_LOG_HEX(DWC2_DEBUG, dwc2->ghwcfg4); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->num_dev_period_in_ep ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->power_optimized ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->ahb_freq_min ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->hibernation ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->service_interval_mode ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->ipg_isoc_en ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->acg_enable ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->utmi_phy_data_width ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->dev_ctrl_ep_num ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->iddg_filter_enabled ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->vbus_valid_filter_enabled ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->a_valid_filter_enabled ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->b_valid_filter_enabled ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->dedicated_fifos ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->num_dev_in_eps ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->dma_desc_enable ); - TU_LOG_INT(DWC2_DEBUG, hw_cfg4->dma_dynamic ); -} -#endif -static void reset_core(dwc2_regs_t * dwc2) -{ - // reset core - dwc2->grstctl |= GRSTCTL_CSRST; + // If The TXFELVL is configured as half empty, the fifo must be twice the max_size. + if ((dwc2->gahbcfg & GAHBCFG_TX_FIFO_EPMTY_LVL) == 0) { + fifo_size *= 2; + } - // wait for reset bit is cleared - // TODO version 4.20a should wait for RESET DONE mask - while (dwc2->grstctl & GRSTCTL_CSRST) { } + // Check if free space is available + TU_ASSERT(_dcd_data.dfifo_top >= fifo_size + dwc2->grxfsiz); + _dcd_data.dfifo_top -= fifo_size; + // TU_LOG(DWC2_DEBUG, " TX FIFO %u: allocated %u words at offset %u\r\n", epnum, fifo_size, dfifo_top); - // wait for AHB master IDLE - while ( !(dwc2->grstctl & GRSTCTL_AHBIDL) ) { } + // Both TXFD and TXSA are in unit of 32-bit words. + if (epnum == 0) { + dwc2->dieptxf0 = (fifo_size << DIEPTXF0_TX0FD_Pos) | _dcd_data.dfifo_top; + } else { + // DIEPTXF starts at FIFO #1. + dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | _dcd_data.dfifo_top; + } + } - // wait for device mode ? + return true; } -static bool phy_hs_supported(dwc2_regs_t * dwc2) -{ - // note: esp32 incorrect report its hs_phy_type as utmi -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) - return false; -#else - return TUD_OPT_HIGH_SPEED && dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE; -#endif -} +static void dfifo_device_init(uint8_t rhport) { + const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2->grxfsiz = calc_device_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count); -static void phy_fs_init(dwc2_regs_t * dwc2) -{ - TU_LOG(DWC2_DEBUG, "Fullspeed PHY init\r\n"); + // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction + const bool is_dma = dma_device_enabled(dwc2); + _dcd_data.dfifo_top = dwc2_controller->ep_fifo_size/4; + if (is_dma) { + _dcd_data.dfifo_top -= 2 * dwc2_controller->ep_count; + } + dwc2->gdfifocfg = (_dcd_data.dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | _dcd_data.dfifo_top; - // Select FS PHY - dwc2->gusbcfg |= GUSBCFG_PHYSEL; + // Allocate FIFO for EP0 IN + dfifo_alloc(rhport, 0x80, CFG_TUD_ENDPOINT0_SIZE); +} - // MCU specific PHY init before reset - dwc2_phy_init(dwc2, HS_PHY_TYPE_NONE); - // Reset core after selecting PHY - reset_core(dwc2); +//-------------------------------------------------------------------- +// Endpoint +//-------------------------------------------------------------------- +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) +// Keep count of how many FIFOs are in use +static uint8_t _allocated_fifos = 1; //FIFO0 is always in use - // USB turnaround time is critical for certification where long cables and 5-Hubs are used. - // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical, - // these bits can be programmed to a larger value. Default is 5 - dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos); +// Will either return an unused FIFO number, or 0 if all are used. +static uint8_t get_free_fifo(void) { + if (_allocated_fifos < 5) return _allocated_fifos++; + return 0; +} +#endif - // MCU specific PHY update post reset - dwc2_phy_update(dwc2, HS_PHY_TYPE_NONE); +static void edpt_activate(uint8_t rhport, const tusb_desc_endpoint_t* p_endpoint_desc) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + const uint8_t epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); + const uint8_t dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); + + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); + xfer->max_size = tu_edpt_packet_size(p_endpoint_desc); + xfer->interval = p_endpoint_desc->bInterval; + + // Endpoint control + dwc2_depctl_t depctl = {.value = 0}; + depctl.mps = xfer->max_size; + depctl.active = 1; + depctl.type = p_endpoint_desc->bmAttributes.xfer; + if (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) { + depctl.set_data0_iso_even = 1; + } + if (dir == TUSB_DIR_IN) { + //depctl.tx_fifo_num = epnum; + uint8_t fifo_num = epnum; +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + // Special Case for EP5, which is used by CDC but not actually called by the driver + // we can give it a fake FIFO + if (epnum == 5) { + fifo_num = epnum; + } else { + fifo_num = get_free_fifo(); + } +#endif + depctl.tx_fifo_num = fifo_num; + } - // set max speed - dwc2->dcfg = (dwc2->dcfg & ~DCFG_DSPD_Msk) | (DCFG_DSPD_FS << DCFG_DSPD_Pos); + dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum]; + dep->ctl = depctl.value; + dwc2->daintmsk |= TU_BIT(epnum + DAINT_SHIFT(dir)); } -static void phy_hs_init(dwc2_regs_t * dwc2) -{ - uint32_t gusbcfg = dwc2->gusbcfg; +static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) { + (void) rhport; - // De-select FS PHY - gusbcfg &= ~GUSBCFG_PHYSEL; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + const uint8_t epnum = tu_edpt_number(ep_addr); + const uint8_t dir = tu_edpt_dir(ep_addr); + dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum]; - if (dwc2->ghwcfg2_bm.hs_phy_type == HS_PHY_TYPE_ULPI) - { - TU_LOG(DWC2_DEBUG, "Highspeed ULPI PHY init\r\n"); + if (dir == TUSB_DIR_IN) { + // Only disable currently enabled non-control endpoint + if ((epnum == 0) || !(dep->diepctl & DIEPCTL_EPENA)) { + dep->diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0); + } else { + // Stop transmitting packets and NAK IN xfers. + dep->diepctl |= DIEPCTL_SNAK; + while ((dep->diepint & DIEPINT_INEPNE) == 0) {} - // Select ULPI - gusbcfg |= GUSBCFG_ULPI_UTMI_SEL; + // Disable the endpoint. + dep->diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0); + while ((dep->diepint & DIEPINT_EPDISD_Msk) == 0) {} - // ULPI 8-bit interface, single data rate - gusbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); + dep->diepint = DIEPINT_EPDISD; + } - // default internal VBUS Indicator and Drive - gusbcfg &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI); + // Flush the FIFO, and wait until we have confirmed it cleared. + dfifo_flush_tx(dwc2, epnum); + } else { + // Only disable currently enabled non-control endpoint + if ((epnum == 0) || !(dep->doepctl & DOEPCTL_EPENA)) { + dep->doepctl |= stall ? DOEPCTL_STALL : 0; + } else { + // Asserting GONAK is required to STALL an OUT endpoint. + // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt + // anyway, and it can't be cleared by user code. If this while loop never + // finishes, we have bigger problems than just the stack. + dwc2->dctl |= DCTL_SGONAK; + while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {} - // Disable FS/LS ULPI - gusbcfg &= ~(GUSBCFG_ULPIFSLS | GUSBCFG_ULPICSM); - }else - { - TU_LOG(DWC2_DEBUG, "Highspeed UTMI+ PHY init\r\n"); + // Ditto here disable the endpoint. + dep->doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0); + while ((dep->doepint & DOEPINT_EPDISD_Msk) == 0) {} - // Select UTMI+ with 8-bit interface - gusbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); + dep->doepint = DOEPINT_EPDISD; - // Set 16-bit interface if supported - if (dwc2->ghwcfg4_bm.utmi_phy_data_width) gusbcfg |= GUSBCFG_PHYIF16; + // Allow other OUT endpoints to keep receiving. + dwc2->dctl |= DCTL_CGONAK; + } } - - // Apply config - dwc2->gusbcfg = gusbcfg; - - // mcu specific phy init - dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type); - - // Reset core after selecting PHY - reset_core(dwc2); - - // Set turn-around, must after core reset otherwise it will be clear - // - 9 if using 8-bit PHY interface - // - 5 if using 16-bit PHY interface - gusbcfg &= ~GUSBCFG_TRDT_Msk; - gusbcfg |= (dwc2->ghwcfg4_bm.utmi_phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos; - dwc2->gusbcfg = gusbcfg; - - // MCU specific PHY update post reset - dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type); - - // Set max speed - uint32_t dcfg = dwc2->dcfg; - dcfg &= ~DCFG_DSPD_Msk; - dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos; - - // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required - // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347) - if (dwc2->ghwcfg2_bm.hs_phy_type == HS_PHY_TYPE_ULPI) dcfg |= DCFG_XCVRDLY; - - dwc2->dcfg = dcfg; } -static bool check_dwc2(dwc2_regs_t * dwc2) -{ -#if CFG_TUSB_DEBUG >= DWC2_DEBUG - print_dwc2_info(dwc2); -#endif - - // For some reasons: GD32VF103 snpsid and all hwcfg register are always zero (skip it) - (void) dwc2; -#if !TU_CHECK_MCU(OPT_MCU_GD32VF103) - uint32_t const gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK; - TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID); -#endif - - return true; -} +static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uint8_t dir) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir); + dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum]; + + uint16_t num_packets; + uint16_t total_bytes; + + // EP0 is limited to one packet per xfer + if (epnum == 0) { + total_bytes = tu_min16(_dcd_data.ep0_pending[dir], xfer->max_size); + _dcd_data.ep0_pending[dir] -= total_bytes; + num_packets = 1; + } else { + total_bytes = xfer->total_len; + num_packets = tu_div_ceil(total_bytes, xfer->max_size); + if (num_packets == 0) { + num_packets = 1; // zero length packet still count as 1 + } + } -void dcd_init (uint8_t rhport) -{ - // Programming model begins in the last section of the chapter on the USB - // peripheral in each Reference Manual. - dwc2_regs_t * dwc2 = DWC2_REG(rhport); + // transfer size: A full OUT transfer (multiple packets, possibly) triggers XFRC. + dwc2_ep_tsize_t deptsiz = {.value = 0}; + deptsiz.xfer_size = total_bytes; + deptsiz.packet_count = num_packets; + dep->tsiz = deptsiz.value; + + // control + dwc2_depctl_t depctl = {.value = dep->ctl}; + depctl.clear_nak = 1; + depctl.enable = 1; + if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) { + const dwc2_dsts_t dsts = {.value = dwc2->dsts}; + const uint32_t odd_now = dsts.frame_number & 1u; + if (odd_now) { + depctl.set_data0_iso_even = 1; + } else { + depctl.set_data1_iso_odd = 1; + } + } - // Check Synopsys ID register, failed if controller clock/power is not enabled - TU_VERIFY(check_dwc2(dwc2), ); + const bool is_dma = dma_device_enabled(dwc2); + if(is_dma) { + if (dir == TUSB_DIR_IN && total_bytes != 0) { + dcd_dcache_clean(xfer->buffer, total_bytes); + } + dep->diepdma = (uintptr_t) xfer->buffer; + dep->diepctl = depctl.value; // enable endpoint + } else { + dep->diepctl = depctl.value; // enable endpoint - dcd_disconnect(rhport); + // Enable tx fifo empty interrupt only if there is data. Note must after depctl enable + if (dir == TUSB_DIR_IN && total_bytes != 0) { + dwc2->diepempmsk |= (1 << epnum); + } + } +} - // max number of endpoints & total_fifo_size are: - // hw_cfg2->num_dev_ep, hw_cfg2->total_fifo_size - - if( phy_hs_supported(dwc2) ) - { - // Highspeed - phy_hs_init(dwc2); - }else - { - // core does not support highspeed or hs-phy is not present - phy_fs_init(dwc2); +//-------------------------------------------------------------------- +// Controller API +//-------------------------------------------------------------------- +bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { + (void) rh_init; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + tu_memclr(&_dcd_data, sizeof(_dcd_data)); + + // Core Initialization + const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE); + const bool is_dma = dma_device_enabled(dwc2); + TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma)); + + //------------- 7.1 Device Initialization -------------// + // Set device max speed + uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk; + if (is_highspeed) { + dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos; + + // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required + // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347) + const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2}; + if (ghwcfg2.hs_phy_type == GHWCFG2_HSPHY_ULPI) { + dcfg |= DCFG_XCVRDLY; + } + } else { + dcfg |= DCFG_DSPD_FS << DCFG_DSPD_Pos; } - // Restart PHY clock - dwc2->pcgctl &= ~(PCGCTL_STOPPCLK | PCGCTL_GATEHCLK | PCGCTL_PWRCLMP | PCGCTL_RSTPDWNMODULE); + dcfg |= DCFG_NZLSOHSK; // send STALL back and discard if host send non-zlp during control status + dwc2->dcfg = dcfg; - /* Set HS/FS Timeout Calibration to 7 (max available value). - * The number of PHY clocks that the application programs in - * this field is added to the high/full speed interpacket timeout - * duration in the core to account for any additional delays - * introduced by the PHY. This can be required, because the delay - * introduced by the PHY in generating the linestate condition - * can vary from one PHY to another. - */ - dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos); + dcd_disconnect(rhport); // Force device mode dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD; @@ -524,60 +442,39 @@ void dcd_init (uint8_t rhport) // Clear A override, force B Valid dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; - // If USB host misbehaves during status portion of control xfer - // (non zero-length packet), send STALL back and discard. - dwc2->dcfg |= DCFG_NZLSOHSK; - - // Clear all interrupts - uint32_t int_mask = dwc2->gintsts; - dwc2->gintsts |= int_mask; - int_mask = dwc2->gotgint; - dwc2->gotgint |= int_mask; + // Enable required interrupts + dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; - // Required as part of core initialization. - // TODO: How should mode mismatch be handled? It will cause - // the core to stop working/require reset. - dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_MMISM | GINTMSK_RXFLVLM | - GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; - - // Enable global interrupt - dwc2->gahbcfg |= GAHBCFG_GINT; - - // make sure we are in device mode -// TU_ASSERT(!(dwc2->gintsts & GINTSTS_CMOD), ); - -// TU_LOG_HEX(DWC2_DEBUG, dwc2->gotgctl); -// TU_LOG_HEX(DWC2_DEBUG, dwc2->gusbcfg); -// TU_LOG_HEX(DWC2_DEBUG, dwc2->dcfg); -// TU_LOG_HEX(DWC2_DEBUG, dwc2->gahbcfg); + // TX FIFO empty level for interrupt is complete empty + uint32_t gahbcfg = dwc2->gahbcfg; + gahbcfg |= GAHBCFG_TX_FIFO_EPMTY_LVL; + gahbcfg |= GAHBCFG_GINT; // Enable global interrupt + dwc2->gahbcfg = gahbcfg; dcd_connect(rhport); + return true; } -void dcd_int_enable (uint8_t rhport) -{ +void dcd_int_enable(uint8_t rhport) { dwc2_dcd_int_enable(rhport); } -void dcd_int_disable (uint8_t rhport) -{ +void dcd_int_disable(uint8_t rhport) { dwc2_dcd_int_disable(rhport); } -void dcd_set_address (uint8_t rhport, uint8_t dev_addr) -{ - dwc2_regs_t * dwc2 = DWC2_REG(rhport); +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); dwc2->dcfg = (dwc2->dcfg & ~DCFG_DAD_Msk) | (dev_addr << DCFG_DAD_Pos); // Response with status after changing device address dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); } -void dcd_remote_wakeup(uint8_t rhport) -{ +void dcd_remote_wakeup(uint8_t rhport) { (void) rhport; - dwc2_regs_t * dwc2 = DWC2_REG(rhport); + dwc2_regs_t* dwc2 = DWC2_REG(rhport); // set remote wakeup dwc2->dctl |= DCTL_RWUSIG; @@ -592,35 +489,51 @@ void dcd_remote_wakeup(uint8_t rhport) dwc2->dctl &= ~DCTL_RWUSIG; } -void dcd_connect(uint8_t rhport) -{ +void dcd_connect(uint8_t rhport) { (void) rhport; - dwc2_regs_t * dwc2 = DWC2_REG(rhport); + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + +#ifdef TUP_USBIP_DWC2_ESP32 + usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; + conf.pad_pull_override = 0; + conf.dp_pullup = 0; + conf.dp_pulldown = 0; + conf.dm_pullup = 0; + conf.dm_pulldown = 0; + USB_WRAP.otg_conf = conf; +#endif + dwc2->dctl &= ~DCTL_SDIS; } -void dcd_disconnect(uint8_t rhport) -{ +void dcd_disconnect(uint8_t rhport) { (void) rhport; - dwc2_regs_t * dwc2 = DWC2_REG(rhport); + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + +#ifdef TUP_USBIP_DWC2_ESP32 + usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; + conf.pad_pull_override = 1; + conf.dp_pullup = 0; + conf.dp_pulldown = 1; + conf.dm_pullup = 0; + conf.dm_pulldown = 1; + USB_WRAP.otg_conf = conf; +#endif + dwc2->dctl |= DCTL_SDIS; } // Be advised: audio, video and possibly other iso-ep classes use dcd_sof_enable() to enable/disable its corresponding ISR on purpose! -void dcd_sof_enable(uint8_t rhport, bool en) -{ +void dcd_sof_enable(uint8_t rhport, bool en) { (void) rhport; - dwc2_regs_t * dwc2 = DWC2_REG(rhport); + dwc2_regs_t* dwc2 = DWC2_REG(rhport); - _sof_en = en; + _dcd_data.sof_en = en; - if (en) - { + if (en) { dwc2->gintsts = GINTSTS_SOF; dwc2->gintmsk |= GINTMSK_SOFM; - } - else - { + } else { dwc2->gintmsk &= ~GINTMSK_SOFM; } } @@ -628,170 +541,71 @@ void dcd_sof_enable(uint8_t rhport, bool en) /*------------------------------------------------------------------*/ /* DCD Endpoint port *------------------------------------------------------------------*/ -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) -// Keep count of how many FIFOs are in use -static uint8_t _allocated_fifos = 1; //FIFO0 is always in use -// Will either return an unused FIFO number, or 0 if all are used. -static uint8_t get_free_fifo(void) -{ - if (_allocated_fifos < 5) return _allocated_fifos++; - return 0; +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { + TU_ASSERT(dfifo_alloc(rhport, desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt))); + edpt_activate(rhport, desc_edpt); + return true; } -#endif - -bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) -{ - (void) rhport; - dwc2_regs_t * dwc2 = DWC2_REG(rhport); +// Close all non-control endpoints, cancel all pending transfers if any. +void dcd_edpt_close_all(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); uint8_t const ep_count = _dwc2_controller[rhport].ep_count; - uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); - - TU_ASSERT(epnum < ep_count); + _dcd_data.allocated_epin_count = 0; - xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); - xfer->max_size = tu_edpt_packet_size(desc_edpt); - xfer->interval = desc_edpt->bInterval; - - uint16_t const fifo_size = tu_div_ceil(xfer->max_size, 4); - - if(dir == TUSB_DIR_OUT) - { - // Calculate required size of RX FIFO - uint16_t const sz = calc_grxfsiz(4*fifo_size, ep_count); - - // If size_rx needs to be extended check if possible and if so enlarge it - if (dwc2->grxfsiz < sz) - { - TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size/4); + // Disable non-control interrupt + dwc2->daintmsk = (1 << DAINTMSK_OEPM_Pos) | (1 << DAINTMSK_IEPM_Pos); - // Enlarge RX FIFO - dwc2->grxfsiz = sz; + for (uint8_t n = 1; n < ep_count; n++) { + for (uint8_t d = 0; d < 2; d++) { + dwc2_dep_t* dep = &dwc2->ep[d][n]; + if (dep->ctl & EPCTL_EPENA) { + dep->ctl |= EPCTL_SNAK | EPCTL_EPDIS; + } + xfer_status[n][1-d].max_size = 0; } - - dwc2->epout[epnum].doepctl |= (1 << DOEPCTL_USBAEP_Pos) | - (desc_edpt->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) | - (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) | - (xfer->max_size << DOEPCTL_MPSIZ_Pos); - - dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum); } - else - { - // "USB Data FIFOs" section in reference manual - // Peripheral FIFO architecture - // - // --------------- 320 or 1024 ( 1280 or 4096 bytes ) - // | IN FIFO 0 | - // --------------- (320 or 1024) - 16 - // | IN FIFO 1 | - // --------------- (320 or 1024) - 16 - x - // | . . . . | - // --------------- (320 or 1024) - 16 - x - y - ... - z - // | IN FIFO MAX | - // --------------- - // | FREE | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // In FIFO is allocated by following rules: - // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n". - uint8_t fifo_num = epnum; #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) - // Special Case for EP5, which is used by CDC but not actually called by the driver - // we can give it a fake FIFO - if (epnum == 5) { - fifo_num = epnum; - } else { - fifo_num = get_free_fifo(); - } - TU_ASSERT(fifo_num != 0); + _allocated_fifos = 1; #endif - // Check if free space is available - TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size/4); - - _allocated_fifo_words_tx += fifo_size; - TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %lu", fifo_size*4, _dwc2_controller[rhport].ep_fifo_size-_allocated_fifo_words_tx*4); + dfifo_flush_tx(dwc2, 0x10); // all tx fifo + dfifo_flush_rx(dwc2); - // DIEPTXF starts at FIFO #1. - // Both TXFD and TXSA are in unit of 32-bit words. - dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | (_dwc2_controller[rhport].ep_fifo_size/4 - _allocated_fifo_words_tx); - - dwc2->epin[epnum].diepctl |= (1 << DIEPCTL_USBAEP_Pos) | - (fifo_num << DIEPCTL_TXFNUM_Pos) | - (desc_edpt->bmAttributes.xfer << DIEPCTL_EPTYP_Pos) | - (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DIEPCTL_SD0PID_SEVNFRM : 0) | - (xfer->max_size << DIEPCTL_MPSIZ_Pos); - - dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum)); - } + dfifo_device_init(rhport); // re-init dfifo +} +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + TU_ASSERT(dfifo_alloc(rhport, ep_addr, largest_packet_size)); return true; } -// Close all non-control endpoints, cancel all pending transfers if any. -void dcd_edpt_close_all (uint8_t rhport) -{ - dwc2_regs_t * dwc2 = DWC2_REG(rhport); - uint8_t const ep_count = _dwc2_controller[rhport].ep_count; - - // Disable non-control interrupt - dwc2->daintmsk = (1 << DAINTMSK_OEPM_Pos) | (1 << DAINTMSK_IEPM_Pos); - - for(uint8_t n = 1; n < ep_count; n++) - { - // disable OUT endpoint - dwc2->epout[n].doepctl = 0; - xfer_status[n][TUSB_DIR_OUT].max_size = 0; - - // disable IN endpoint - dwc2->epin[n].diepctl = 0; - xfer_status[n][TUSB_DIR_IN].max_size = 0; - } - - // reset allocated fifo IN - _allocated_fifo_words_tx = 16; -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) - _allocated_fifos = 1; -#endif +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { + // Disable EP to clear potential incomplete transfers + edpt_disable(rhport, p_endpoint_desc->bEndpointAddress, false); + edpt_activate(rhport, p_endpoint_desc); + return true; } -bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) -{ +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); - xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); - xfer->buffer = buffer; - xfer->ff = NULL; - xfer->total_len = total_bytes; + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); + xfer->buffer = buffer; + xfer->ff = NULL; + xfer->total_len = total_bytes; // EP0 can only handle one packet - if(epnum == 0) - { - ep0_pending[dir] = total_bytes; - - // Schedule the first transaction for EP0 transfer - edpt_schedule_packets(rhport, epnum, dir, 1, ep0_pending[dir]); + if (epnum == 0) { + _dcd_data.ep0_pending[dir] = total_bytes; } - else - { - uint16_t num_packets = (total_bytes / xfer->max_size); - uint16_t const short_packet_size = total_bytes % xfer->max_size; - - // Zero-size packet is special case. - if ( (short_packet_size > 0) || (total_bytes == 0) ) num_packets++; - // Schedule packets to be sent within interrupt - edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); - } + // Schedule packets to be sent within interrupt + edpt_schedule_packets(rhport, epnum, dir); return true; } @@ -800,494 +614,421 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t // bytes should be written and second to keep the return value free to give back a boolean // success message. If total_bytes is too big, the FIFO will copy only what is available // into the USB buffer! -bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) -{ +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes) { // USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1 TU_ASSERT(ff->item_size == 1); uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); - xfer->buffer = NULL; - xfer->ff = ff; - xfer->total_len = total_bytes; + uint8_t const dir = tu_edpt_dir(ep_addr); - uint16_t num_packets = (total_bytes / xfer->max_size); - uint16_t const short_packet_size = total_bytes % xfer->max_size; - - // Zero-size packet is special case. - if ( short_packet_size > 0 || (total_bytes == 0) ) num_packets++; + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); + xfer->buffer = NULL; + xfer->ff = ff; + xfer->total_len = total_bytes; // Schedule packets to be sent within interrupt - edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); + // TODO xfer fifo may only available for slave mode + edpt_schedule_packets(rhport, epnum, dir); return true; } -static void dcd_edpt_disable (uint8_t rhport, uint8_t ep_addr, bool stall) -{ - (void) rhport; - - dwc2_regs_t *dwc2 = DWC2_REG(rhport); +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + edpt_disable(rhport, ep_addr, true); + if((tu_edpt_number(ep_addr) == 0) && dma_device_enabled(dwc2)) { + dma_setup_prepare(rhport); + } +} +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - if ( dir == TUSB_DIR_IN ) - { - dwc2_epin_t* epin = dwc2->epin; - - // Only disable currently enabled non-control endpoint - if ( (epnum == 0) || !(epin[epnum].diepctl & DIEPCTL_EPENA) ) - { - epin[epnum].diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0); - } - else - { - // Stop transmitting packets and NAK IN xfers. - epin[epnum].diepctl |= DIEPCTL_SNAK; - while ( (epin[epnum].diepint & DIEPINT_INEPNE) == 0 ) {} + uint8_t const dir = tu_edpt_dir(ep_addr); + dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum]; - // Disable the endpoint. - epin[epnum].diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0); - while ( (epin[epnum].diepint & DIEPINT_EPDISD_Msk) == 0 ) {} + // Clear stall and reset data toggle + dep->ctl &= ~EPCTL_STALL;; + dep->ctl |= EPCTL_SD0PID_SEVNFRM; +} - epin[epnum].diepint = DIEPINT_EPDISD; - } +//-------------------------------------------------------------------- +// Interrupt Handler +//-------------------------------------------------------------------- - // Flush the FIFO, and wait until we have confirmed it cleared. - dwc2->grstctl = ((epnum << GRSTCTL_TXFNUM_Pos) | GRSTCTL_TXFFLSH); - while ( (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) != 0 ) {} - } - else - { - dwc2_epout_t* epout = dwc2->epout; +// 7.4.1 Initialization on USB Reset +static void handle_bus_reset(uint8_t rhport) { + dwc2_regs_t *dwc2 = DWC2_REG(rhport); + const uint8_t ep_count = dwc2_ep_count(dwc2); - // Only disable currently enabled non-control endpoint - if ( (epnum == 0) || !(epout[epnum].doepctl & DOEPCTL_EPENA) ) - { - epout[epnum].doepctl |= stall ? DOEPCTL_STALL : 0; - } - else - { - // Asserting GONAK is required to STALL an OUT endpoint. - // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt - // anyway, and it can't be cleared by user code. If this while loop never - // finishes, we have bigger problems than just the stack. - dwc2->dctl |= DCTL_SGONAK; - while ( (dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0 ) {} + tu_memclr(xfer_status, sizeof(xfer_status)); - // Ditto here- disable the endpoint. - epout[epnum].doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0); - while ( (epout[epnum].doepint & DOEPINT_EPDISD_Msk) == 0 ) {} + _dcd_data.sof_en = false; + _dcd_data.allocated_epin_count = 0; - epout[epnum].doepint = DOEPINT_EPDISD; + // 1. NAK for all OUT endpoints + for (uint8_t n = 0; n < ep_count; n++) { + dwc2->epout[n].doepctl |= DOEPCTL_SNAK; + } - // Allow other OUT endpoints to keep receiving. - dwc2->dctl |= DCTL_CGONAK; + // Disable all IN endpoints + for (uint8_t n = 0; n < ep_count; n++) { + if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) { + dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS; } } -} -/** - * Close an endpoint. - */ -void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) -{ - dwc2_regs_t * dwc2 = DWC2_REG(rhport); + // 2. Set up interrupt mask for EP0 + dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos); + dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM; + dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM; - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + // 4. Set up DFIFO + dfifo_flush_tx(dwc2, 0x10); // all tx fifo + dfifo_flush_rx(dwc2); + dfifo_device_init(rhport); - dcd_edpt_disable(rhport, ep_addr, false); + // 5. Reset device address + dwc2_dcfg_t dcfg = {.value = dwc2->dcfg}; + dcfg.address = 0; + dwc2->dcfg = dcfg.value; - // Update max_size - xfer_status[epnum][dir].max_size = 0; // max_size = 0 marks a disabled EP - required for changing FIFO allocation + // Fixed both control EP0 size to 64 bytes + dwc2->epin[0].ctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos); + dwc2->epout[0].ctl &= ~(0x03 << DOEPCTL_MPSIZ_Pos); - if (dir == TUSB_DIR_IN) - { - uint16_t const fifo_size = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXFD_Msk) >> DIEPTXF_INEPTXFD_Pos; - uint16_t const fifo_start = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXSA_Msk) >> DIEPTXF_INEPTXSA_Pos; + xfer_status[0][TUSB_DIR_OUT].max_size = 64; + xfer_status[0][TUSB_DIR_IN].max_size = 64; - // For now only the last opened endpoint can be closed without fuss. - TU_ASSERT(fifo_start == _dwc2_controller[rhport].ep_fifo_size/4 - _allocated_fifo_words_tx,); - _allocated_fifo_words_tx -= fifo_size; - } - else - { - _out_ep_closed = true; // Set flag such that RX FIFO gets reduced in size once RX FIFO is empty + if(dma_device_enabled(dwc2)) { + dma_setup_prepare(rhport); + } else { + dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); } -} -void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) -{ - dcd_edpt_disable(rhport, ep_addr, true); + dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT; } -void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - - dwc2_regs_t * dwc2 = DWC2_REG(rhport); +static void handle_enum_done(uint8_t rhport) { + dwc2_regs_t *dwc2 = DWC2_REG(rhport); + const dwc2_dsts_t dsts = {.value = dwc2->dsts}; + tusb_speed_t speed; + switch (dsts.enum_speed) { + case DCFG_SPEED_HIGH: + speed = TUSB_SPEED_HIGH; + break; - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + case DCFG_SPEED_LOW: + speed = TUSB_SPEED_LOW; + break; - // Clear stall and reset data toggle - if ( dir == TUSB_DIR_IN ) - { - dwc2->epin[epnum].diepctl &= ~DIEPCTL_STALL; - dwc2->epin[epnum].diepctl |= DIEPCTL_SD0PID_SEVNFRM; - } - else - { - dwc2->epout[epnum].doepctl &= ~DOEPCTL_STALL; - dwc2->epout[epnum].doepctl |= DOEPCTL_SD0PID_SEVNFRM; + case DCFG_SPEED_FULL_30_60MHZ: + case DCFG_SPEED_FULL_48MHZ: + default: + speed = TUSB_SPEED_FULL; + break; } -} - -/*------------------------------------------------------------------*/ - -// Read a single data packet from receive FIFO -static void read_fifo_packet(uint8_t rhport, uint8_t * dst, uint16_t len) -{ - (void) rhport; - dwc2_regs_t * dwc2 = DWC2_REG(rhport); - volatile const uint32_t * rx_fifo = dwc2->fifo[0]; - - // Reading full available 32 bit words from fifo - uint16_t full_words = len >> 2; - while(full_words--) - { - tu_unaligned_write32(dst, *rx_fifo); - dst += 4; - } + // TODO must update GUSBCFG_TRDT according to link speed + dcd_event_bus_reset(rhport, speed, true); +} - // Read the remaining 1-3 bytes from fifo - uint8_t const bytes_rem = len & 0x03; - if ( bytes_rem != 0 ) - { - uint32_t const tmp = *rx_fifo; - dst[0] = tu_u32_byte0(tmp); - if ( bytes_rem > 1 ) dst[1] = tu_u32_byte1(tmp); - if ( bytes_rem > 2 ) dst[2] = tu_u32_byte2(tmp); +#if 0 +TU_ATTR_ALWAYS_INLINE static inline void print_doepint(uint32_t doepint) { + const char* str[] = { + "XFRC", "DIS", "AHBERR", "SETUP_DONE", + "ORXED", "STATUS_RX", "SETUP_B2B", "RSV7", + "OPERR", "BNA", "RSV10", "ISODROP", + "BBLERR", "NAK", "NYET", "SETUP_RX" + }; + + for(uint32_t i=0; ififo[fifo_num]; +#if CFG_TUD_DWC2_SLAVE_ENABLE +// Process shared receive FIFO, this interrupt is only used in Slave mode +static void handle_rxflvl_irq(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + const volatile uint32_t* rx_fifo = dwc2->fifo[0]; - // Pushing full available 32 bit words to fifo - uint16_t full_words = len >> 2; - while(full_words--) - { - *tx_fifo = tu_unaligned_read32(src); - src += 4; - } + // Pop control word off FIFO + const dwc2_grxstsp_t grxstsp = {.value = dwc2->grxstsp}; + const uint8_t epnum = grxstsp.ep_ch_num; - // Write the remaining 1-3 bytes into fifo - uint8_t const bytes_rem = len & 0x03; - if ( bytes_rem ) - { - uint32_t tmp_word = src[0]; - if ( bytes_rem > 1 ) tmp_word |= (src[1] << 8); - if ( bytes_rem > 2 ) tmp_word |= (src[2] << 16); + dwc2_dep_t* epout = &dwc2->epout[epnum]; - *tx_fifo = tmp_word; - } -} - -static void handle_rxflvl_irq(uint8_t rhport) -{ - dwc2_regs_t * dwc2 = DWC2_REG(rhport); - volatile uint32_t const * rx_fifo = dwc2->fifo[0]; + switch (grxstsp.packet_status) { + case GRXSTS_PKTSTS_GLOBAL_OUT_NAK: + // Global OUT NAK: do nothing + break; - // Pop control word off FIFO - uint32_t const ctl_word = dwc2->grxstsp; - uint8_t const pktsts = (ctl_word & GRXSTSP_PKTSTS_Msk ) >> GRXSTSP_PKTSTS_Pos; - uint8_t const epnum = (ctl_word & GRXSTSP_EPNUM_Msk ) >> GRXSTSP_EPNUM_Pos; - uint16_t const bcnt = (ctl_word & GRXSTSP_BCNT_Msk ) >> GRXSTSP_BCNT_Pos; - - dwc2_epout_t* epout = &dwc2->epout[epnum]; - -//#if CFG_TUSB_DEBUG >= DWC2_DEBUG -// const char * pktsts_str[] = -// { -// "ASSERT", "Global NAK (ISR)", "Out Data Received", "Out Transfer Complete (ISR)", -// "Setup Complete (ISR)", "ASSERT", "Setup Data Received" -// }; -// TU_LOG_LOCATION(); -// TU_LOG(DWC2_DEBUG, " EP %02X, Byte Count %u, %s\r\n", epnum, bcnt, pktsts_str[pktsts]); -// TU_LOG(DWC2_DEBUG, " daint = %08lX, doepint = %04X\r\n", (unsigned long) dwc2->daint, (unsigned int) epout->doepint); -//#endif - - switch ( pktsts ) - { - // Global OUT NAK: do nothing - case GRXSTS_PKTSTS_GLOBALOUTNAK: break; - - case GRXSTS_PKTSTS_SETUPRX: + case GRXSTS_PKTSTS_SETUP_RX: { // Setup packet received + uint32_t* setup = (uint32_t*)(uintptr_t) _dcd_usbbuf.setup_packet; + // We can receive up to three setup packets in succession, but only the last one is valid. + setup[0] = (*rx_fifo); + setup[1] = (*rx_fifo); + break; + } - // We can receive up to three setup packets in succession, but - // only the last one is valid. - _setup_packet[0] = (*rx_fifo); - _setup_packet[1] = (*rx_fifo); - break; - - case GRXSTS_PKTSTS_SETUPDONE: - // Setup packet done (Interrupt) + case GRXSTS_PKTSTS_SETUP_DONE: + // Setup packet done: + // After popping this out, dwc2 asserts a DOEPINT_SETUP interrupt which is handled by handle_epout_irq() epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); - break; + break; - case GRXSTS_PKTSTS_OUTRX: - { + case GRXSTS_PKTSTS_RX_DATA: { // Out packet received - xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - - // Read packet off RxFIFO - if ( xfer->ff ) - { - // Ring buffer - tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, bcnt); - } - else - { - // Linear buffer - read_fifo_packet(rhport, xfer->buffer, bcnt); - - // Increment pointer to xfer data - xfer->buffer += bcnt; - } + const uint16_t byte_count = grxstsp.byte_count; + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); + + if (byte_count) { + // Read packet off RxFIFO + if (xfer->ff) { + tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, byte_count); + } else { + dfifo_read_packet(dwc2, xfer->buffer, byte_count); + xfer->buffer += byte_count; + } - // Truncate transfer length in case of short packet - if ( bcnt < xfer->max_size ) - { - xfer->total_len -= (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos; - if ( epnum == 0 ) - { - xfer->total_len -= ep0_pending[TUSB_DIR_OUT]; - ep0_pending[TUSB_DIR_OUT] = 0; + // short packet, minus remaining bytes (xfer_size) + if (byte_count < xfer->max_size) { + const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz}; + xfer->total_len -= tsiz.xfer_size; + if (epnum == 0) { + xfer->total_len -= _dcd_data.ep0_pending[TUSB_DIR_OUT]; + _dcd_data.ep0_pending[TUSB_DIR_OUT] = 0; + } } } + break; } - break; - - // Out packet done (Interrupt) - case GRXSTS_PKTSTS_OUTDONE: - // Occurred on STM32L47 with dwc2 version 3.10a but not found on other version like 2.80a or 3.30a - // May (or not) be 3.10a specific feature/bug or depending on MCU configuration - // XFRC complete is additionally generated when - // - setup packet is received - // - complete the data stage of control write is complete - if ((epnum == 0) && (bcnt == 0) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) - { - uint32_t doepint = epout->doepint; - - if (doepint & (DOEPINT_STPKTRX | DOEPINT_OTEPSPR)) - { - // skip this "no-data" transfer complete event - // Note: STPKTRX will be clear later by setup received handler - uint32_t clear_flags = DOEPINT_XFRC; - if (doepint & DOEPINT_OTEPSPR) clear_flags |= DOEPINT_OTEPSPR; + case GRXSTS_PKTSTS_RX_COMPLETE: + // Out packet done + // After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on + // the specified OUT endpoint which will be handled by handle_epout_irq() + break; - epout->doepint = clear_flags; + default: break; + } +} - // TU_LOG(DWC2_DEBUG, " FIX extra transfer complete on setup/data compete\r\n"); - } - } - break; +static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) { + if (doepint_bm.setup_phase_done) { + dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true); + return; + } - default: // Invalid - TU_BREAKPOINT(); - break; + // Normal OUT transfer complete + if (doepint_bm.xfer_complete) { + // only handle data skip if it is setup or status related + // Note: even though (xfer_complete + status_phase_rx) is for buffered DMA only, for STM32L47x (dwc2 v3.00a) they + // can is set when GRXSTS_PKTSTS_SETUP_RX is popped therefore they can bet set before/together with setup_phase_done + if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) { + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); + + if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_OUT]) { + // EP0 can only handle one packet, Schedule another packet to be received. + edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT); + } else { + dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true); + } + } } } -static void handle_epout_irq (uint8_t rhport) -{ - dwc2_regs_t * dwc2 = DWC2_REG(rhport); - uint8_t const ep_count = _dwc2_controller[rhport].ep_count; +static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepint_bm) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2_dep_t* epin = &dwc2->epin[epnum]; + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN); - // DAINT for a given EP clears when DOEPINTx is cleared. - // OEPINT will be cleared when DAINT's out bits are cleared. - for ( uint8_t n = 0; n < ep_count; n++ ) - { - if ( dwc2->daint & TU_BIT(DAINT_OEPINT_Pos + n) ) - { - dwc2_epout_t* epout = &dwc2->epout[n]; - - uint32_t const doepint = epout->doepint; - - // SETUP packet Setup Phase done. - if ( doepint & DOEPINT_STUP ) - { - uint32_t clear_flag = DOEPINT_STUP; - - // STPKTRX is only available for version from 3_00a - if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) - { - clear_flag |= DOEPINT_STPKTRX; - } + if (diepint_bm.xfer_complete) { + if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_IN]) { + // EP0 can only handle one packet. Schedule another packet to be transmitted. + edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN); + } else { + dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); + } + } - epout->doepint = clear_flag; - dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true); + // TX FIFO empty bit is read-only. It will only be cleared by hardware when written bytes is more than + // - 64 bytes or + // - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL) + if (diepint_bm.txfifo_empty && (dwc2->diepempmsk & (1 << epnum))) { + dwc2_ep_tsize_t tsiz = {.value = epin->tsiz}; + const uint16_t remain_packets = tsiz.packet_count; + + // Process every single packet (only whole packets can be written to fifo) + for (uint16_t i = 0; i < remain_packets; i++) { + tsiz.value = epin->tsiz; + const uint16_t remain_bytes = (uint16_t) tsiz.xfer_size; + const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size); + + // Check if dtxfsts has enough space available + if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) { + break; } - // OUT XFER complete - if ( epout->doepint & DOEPINT_XFRC ) - { - epout->doepint = DOEPINT_XFRC; + // Push packet to Tx-FIFO + if (xfer->ff) { + volatile uint32_t* tx_fifo = dwc2->fifo[epnum]; + tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes); + } else { + dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes); + xfer->buffer += xact_bytes; + } + } - xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); + // Turn off TXFE if all bytes are written. + tsiz.value = epin->tsiz; + if (tsiz.xfer_size == 0) { + dwc2->diepempmsk &= ~(1 << epnum); + } + } +} +#endif - // EP0 can only handle one packet - if ( (n == 0) && ep0_pending[TUSB_DIR_OUT] ) - { - // Schedule another packet to be received. - edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); - } - else - { - dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true); +#if CFG_TUD_DWC2_DMA_ENABLE +static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + if (doepint_bm.setup_phase_done) { + dma_setup_prepare(rhport); + dcd_dcache_invalidate(_dcd_usbbuf.setup_packet, 8); + dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true); + return; + } + + // OUT XFER complete + if (doepint_bm.xfer_complete) { + // only handle data skip if it is setup or status related + // Normal OUT transfer complete + if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) { + if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_OUT]) { + // EP0 can only handle one packet Schedule another packet to be received. + edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT); + } else { + dwc2_dep_t* epout = &dwc2->epout[epnum]; + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); + + // determine actual received bytes + const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz}; + const uint16_t remain = tsiz.xfer_size; + xfer->total_len -= remain; + + // this is ZLP, so prepare EP0 for next setup + // TODO use status phase rx + if(epnum == 0 && xfer->total_len == 0) { + dma_setup_prepare(rhport); } + + dcd_dcache_invalidate(xfer->buffer, xfer->total_len); + dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true); } } } } -static void handle_epin_irq (uint8_t rhport) -{ - dwc2_regs_t * dwc2 = DWC2_REG(rhport); - uint8_t const ep_count = _dwc2_controller[rhport].ep_count; - dwc2_epin_t* epin = dwc2->epin; - - // DAINT for a given EP clears when DIEPINTx is cleared. - // IEPINT will be cleared when DAINT's out bits are cleared. - for ( uint8_t n = 0; n < ep_count; n++ ) - { - if ( dwc2->daint & TU_BIT(DAINT_IEPINT_Pos + n) ) - { - // IN XFER complete (entire xfer). - xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_IN); - - if ( epin[n].diepint & DIEPINT_XFRC ) - { - epin[n].diepint = DIEPINT_XFRC; - - // EP0 can only handle one packet - if ( (n == 0) && ep0_pending[TUSB_DIR_IN] ) - { - // Schedule another packet to be transmitted. - edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]); - } - else - { - dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); - } - } +static void handle_epin_dma(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepint_bm) { + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN); - // XFER FIFO empty - if ( (epin[n].diepint & DIEPINT_TXFE) && (dwc2->diepempmsk & (1 << n)) ) - { - // diepint's TXFE bit is read-only, software cannot clear it. - // It will only be cleared by hardware when written bytes is more than - // - 64 bytes or - // - Half of TX FIFO size (configured by DIEPTXF) - - uint16_t remaining_packets = (epin[n].dieptsiz & DIEPTSIZ_PKTCNT_Msk) >> DIEPTSIZ_PKTCNT_Pos; - - // Process every single packet (only whole packets can be written to fifo) - for ( uint16_t i = 0; i < remaining_packets; i++ ) - { - uint16_t const remaining_bytes = (epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos; - - // Packet can not be larger than ep max size - uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size); - - // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current - // EP has to be checked if the buffer can take another WHOLE packet - if ( packet_size > ((epin[n].dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2) ) break; - - // Push packet to Tx-FIFO - if ( xfer->ff ) - { - volatile uint32_t *tx_fifo = dwc2->fifo[n]; - tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*) (uintptr_t) tx_fifo, packet_size); - } - else - { - write_fifo_packet(rhport, n, xfer->buffer, packet_size); + if (diepint_bm.xfer_complete) { + if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_IN]) { + // EP0 can only handle one packet. Schedule another packet to be transmitted. + edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN); + } else { + if(epnum == 0) { + dma_setup_prepare(rhport); + } + dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); + } + } +} +#endif - // Increment pointer to xfer data - xfer->buffer += packet_size; - } +static void handle_ep_irq(uint8_t rhport, uint8_t dir) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + const bool is_dma = dma_device_enabled(dwc2); + const uint8_t ep_count = dwc2_ep_count(dwc2); + const uint8_t daint_offset = (dir == TUSB_DIR_IN) ? DAINT_IEPINT_Pos : DAINT_OEPINT_Pos; + dwc2_dep_t* ep_base = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][0]; + + // DAINT for a given EP clears when DEPINTx is cleared. + // EPINT will be cleared when DAINT bits are cleared. + for (uint8_t epnum = 0; epnum < ep_count; epnum++) { + if (dwc2->daint & TU_BIT(daint_offset + epnum)) { + dwc2_dep_t* epout = &ep_base[epnum]; + union { + uint32_t value; + dwc2_diepint_t diepint_bm; + dwc2_doepint_t doepint_bm; + } intr; + intr.value = epout->intr; + + epout->intr = intr.value; // Clear interrupt + + if (is_dma) { + #if CFG_TUD_DWC2_DMA_ENABLE + if (dir == TUSB_DIR_IN) { + handle_epin_dma(rhport, epnum, intr.diepint_bm); + } else { + handle_epout_dma(rhport, epnum, intr.doepint_bm); } - - // Turn off TXFE if all bytes are written. - if ( ((epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos) == 0 ) - { - dwc2->diepempmsk &= ~(1 << n); + #endif + } else { + #if CFG_TUD_DWC2_SLAVE_ENABLE + if (dir == TUSB_DIR_IN) { + handle_epin_slave(rhport, epnum, intr.diepint_bm); + } else { + handle_epout_slave(rhport, epnum, intr.doepint_bm); } + #endif } } } } -void dcd_int_handler(uint8_t rhport) -{ - dwc2_regs_t *dwc2 = DWC2_REG(rhport); +/* Interrupt Hierarchy + DIEPINT DIEPINT + \ / + \ / + DAINT + / \ + / \ + GINTSTS: OEPInt IEPInt | USBReset | EnumDone | USBSusp | WkUpInt | OTGInt | SOF | RXFLVL + + Note: when OTG_MULTI_PROC_INTRPT = 1, Device Each endpoint interrupt deachint/deachmsk/diepeachmsk/doepeachmsk + are combined to generate dedicated interrupt line for each endpoint. + */ +void dcd_int_handler(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint32_t const int_mask = dwc2->gintmsk; - uint32_t const int_status = dwc2->gintsts & int_mask; + const uint32_t gintmask = dwc2->gintmsk; + const uint32_t gintsts = dwc2->gintsts & gintmask; - if(int_status & GINTSTS_USBRST) - { + if (gintsts & GINTSTS_USBRST) { // USBRST is start of reset. dwc2->gintsts = GINTSTS_USBRST; #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) _allocated_fifos = 1; #endif - bus_reset(rhport); + handle_bus_reset(rhport); } - if(int_status & GINTSTS_ENUMDNE) - { + if (gintsts & GINTSTS_ENUMDNE) { // ENUMDNE is the end of reset where speed of the link is detected - dwc2->gintsts = GINTSTS_ENUMDNE; - - tusb_speed_t speed; - switch ((dwc2->dsts & DSTS_ENUMSPD_Msk) >> DSTS_ENUMSPD_Pos) - { - case DSTS_ENUMSPD_HS: - speed = TUSB_SPEED_HIGH; - break; - - case DSTS_ENUMSPD_LS: - speed = TUSB_SPEED_LOW; - break; - - case DSTS_ENUMSPD_FS_HSPHY: - case DSTS_ENUMSPD_FS: - default: - speed = TUSB_SPEED_FULL; - break; - } - - dcd_event_bus_reset(rhport, speed, true); + handle_enum_done(rhport); } - if(int_status & GINTSTS_USBSUSP) - { + if (gintsts & GINTSTS_USBSUSP) { dwc2->gintsts = GINTSTS_USBSUSP; //dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); @@ -1296,8 +1037,7 @@ void dcd_int_handler(uint8_t rhport) #endif } - if(int_status & GINTSTS_WKUINT) - { + if (gintsts & GINTSTS_WKUINT) { dwc2->gintsts = GINTSTS_WKUINT; dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); } @@ -1305,13 +1045,11 @@ void dcd_int_handler(uint8_t rhport) // TODO check GINTSTS_DISCINT for disconnect detection // if(int_status & GINTSTS_DISCINT) - if(int_status & GINTSTS_OTGINT) - { + if (gintsts & GINTSTS_OTGINT) { // OTG INT bit is read-only - uint32_t const otg_int = dwc2->gotgint; + const uint32_t otg_int = dwc2->gotgint; - if (otg_int & GOTGINT_SEDET) - { + if (otg_int & GOTGINT_SEDET) { dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) _allocated_fifos = 1; @@ -1321,69 +1059,52 @@ void dcd_int_handler(uint8_t rhport) dwc2->gotgint = otg_int; } - if(int_status & GINTSTS_SOF) - { - dwc2->gotgint = GINTSTS_SOF; + if(gintsts & GINTSTS_SOF) { + dwc2->gintsts = GINTSTS_SOF; + const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos; - if (_sof_en) - { - uint32_t frame = (dwc2->dsts & (DSTS_FNSOF)) >> 8; - dcd_event_sof(rhport, frame, true); - } - else - { - // Disable SOF interrupt if SOF was not explicitly enabled. SOF was used for remote wakeup detection + // Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection + if (!_dcd_data.sof_en) { dwc2->gintmsk &= ~GINTMSK_SOFM; } - dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); + dcd_event_sof(rhport, frame, true); } +#if CFG_TUD_DWC2_SLAVE_ENABLE // RxFIFO non-empty interrupt handling. - if(int_status & GINTSTS_RXFLVL) - { + if (gintsts & GINTSTS_RXFLVL) { // RXFLVL bit is read-only + dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading - // Mask out RXFLVL while reading data from FIFO - dwc2->gintmsk &= ~GINTMSK_RXFLVLM; - - // Loop until all available packets were handled - do - { - handle_rxflvl_irq(rhport); - } while(dwc2->gotgint & GINTSTS_RXFLVL); - - // Manage RX FIFO size - if (_out_ep_closed) - { - update_grxfsiz(rhport); - - // Disable flag - _out_ep_closed = false; - } + do { + handle_rxflvl_irq(rhport); // read all packets + } while(dwc2->gintsts & GINTSTS_RXFLVL); dwc2->gintmsk |= GINTMSK_RXFLVLM; } +#endif // OUT endpoint interrupt handling. - if(int_status & GINTSTS_OEPINT) - { + if (gintsts & GINTSTS_OEPINT) { // OEPINT is read-only, clear using DOEPINTn - handle_epout_irq(rhport); + handle_ep_irq(rhport, TUSB_DIR_OUT); } // IN endpoint interrupt handling. - if(int_status & GINTSTS_IEPINT) - { + if (gintsts & GINTSTS_IEPINT) { // IEPINT bit read-only, clear using DIEPINTn - handle_epin_irq(rhport); + handle_ep_irq(rhport, TUSB_DIR_IN); } +} - // // Check for Incomplete isochronous IN transfer - // if(int_status & GINTSTS_IISOIXFR) { - // printf(" IISOIXFR!\r\n"); - //// TU_LOG(DWC2_DEBUG, " IISOIXFR!\r\n"); - // } +#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) | (((uint8_t) test_selector) << DCTL_TCTL_Pos); } +#endif #endif diff --git a/components/arduino_tinyusb/src/dcd_esp32sx.c b/components/arduino_tinyusb/src/dcd_esp32sx.c deleted file mode 100755 index cfccf95ae..000000000 --- a/components/arduino_tinyusb/src/dcd_esp32sx.c +++ /dev/null @@ -1,954 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * Additions Copyright (c) 2020, Espressif Systems (Shanghai) Co. Ltd. - * - * 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 "tusb_option.h" - -#if (((CFG_TUSB_MCU == OPT_MCU_ESP32S2) || (CFG_TUSB_MCU == OPT_MCU_ESP32S3)) && CFG_TUD_ENABLED) - -// Espressif -#include "freertos/xtensa_api.h" -#include "esp_intr_alloc.h" -#include "esp_log.h" -#include "soc/dport_reg.h" -#include "soc/gpio_sig_map.h" -#include "soc/usb_periph.h" -#include "soc/periph_defs.h" // for interrupt source - -#include "device/dcd.h" - -// Max number of bi-directional endpoints including EP0 -// Note: ESP32S2 specs say there are only up to 5 IN active endpoints include EP0 -// We should probably prohibit enabling Endpoint IN > 4 (not done yet) -#define EP_MAX USB_OUT_EP_NUM - -// FIFO size in bytes -#define EP_FIFO_SIZE 1024 - -// Max number of IN EP FIFOs -#define EP_FIFO_NUM 5 - -typedef struct { - uint8_t *buffer; - // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API - uint16_t total_len; - uint16_t queued_len; - uint16_t max_size; - bool short_packet; - uint8_t interval; -} xfer_ctl_t; - -static const char *TAG = "TUSB:DCD"; -static intr_handle_t usb_ih; - - -static uint32_t _setup_packet[2]; - -#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir] -static xfer_ctl_t xfer_status[EP_MAX][2]; - -// Keep count of how many FIFOs are in use -static uint8_t _allocated_fifos = 1; //FIFO0 is always in use - -// Will either return an unused FIFO number, or 0 if all are used. -static uint8_t get_free_fifo(void) -{ - if (_allocated_fifos < EP_FIFO_NUM) return _allocated_fifos++; - return 0; -} - -// Setup the control endpoint 0. -static void bus_reset(void) -{ - for (int ep_num = 0; ep_num < USB_OUT_EP_NUM; ep_num++) { - USB0.out_ep_reg[ep_num].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK - } - - // clear device address - USB0.dcfg &= ~USB_DEVADDR_M; - - USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M; - USB0.doepmsk = USB_SETUPMSK_M | USB_XFERCOMPLMSK; - USB0.diepmsk = USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M /*| USB_INTKNTXFEMPMSK_M*/; - - // "USB Data FIFOs" section in reference manual - // Peripheral FIFO architecture - // - // --------------- 320 or 1024 ( 1280 or 4096 bytes ) - // | IN FIFO MAX | - // --------------- - // | ... | - // --------------- y + x + 16 + GRXFSIZ - // | IN FIFO 2 | - // --------------- x + 16 + GRXFSIZ - // | IN FIFO 1 | - // --------------- 16 + GRXFSIZ - // | IN FIFO 0 | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits): - // - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN - // - // - All EP OUT shared a unique OUT FIFO which uses - // * 10 locations in hardware for setup packets + setup control words (up to 3 setup packets). - // * 2 locations for OUT endpoint control words. - // * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes) - // * 1 location for global NAK (not required/used here). - // * It is recommended to allocate 2 times the largest packet size, therefore - // Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 52 - USB0.grstctl |= 0x10 << USB_TXFNUM_S; // fifo 0x10, - USB0.grstctl |= USB_TXFFLSH_M; // Flush fifo - USB0.grxfsiz = 52; - - // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) - USB0.gnptxfsiz = (16 << USB_NPTXFDEP_S) | (USB0.grxfsiz & 0x0000ffffUL); - - // Ready to receive SETUP packet - USB0.out_ep_reg[0].doeptsiz |= USB_SUPCNT0_M; - - USB0.gintmsk |= USB_IEPINTMSK_M | USB_OEPINTMSK_M; -} - -static void enum_done_processing(void) -{ - ESP_EARLY_LOGV(TAG, "dcd_int_handler - Speed enumeration done! Sending DCD_EVENT_BUS_RESET then"); - // On current silicon on the Full Speed core, speed is fixed to Full Speed. - // However, keep for debugging and in case Low Speed is ever supported. - uint32_t enum_spd = (USB0.dsts >> USB_ENUMSPD_S) & (USB_ENUMSPD_V); - - // Maximum packet size for EP 0 is set for both directions by writing DIEPCTL - if (enum_spd == 0x03) { // Full-Speed (PHY on 48 MHz) - USB0.in_ep_reg[0].diepctl &= ~USB_D_MPS0_V; // 64 bytes - USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall - xfer_status[0][TUSB_DIR_OUT].max_size = 64; - xfer_status[0][TUSB_DIR_IN].max_size = 64; - } else { - USB0.in_ep_reg[0].diepctl |= USB_D_MPS0_V; // 8 bytes - USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall - xfer_status[0][TUSB_DIR_OUT].max_size = 8; - xfer_status[0][TUSB_DIR_IN].max_size = 8; - } -} - - -/*------------------------------------------------------------------*/ -/* Controller API - *------------------------------------------------------------------*/ -void dcd_init(uint8_t rhport) -{ - ESP_LOGV(TAG, "DCD init - Start"); - - bool did_persist = (USB_WRAP.date.val & (1 << 31)) != 0; - - if (did_persist) { - //Clear persistence of USB peripheral through reset - USB_WRAP.date.val = 0; - } else { - // A. Disconnect - ESP_LOGV(TAG, "DCD init - Soft DISCONNECT and Setting up"); - USB0.dctl |= USB_SFTDISCON_M; // Soft disconnect - - // B. Programming DCFG - /* If USB host misbehaves during status portion of control xfer - (non zero-length packet), send STALL back and discard. Full speed. */ - USB0.dcfg |= USB_NZSTSOUTHSHK_M | // NonZero .... STALL - (3 << 0); // dev speed: fullspeed 1.1 on 48 mhz // TODO no value in usb_reg.h (IDF-1476) - } - - USB0.gahbcfg |= USB_NPTXFEMPLVL_M | USB_GLBLLNTRMSK_M; // Global interruptions ON - USB0.gusbcfg |= USB_FORCEDEVMODE_M; // force devmode - USB0.gotgctl &= ~(USB_BVALIDOVVAL_M | USB_BVALIDOVEN_M | USB_VBVALIDOVVAL_M); //no overrides - - // C. Setting SNAKs, then connect - for (int n = 0; n < USB_OUT_EP_NUM; n++) { - USB0.out_ep_reg[n].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK - } - - if (!did_persist) { - // D. Interruption masking - USB0.gintmsk = 0; //mask all - USB0.gotgint = ~0U; //clear OTG ints - USB0.gintsts = ~0U; //clear pending ints - } - - USB0.gintmsk = USB_OTGINTMSK_M | - USB_MODEMISMSK_M | - USB_RXFLVIMSK_M | - USB_ERLYSUSPMSK_M | - USB_USBSUSPMSK_M | - USB_USBRSTMSK_M | - USB_ENUMDONEMSK_M | - USB_RESETDETMSK_M | - USB_DISCONNINTMSK_M; // host most only - if (did_persist) { - USB0.grstctl &= ~USB_TXFNUM_M; - USB0.grstctl |= 0x10 << USB_TXFNUM_S; - USB0.grstctl |= USB_TXFFLSH; - USB0.grxfsiz = 52; - - for (int n = 0; n < USB_IN_EP_NUM; n++) { - USB0.in_ep_reg[n].diepint = USB_D_XFERCOMPL0_M | USB_D_TXFEMP0_M; - USB0.in_ep_reg[n].diepctl &= ~USB_D_STALL0_M; // clear Stall - USB0.in_ep_reg[n].diepctl |= USB_D_CNAK0 | USB_D_EPENA0; // clear NAK - } - USB0.dtknqr4_fifoemptymsk &= ~(0x7F); - - USB0.gnptxfsiz = (16 << USB_NPTXFDEP_S) | (USB0.grxfsiz & 0x0000ffffUL); - - USB0.daintmsk |= USB_OUTEPMSK0_M | USB_INEPMSK0_M; - USB0.doepmsk |= USB_SETUP0 | USB_XFERCOMPLMSK; - USB0.diepmsk |= USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M;//USB_INEPNAKEFFMSK - - USB0.gintmsk |= USB_IEPINTMSK_M | USB_OEPINTMSK_M; - USB0.gotgint = ~0; //clear OTG ints - USB0.gintsts = ~0; //clear pending ints - enum_done_processing(); - dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); - tusb_control_request_t request = { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT }, - .bRequest = TUSB_REQ_SET_CONFIGURATION, - .wValue = 1, - .wIndex = 0, - .wLength = 0 - }; - dcd_event_setup_received(0, (uint8_t *)&request, true); - } else { - dcd_connect(rhport); - } -} - -void dcd_set_address(uint8_t rhport, uint8_t dev_addr) -{ - (void)rhport; - ESP_LOGV(TAG, "DCD init - Set address : %u", dev_addr); - USB0.dcfg |= ((dev_addr & USB_DEVADDR_V) << USB_DEVADDR_S); - // Response with status after changing device address - dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); -} - -void dcd_remote_wakeup(uint8_t rhport) -{ - (void)rhport; - - // set remote wakeup - USB0.dctl |= USB_RMTWKUPSIG_M; - - // enable SOF to detect bus resume - USB0.gintsts = USB_SOF_M; - USB0.gintmsk |= USB_SOFMSK_M; - - // Per specs: remote wakeup signal bit must be clear within 1-15ms - vTaskDelay(pdMS_TO_TICKS(1)); - - USB0.dctl &= ~USB_RMTWKUPSIG_M; -} - -// connect by enabling internal pull-up resistor on D+/D- -void dcd_connect(uint8_t rhport) -{ - (void) rhport; - USB0.dctl &= ~USB_SFTDISCON_M; -} - -// disconnect by disabling internal pull-up resistor on D+/D- -void dcd_disconnect(uint8_t rhport) -{ - (void) rhport; - USB0.dctl |= USB_SFTDISCON_M; -} - -void dcd_sof_enable(uint8_t rhport, bool en) -{ - (void) rhport; - (void) en; - - // TODO implement later -} - -/*------------------------------------------------------------------*/ -/* DCD Endpoint port - *------------------------------------------------------------------*/ - -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt) -{ - ESP_LOGV(TAG, "DCD endpoint opened"); - (void)rhport; - - usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); - usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); - - uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); - - TU_ASSERT(epnum < EP_MAX); - - xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir); - xfer->max_size = tu_edpt_packet_size(desc_edpt); - xfer->interval = desc_edpt->bInterval; - - if (dir == TUSB_DIR_OUT) { - out_ep[epnum].doepctl &= ~(USB_D_EPTYPE0_M | USB_D_MPS0_M); - out_ep[epnum].doepctl |= USB_USBACTEP1_M | - desc_edpt->bmAttributes.xfer << USB_EPTYPE1_S | - (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_DO_SETD0PID1_M : 0) | - xfer->max_size << USB_MPS1_S; - USB0.daintmsk |= (1 << (16 + epnum)); - } else { - // "USB Data FIFOs" section in reference manual - // Peripheral FIFO architecture - // - // --------------- 320 or 1024 ( 1280 or 4096 bytes ) - // | IN FIFO MAX | - // --------------- - // | ... | - // --------------- y + x + 16 + GRXFSIZ - // | IN FIFO 2 | - // --------------- x + 16 + GRXFSIZ - // | IN FIFO 1 | - // --------------- 16 + GRXFSIZ - // | IN FIFO 0 | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // Since OUT FIFO = GRXFSIZ, FIFO 0 = 16, for simplicity, we equally allocated for the rest of endpoints - // - Size : (FIFO_SIZE/4 - GRXFSIZ - 16) / (EP_MAX-1) - // - Offset: GRXFSIZ + 16 + Size*(epnum-1) - // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n". - - uint8_t fifo_num = 0; - // Special Case for EP5, which is used by CDC but not actually called by the driver - // we can give it a fake FIFO - if (epnum == 5) { - fifo_num = EP_FIFO_NUM; - } else { - fifo_num = get_free_fifo(); - } - TU_ASSERT(fifo_num != 0); - - in_ep[epnum].diepctl &= ~(USB_D_TXFNUM1_M | USB_D_EPTYPE1_M | USB_DI_SETD0PID1 | USB_D_MPS1_M); - in_ep[epnum].diepctl |= USB_D_USBACTEP1_M | - fifo_num << USB_D_TXFNUM1_S | - desc_edpt->bmAttributes.xfer << USB_D_EPTYPE1_S | - (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? (1 << USB_DI_SETD0PID1_S) : 0) | - xfer->max_size << 0; - - USB0.daintmsk |= (1 << (0 + epnum)); - - // Both TXFD and TXSA are in unit of 32-bit words. - // IN FIFO 0 was configured during enumeration, hence the "+ 16". - uint16_t const allocated_size = (USB0.grxfsiz & 0x0000ffff) + 16; - uint16_t const fifo_size = (EP_FIFO_SIZE/4 - allocated_size) / (EP_FIFO_NUM-1); - uint32_t const fifo_offset = allocated_size + fifo_size*(fifo_num-1); - - // DIEPTXF starts at FIFO #1. - USB0.dieptxf[epnum - 1] = (fifo_size << USB_NPTXFDEP_S) | fifo_offset; - } - return true; -} - -void dcd_edpt_close_all(uint8_t rhport) -{ - (void) rhport; - - usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); - usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); - - // Disable non-control interrupt - USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M; - - for(uint8_t n = 1; n < EP_MAX; n++) - { - // disable OUT endpoint - out_ep[n].doepctl = 0; - xfer_status[n][TUSB_DIR_OUT].max_size = 0; - - // disable IN endpoint - in_ep[n].diepctl = 0; - xfer_status[n][TUSB_DIR_IN].max_size = 0; - } - - _allocated_fifos = 1; -} - -bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) -{ - (void)rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); - xfer->buffer = buffer; - // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API - xfer->total_len = total_bytes; - xfer->queued_len = 0; - xfer->short_packet = false; - - uint16_t num_packets = (total_bytes / xfer->max_size); - uint8_t short_packet_size = total_bytes % xfer->max_size; - - // Zero-size packet is special case. - if (short_packet_size > 0 || (total_bytes == 0)) { - num_packets++; - } - - ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i", - epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"), - num_packets, total_bytes); - - // IN and OUT endpoint xfers are interrupt-driven, we just schedule them - // here. - if (dir == TUSB_DIR_IN) { - // A full IN transfer (multiple packets, possibly) triggers XFRC. - USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes; - USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK - - // For ISO endpoint with interval=1 set correct DATA0/DATA1 bit for next frame - if ((USB0.in_ep_reg[epnum].diepctl & USB_D_EPTYPE0_M) == (1 << USB_D_EPTYPE1_S) && xfer->interval == 1) { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (USB0.dsts & (1u << USB_SOFFN_S)); - USB0.in_ep_reg[epnum].diepctl |= (odd_frame_now ? USB_DI_SETD0PID1 : USB_DI_SETD1PID1); - } - - // Enable fifo empty interrupt only if there are something to put in the fifo. - if(total_bytes != 0) { - USB0.dtknqr4_fifoemptymsk |= (1 << epnum); - } - } else { - // Each complete packet for OUT xfers triggers XFRC. - USB0.out_ep_reg[epnum].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S); - USB0.out_ep_reg[epnum].doepctl |= USB_EPENA0_M | USB_CNAK0_M; - - // For ISO endpoint with interval=1 set correct DATA0/DATA1 bit for next frame - if ((USB0.out_ep_reg[epnum].doepctl & USB_D_EPTYPE0_M) == (1 << USB_D_EPTYPE1_S) && xfer->interval == 1) { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (USB0.dsts & (1u << USB_SOFFN_S)); - USB0.out_ep_reg[epnum].doepctl |= (odd_frame_now ? USB_DO_SETD0PID1 : USB_DO_SETD1PID1); - } - } - return true; -} - -#if 0 // TODO support dcd_edpt_xfer_fifo API -bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) -{ - (void)rhport; -} -#endif - -void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void)rhport; - - usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); - usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - if (dir == TUSB_DIR_IN) { - // Only disable currently enabled non-control endpoint - if ((epnum == 0) || !(in_ep[epnum].diepctl & USB_D_EPENA1_M)) { - in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M); - } else { - // Stop transmitting packets and NAK IN xfers. - in_ep[epnum].diepctl |= USB_DI_SNAK1_M; - // while ((in_ep[epnum].diepint & USB_DI_SNAK1_M) == 0) ; - while ((in_ep[epnum].diepint & USB_D_INEPNAKEFF1_M) == 0) ; - - // Disable the endpoint. Note that both SNAK and STALL are set here. - in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M | USB_D_EPDIS1_M); - while ((in_ep[epnum].diepint & USB_D_EPDISBLD0_M) == 0) ; - in_ep[epnum].diepint = USB_D_EPDISBLD0_M; - } - - // Flush the FIFO, and wait until we have confirmed it cleared. - uint8_t const fifo_num = ((in_ep[epnum].diepctl >> USB_D_TXFNUM1_S) & USB_D_TXFNUM1_V); - // USB0.grstctl |= (fifo_num << USB_TXFNUM_S); - // USB0.grstctl |= USB_TXFFLSH_M; - // while ((USB0.grstctl & USB_TXFFLSH_M) != 0) ; - uint32_t rstctl_last = USB0.grstctl; - uint32_t rstctl = USB_TXFFLSH_M; - rstctl |= (fifo_num << USB_TXFNUM_S); - USB0.grstctl = rstctl; - while ((USB0.grstctl & USB_TXFFLSH_M) != 0) ; - USB0.grstctl = rstctl_last; - // TODO: Clear grstctl::fifo_num after fifo flsh - } else { - // Only disable currently enabled non-control endpoint - if ((epnum == 0) || !(out_ep[epnum].doepctl & USB_EPENA0_M)) { - out_ep[epnum].doepctl |= USB_STALL0_M; - } else { - // Asserting GONAK is required to STALL an OUT endpoint. - // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt - // anyway, and it can't be cleared by user code. If this while loop never - // finishes, we have bigger problems than just the stack. - USB0.dctl |= USB_SGOUTNAK_M; - while ((USB0.gintsts & USB_GOUTNAKEFF_M) == 0) ; - - // Ditto here- disable the endpoint. Note that only STALL and not SNAK - // is set here. - out_ep[epnum].doepctl |= (USB_STALL0_M | USB_EPDIS0_M); - while ((out_ep[epnum].doepint & USB_EPDISBLD0_M) == 0) ; - out_ep[epnum].doepint = USB_EPDISBLD0_M; - - // Allow other OUT endpoints to keep receiving. - USB0.dctl |= USB_CGOUTNAK_M; - } - } -} - -void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void)rhport; - - usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); - usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - if (dir == TUSB_DIR_IN) { - in_ep[epnum].diepctl &= ~USB_D_STALL1_M; - - uint8_t eptype = (in_ep[epnum].diepctl & USB_D_EPTYPE1_M) >> USB_D_EPTYPE1_S; - // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt - // and bulk endpoints. - if (eptype == 2 || eptype == 3) { - in_ep[epnum].diepctl |= USB_DI_SETD0PID1_M; - } - } else { - out_ep[epnum].doepctl &= ~USB_STALL1_M; - - uint8_t eptype = (out_ep[epnum].doepctl & USB_EPTYPE1_M) >> USB_EPTYPE1_S; - // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt - // and bulk endpoints. - if (eptype == 2 || eptype == 3) { - out_ep[epnum].doepctl |= USB_DO_SETD0PID1_M; - } - } -} - -/*------------------------------------------------------------------*/ - -static void receive_packet(xfer_ctl_t *xfer, /* usb_out_endpoint_t * out_ep, */ uint16_t xfer_size) -{ - ESP_EARLY_LOGV(TAG, "USB - receive_packet"); - volatile uint32_t *rx_fifo = USB0.fifo[0]; - - // See above TODO - // uint16_t remaining = (out_ep->DOEPTSIZ & UsbDOEPTSIZ_XFRSIZ_Msk) >> UsbDOEPTSIZ_XFRSIZ_Pos; - // xfer->queued_len = xfer->total_len - remaining; - - uint16_t remaining = xfer->total_len - xfer->queued_len; - uint16_t to_recv_size; - - if (remaining <= xfer->max_size) { - // Avoid buffer overflow. - to_recv_size = (xfer_size > remaining) ? remaining : xfer_size; - } else { - // Room for full packet, choose recv_size based on what the microcontroller - // claims. - to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size; - } - - // Common buffer read -#if 0 // TODO support dcd_edpt_xfer_fifo API - if (xfer->ff) - { - // Ring buffer - tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) rx_fifo, to_recv_size); - } - else -#endif - { - uint8_t to_recv_rem = to_recv_size % 4; - uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem; - - // Do not assume xfer buffer is aligned. - uint8_t *base = (xfer->buffer + xfer->queued_len); - - // This for loop always runs at least once- skip if less than 4 bytes - // to collect. - if (to_recv_size >= 4) { - for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) { - uint32_t tmp = (*rx_fifo); - base[i] = tmp & 0x000000FF; - base[i + 1] = (tmp & 0x0000FF00) >> 8; - base[i + 2] = (tmp & 0x00FF0000) >> 16; - base[i + 3] = (tmp & 0xFF000000) >> 24; - } - } - - // Do not read invalid bytes from RX FIFO. - if (to_recv_rem != 0) { - uint32_t tmp = (*rx_fifo); - uint8_t *last_32b_bound = base + to_recv_size_aligned; - - last_32b_bound[0] = tmp & 0x000000FF; - if (to_recv_rem > 1) { - last_32b_bound[1] = (tmp & 0x0000FF00) >> 8; - } - if (to_recv_rem > 2) { - last_32b_bound[2] = (tmp & 0x00FF0000) >> 16; - } - } - } - - xfer->queued_len += xfer_size; - - // Per USB spec, a short OUT packet (including length 0) is always - // indicative of the end of a transfer (at least for ctl, bulk, int). - xfer->short_packet = (xfer_size < xfer->max_size); -} - -static void transmit_packet(xfer_ctl_t *xfer, volatile usb_in_endpoint_t *in_ep, uint8_t fifo_num) -{ - ESP_EARLY_LOGV(TAG, "USB - transmit_packet"); - volatile uint32_t *tx_fifo = USB0.fifo[fifo_num]; - - uint16_t remaining = (in_ep->dieptsiz & 0x7FFFFU) >> USB_D_XFERSIZE0_S; - xfer->queued_len = xfer->total_len - remaining; - - uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining; - -#if 0 // TODO support dcd_edpt_xfer_fifo API - if (xfer->ff) - { - tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) tx_fifo, to_xfer_size); - } - else -#endif - { - uint8_t to_xfer_rem = to_xfer_size % 4; - uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem; - - // Buffer might not be aligned to 32b, so we need to force alignment - // by copying to a temp var. - uint8_t *base = (xfer->buffer + xfer->queued_len); - - // This for loop always runs at least once- skip if less than 4 bytes - // to send off. - if (to_xfer_size >= 4) { - for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) { - uint32_t tmp = base[i] | (base[i + 1] << 8) | - (base[i + 2] << 16) | (base[i + 3] << 24); - (*tx_fifo) = tmp; - } - } - - // Do not read beyond end of buffer if not divisible by 4. - if (to_xfer_rem != 0) { - uint32_t tmp = 0; - uint8_t *last_32b_bound = base + to_xfer_size_aligned; - - tmp |= last_32b_bound[0]; - if (to_xfer_rem > 1) { - tmp |= (last_32b_bound[1] << 8); - } - if (to_xfer_rem > 2) { - tmp |= (last_32b_bound[2] << 16); - } - - (*tx_fifo) = tmp; - } - } -} - -static void read_rx_fifo(void) -{ - // Pop control word off FIFO (completed xfers will have 2 control words, - // we only pop one ctl word each interrupt). - uint32_t const ctl_word = USB0.grxstsp; - uint8_t const pktsts = (ctl_word & USB_PKTSTS_M) >> USB_PKTSTS_S; - uint8_t const epnum = (ctl_word & USB_CHNUM_M ) >> USB_CHNUM_S; - uint16_t const bcnt = (ctl_word & USB_BCNT_M ) >> USB_BCNT_S; - - switch (pktsts) { - case 0x01: // Global OUT NAK (Interrupt) - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Global OUT NAK"); - break; - - case 0x02: { // Out packet recvd - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet"); - xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - receive_packet(xfer, bcnt); - } - break; - - case 0x03: // Out packet done (Interrupt) - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet done"); - break; - - case 0x04: // Step 2: Setup transaction completed (Interrupt) - // After this event, OEPINT interrupt will occur with SETUP bit set - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet done"); - USB0.out_ep_reg[epnum].doeptsiz |= USB_SUPCNT0_M; - break; - - case 0x06: { // Step1: Setup data packet received - volatile uint32_t *rx_fifo = USB0.fifo[0]; - - // We can receive up to three setup packets in succession, but - // only the last one is valid. Therefore we just overwrite it - _setup_packet[0] = (*rx_fifo); - _setup_packet[1] = (*rx_fifo); - - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet : 0x%08x 0x%08x", _setup_packet[0], _setup_packet[1]); - } - break; - - default: // Invalid, do something here, like breakpoint? - TU_BREAKPOINT(); - break; - } -} - -static void handle_epout_ints(void) -{ - // GINTSTS will be cleared with DAINT == 0 - // DAINT for a given EP clears when DOEPINTx is cleared. - // DOEPINT will be cleared when DAINT's out bits are cleared. - for (int n = 0; n < USB_OUT_EP_NUM; n++) { - xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); - - if (USB0.daint & (1 << (16 + n))) { - // SETUP packet Setup Phase done. - if ((USB0.out_ep_reg[n].doepint & USB_SETUP0_M)) { - USB0.out_ep_reg[n].doepint = USB_STUPPKTRCVD0_M | USB_SETUP0_M; // clear - dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true); - } - - // OUT XFER complete (single packet).q - if (USB0.out_ep_reg[n].doepint & USB_XFERCOMPL0_M) { - - ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP OUT - XFER complete (single packet)"); - USB0.out_ep_reg[n].doepint = USB_XFERCOMPL0_M; - - // Transfer complete if short packet or total len is transferred - if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) { - xfer->short_packet = false; - dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true); - } else { - // Schedule another packet to be received. - USB0.out_ep_reg[n].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S); - USB0.out_ep_reg[n].doepctl |= USB_EPENA0_M | USB_CNAK0_M; - } - } - } - } -} - -static void handle_epin_ints(void) -{ - // GINTSTS will be cleared with DAINT == 0 - // DAINT for a given EP clears when DIEPINTx is cleared. - // IEPINT will be cleared when DAINT's out bits are cleared. - for (uint32_t n = 0; n < USB_IN_EP_NUM; n++) { - xfer_ctl_t *xfer = &xfer_status[n][TUSB_DIR_IN]; - - if (USB0.daint & (1 << (0 + n))) { - ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP IN %u", n); - - if (USB0.in_ep_reg[n].diepint & BIT(15)) { - USB0.in_ep_reg[n].diepint = BIT(15); - ESP_EARLY_LOGE(TAG, "Unknown Condition");//todo: - bus_reset(); - } - - // IN XFER complete (entire xfer). - if (USB0.in_ep_reg[n].diepint & USB_D_XFERCOMPL0_M) { - ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER complete!"); - USB0.in_ep_reg[n].diepint = USB_D_XFERCOMPL0_M; - dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); - if (!(USB0.in_ep_reg[n].diepint & USB_D_TXFEMP0_M)) { - ESP_EARLY_LOGE(TAG, "Complete but not empty: %u/%u", xfer->queued_len, xfer->total_len);//todo: - } - } - - // XFER FIFO empty - if (USB0.in_ep_reg[n].diepint & USB_D_TXFEMP0_M) { - ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER FIFO empty!"); - USB0.in_ep_reg[n].diepint = USB_D_TXFEMP0_M; - transmit_packet(xfer, &USB0.in_ep_reg[n], n); - - // Turn off TXFE if all bytes are written. - if (xfer->queued_len == xfer->total_len) - { - USB0.dtknqr4_fifoemptymsk &= ~(1 << n); - } - } - - // XFER Timeout - if (USB0.in_ep_reg[n].diepint & USB_D_TIMEOUT0_M) { - // Clear interrupt or enpoint will hang. - USB0.in_ep_reg[n].diepint = USB_D_TIMEOUT0_M; - ESP_EARLY_LOGE(TAG, "XFER Timeout");//todo: - // Maybe retry? - } - } - } -} - - -static void _dcd_int_handler(void* arg) -{ - (void) arg; - uint8_t const rhport = 0; - - const uint32_t int_msk = USB0.gintmsk; - const uint32_t int_status = USB0.gintsts & int_msk; - - if (int_status & USB_USBRST_M) { - // start of reset - ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset"); - USB0.gintsts = USB_USBRST_M; - // FIFOs will be reassigned when the endpoints are reopen - _allocated_fifos = 1; - bus_reset(); - } - - if (int_status & USB_RESETDET_M) { - ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset while suspend"); - USB0.gintsts = USB_RESETDET_M; - // no need to double reset - if ((int_status & USB_USBRST_M) == 0) { - _allocated_fifos = 1; - bus_reset(); - } - } - - if (int_status & USB_ENUMDONE_M) { - // ENUMDNE detects speed of the link. For full-speed, we - // always expect the same value. This interrupt is considered - // the end of reset. - USB0.gintsts = USB_ENUMDONE_M; - enum_done_processing(); - dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true); - } - - if(int_status & USB_USBSUSP_M) - { - USB0.gintsts = USB_USBSUSP_M; - //dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); - dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); - _allocated_fifos = 1; - } - - if(int_status & USB_WKUPINT_M) - { - USB0.gintsts = USB_WKUPINT_M; - dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); - } - - if (int_status & USB_OTGINT_M) - { - // OTG INT bit is read-only - ESP_EARLY_LOGV(TAG, "dcd_int_handler - disconnected"); - - uint32_t const otg_int = USB0.gotgint; - - if (otg_int & USB_SESENDDET_M) - { - dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); - _allocated_fifos = 1; - } - - USB0.gotgint = otg_int; - } - - if (int_status & USB_SOF_M) { - USB0.gintsts = USB_SOF_M; - - // Disable SOF interrupt since currently only used for remote wakeup detection - USB0.gintmsk &= ~USB_SOFMSK_M; - - dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); - } - - - if (int_status & USB_RXFLVI_M) { - // RXFLVL bit is read-only - ESP_EARLY_LOGV(TAG, "dcd_int_handler - rx!"); - - // Mask out RXFLVL while reading data from FIFO - USB0.gintmsk &= ~USB_RXFLVIMSK_M; - read_rx_fifo(); - USB0.gintmsk |= USB_RXFLVIMSK_M; - } - - // OUT endpoint interrupt handling. - if (int_status & USB_OEPINT_M) { - // OEPINT is read-only - ESP_EARLY_LOGV(TAG, "dcd_int_handler - OUT endpoint!"); - handle_epout_ints(); - } - - // IN endpoint interrupt handling. - if (int_status & USB_IEPINT_M) { - // IEPINT bit read-only - ESP_EARLY_LOGV(TAG, "dcd_int_handler - IN endpoint!"); - handle_epin_ints(); - } - - // Without handling - USB0.gintsts |= USB_CURMOD_INT_M | - USB_MODEMIS_M | - USB_OTGINT_M | - USB_NPTXFEMP_M | - USB_GINNAKEFF_M | - USB_GOUTNAKEFF | - USB_ERLYSUSP_M | - USB_USBSUSP_M | - USB_ISOOUTDROP_M | - USB_EOPF_M | - USB_EPMIS_M | - USB_INCOMPISOIN_M | - USB_INCOMPIP_M | - USB_FETSUSP_M | - USB_PTXFEMP_M; -} - -void dcd_int_enable (uint8_t rhport) -{ - (void) rhport; - esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, (intr_handler_t) _dcd_int_handler, NULL, &usb_ih); -} - -void dcd_int_disable (uint8_t rhport) -{ - (void) rhport; - esp_intr_free(usb_ih); -} - -#endif // #if OPT_MCU_ESP32S2 || OPT_MCU_ESP32S3 - diff --git a/configs/builds.json b/configs/builds.json index e34813cfa..ddfb8e4f8 100644 --- a/configs/builds.json +++ b/configs/builds.json @@ -4,7 +4,7 @@ "file":"libspi_flash.a", "src":"build/esp-idf/spi_flash/libspi_flash.a", "out":"lib/libspi_flash.a", - "targets":["esp32","esp32c2","esp32c3","esp32s2","esp32s3","esp32c6","esp32h2"] + "targets":["esp32","esp32c2","esp32c3","esp32s2","esp32s3","esp32c6","esp32h2","esp32p4"] }, { "file":"libesp_psram.a", @@ -44,6 +44,20 @@ } ], "targets":[ + { + "target": "esp32p4", + "features":["qio_ram"], + "idf_libs":["qio","80m"], + "bootloaders":[ + ["qio","80m"], + ["dio","80m"], + ["qio","40m"], + ["dio","40m"] + ], + "mem_variants":[ + ["dio","80m"] + ] + }, { "target": "esp32c2", "skip": 1, @@ -143,8 +157,7 @@ ["qio","80m","opi_ram"], ["dio","80m","qio_ram"], ["dio","80m","opi_ram"], - ["opi","80m","opi_ram"], - ["opi","80m","qio_ram"] + ["opi","80m","opi_ram"] ] } ] diff --git a/configs/defconfig.common b/configs/defconfig.common index e3f393f2e..48baf5081 100644 --- a/configs/defconfig.common +++ b/configs/defconfig.common @@ -19,12 +19,8 @@ CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2048 CONFIG_ESP_TASK_WDT_PANIC=y -CONFIG_ESP_TIMER_TASK_STACK_SIZE=4096 +CONFIG_ESP_TIMER_TASK_STACK_SIZE=8192 CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESP_WIFI_11KV_SUPPORT=y -CONFIG_ESP_WIFI_SCAN_CACHE=y -CONFIG_ESP_WIFI_MBO_SUPPORT=y -CONFIG_ESP_WIFI_11R_SUPPORT=y CONFIG_ESP_WIFI_FTM_ENABLE=y CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=8 CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=8 @@ -33,6 +29,7 @@ CONFIG_ESP_WIFI_CSI_ENABLED=y CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y # CONFIG_ESP_WIFI_IRAM_OPT is not set # CONFIG_ESP_WIFI_RX_IRAM_OPT is not set +CONFIG_ESP_PHY_REDUCE_TX_POWER=y CONFIG_ETH_SPI_ETHERNET_DM9051=y CONFIG_ETH_SPI_ETHERNET_W5500=y CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL=y @@ -40,12 +37,17 @@ CONFIG_FATFS_CODEPAGE_850=y CONFIG_FATFS_LFN_STACK=y # CONFIG_FATFS_API_ENCODING_ANSI_OEM is not set CONFIG_FATFS_API_ENCODING_UTF_8=y +CONFIG_FATFS_USE_LABEL=y # CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT is not set CONFIG_FMB_TIMER_PORT_ENABLED=y CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y # CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION is not set CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1024 +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y CONFIG_HEAP_POISONING_LIGHT=y CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 CONFIG_HTTPD_WS_SUPPORT=y @@ -55,19 +57,37 @@ CONFIG_LWIP_ETHARP_TRUST_IP_MAC=y CONFIG_LWIP_TCP_SYNMAXRTX=6 CONFIG_LWIP_TCP_MSS=1436 CONFIG_LWIP_TCP_RTO_TIME=3000 -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=2560 +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096 CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0=y CONFIG_LWIP_MAX_SOCKETS=16 CONFIG_LWIP_IP_FORWARD=y CONFIG_LWIP_IPV4_NAPT=y -CONFIG_LWIP_DHCP_RESTORE_LAST_IP=y +CONFIG_LWIP_DHCP_RESTORE_LAST_IP=n CONFIG_LWIP_DHCP_OPTIONS_LEN=128 CONFIG_LWIP_SNTP_MAX_SERVERS=3 CONFIG_LWIP_SNTP_UPDATE_DELAY=10800000 CONFIG_LWIP_DHCP_GET_NTP_SRV=y +CONFIG_LWIP_IPV6_AUTOCONFIG=y +CONFIG_LWIP_IPV6_DHCP6=y +CONFIG_LWIP_IPV6_RDNSS_MAX_DNS_SERVERS=2 +CONFIG_LWIP_PPP_SUPPORT=y +CONFIG_LWIP_PPP_NOTIFY_PHASE_SUPPORT=y +CONFIG_LWIP_PPP_PAP_SUPPORT=y +CONFIG_LWIP_PPP_ENABLE_IPV6=n +CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y +CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y +CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_DEFAULT=y +CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT=y +CONFIG_LWIP_MULTICAST_PING=y +CONFIG_LWIP_BROADCAST_PING=y +CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 CONFIG_MBEDTLS_PSK_MODES=y CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y +CONFIG_MBEDTLS_ECJPAKE_C=y +CONFIG_MBEDTLS_HKDF_C=y CONFIG_MBEDTLS_CAMELLIA_C=y +CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER=y # CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN is not set CONFIG_MBEDTLS_SSL_PROTO_DTLS=y CONFIG_OPENSSL_ASSERT_DO_NOTHING=y @@ -80,7 +100,6 @@ CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=4096 # CONFIG_SPI_SLAVE_ISR_IN_IRAM is not set CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=4096 CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=0 -CONFIG_LWIP_IPV6_AUTOCONFIG=y CONFIG_ESP_RMAKER_SKIP_VERSION_CHECK=y CONFIG_ESP_RMAKER_USER_ID_CHECK=y CONFIG_ESP_INSIGHTS_ENABLED=y @@ -93,11 +112,31 @@ CONFIG_DIAG_ENABLE_WIFI_METRICS=y CONFIG_DIAG_ENABLE_VARIABLES=y CONFIG_DIAG_ENABLE_NETWORK_VARIABLES=y CONFIG_ESP_COREDUMP_ENABLE=y -CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=n +CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=y CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF=y CONFIG_ESP_COREDUMP_CHECKSUM_CRC32=y CONFIG_ESP_COREDUMP_MAX_TASKS_NUM=64 -CONFIG_ESP_COREDUMP_STACK_SIZE=1024 +CONFIG_ESP_COREDUMP_STACK_SIZE=0 CONFIG_MBEDTLS_DYNAMIC_BUFFER=y CONFIG_MBEDTLS_DYNAMIC_FREE_PEER_CERT=y CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y +CONFIG_I2S_ISR_IRAM_SAFE=y +# +# Matter Settings +# +# Disable Matter BLE +CONFIG_ENABLE_CHIPOBLE=n +CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=n +# ESP Insights +CONFIG_ENABLE_ESP_INSIGHTS_TRACE=n +# Use compact attribute storage mode +CONFIG_ESP_MATTER_NVS_USE_COMPACT_ATTR_STORAGE=y + +#TinyUSB Config +CONFIG_TINYUSB_CDC_MAX_PORTS=2 +CONFIG_USB_HOST_HUBS_SUPPORTED=y +CONFIG_USB_HOST_HUB_MULTI_LEVEL=y +CONFIG_USB_HOST_HW_BUFFER_BIAS_PERIODIC_OUT=y + +# Zigbee Config +CONFIG_ZB_ENABLED=y diff --git a/configs/defconfig.debug_default b/configs/defconfig.debug_default index 1d8258392..c1858126e 100644 --- a/configs/defconfig.debug_default +++ b/configs/defconfig.debug_default @@ -1,2 +1,2 @@ -CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y CONFIG_LOG_DEFAULT_LEVEL_ERROR=y diff --git a/configs/defconfig.esp32 b/configs/defconfig.esp32 index eb522af1a..db01aeab3 100644 --- a/configs/defconfig.esp32 +++ b/configs/defconfig.esp32 @@ -18,4 +18,5 @@ CONFIG_TWAI_ERRATA_FIX_BUS_OFF_REC=y CONFIG_TWAI_ERRATA_FIX_TX_INTR_LOST=y CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID=y CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT=y -CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y \ No newline at end of file +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/configs/defconfig.esp32c3 b/configs/defconfig.esp32c3 index 9320ceb9e..1baf62540 100644 --- a/configs/defconfig.esp32c3 +++ b/configs/defconfig.esp32c3 @@ -2,3 +2,8 @@ CONFIG_RTC_CLK_CAL_CYCLES=576 # CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 is not set CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=2304 CONFIG_BT_ENABLED=y +CONFIG_ESP_WIFI_11KV_SUPPORT=y +CONFIG_ESP_WIFI_SCAN_CACHE=y +CONFIG_ESP_WIFI_MBO_SUPPORT=y +CONFIG_ESP_WIFI_11R_SUPPORT=y +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/configs/defconfig.esp32c6 b/configs/defconfig.esp32c6 index 14939d8fa..d0e2b7bbd 100644 --- a/configs/defconfig.esp32c6 +++ b/configs/defconfig.esp32c6 @@ -3,3 +3,48 @@ CONFIG_BT_BLE_BLUFI_ENABLE=y CONFIG_RTC_CLK_CAL_CYCLES=576 # CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 is not set CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=2304 +# This Enables RISCV LP for C6 - but it can't be used within Arduino at this time. +#CONFIG_ULP_COPROC_ENABLED=y +#CONFIG_ULP_COPROC_LP_CORE=y +#CONFIG_ULP_COPROC_RESERVE_MEM=4096 + +# +# OpenThread +# +CONFIG_OPENTHREAD_ENABLED=y +# Border Router disabled +# CONFIG_OPENTHREAD_BORDER_ROUTER=y +# CONFIG_OPENTHREAD_RADIO_SPINEL_UART=y + +# DNS64 and NAT64 will be disabled for a while +# OT IDF issue https://github.com/espressif/esp-idf/issues/15069 +# CONFIG_OPENTHREAD_DNS64_CLIENT=y + +# Radio for RPC +# CONFIG_OPENTHREAD_RADIO=y +# CONFIG_OPENTHREAD_RADIO_NATIVE=y +# CONFIG_OPENTHREAD_DIAG=n +CONFIG_OPENTHREAD_COMMISSIONER=y +CONFIG_OPENTHREAD_JOINER=y +CONFIG_OPENTHREAD_CLI=y +CONFIG_OPENTHREAD_SRP_CLIENT=y +CONFIG_OPENTHREAD_DNS_CLIENT=y +# Default dataset for quick start +CONFIG_OPENTHREAD_NETWORK_NAME="OpenThread-ESP" +CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX="fd00:db8:a0:0::/64" +CONFIG_OPENTHREAD_NETWORK_CHANNEL=15 +CONFIG_OPENTHREAD_NETWORK_PANID=0x1234 +CONFIG_OPENTHREAD_NETWORK_EXTPANID="dead00beef00cafe" +CONFIG_OPENTHREAD_NETWORK_MASTERKEY="00112233445566778899aabbccddeeff" +CONFIG_OPENTHREAD_NETWORK_PSKC="104810e2315100afd6bc9215a6bfac53" +# end of OpenThread + +# Matter shall use only WiFi +CONFIG_ENABLE_MATTER_OVER_THREAD=n + +# +# Zigbee +# +CONFIG_ZB_ZED=y +CONFIG_ZB_RADIO_NATIVE=y +# end of Zigbee diff --git a/configs/defconfig.esp32h2 b/configs/defconfig.esp32h2 index 14939d8fa..024120d34 100644 --- a/configs/defconfig.esp32h2 +++ b/configs/defconfig.esp32h2 @@ -3,3 +3,41 @@ CONFIG_BT_BLE_BLUFI_ENABLE=y CONFIG_RTC_CLK_CAL_CYCLES=576 # CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 is not set CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=2304 + +# +# OpenThread +# +CONFIG_OPENTHREAD_ENABLED=y +# Border Router disabled +# CONFIG_OPENTHREAD_BORDER_ROUTER=y +# CONFIG_OPENTHREAD_RADIO_SPINEL_UART=y + +# DNS64 and NAT64 will be disabled for a while +# OT IDF issue https://github.com/espressif/esp-idf/issues/15069 +# CONFIG_OPENTHREAD_DNS64_CLIENT=y + +# Radio for RPC +# CONFIG_OPENTHREAD_RADIO=y +# CONFIG_OPENTHREAD_RADIO_NATIVE=y +# CONFIG_OPENTHREAD_DIAG=n +CONFIG_OPENTHREAD_COMMISSIONER=y +CONFIG_OPENTHREAD_JOINER=y +CONFIG_OPENTHREAD_CLI=y +CONFIG_OPENTHREAD_SRP_CLIENT=y +CONFIG_OPENTHREAD_DNS_CLIENT=y +# Default dataset for quick start +CONFIG_OPENTHREAD_NETWORK_NAME="OpenThread-ESP" +CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX="fd00:db8:a0:0::/64" +CONFIG_OPENTHREAD_NETWORK_CHANNEL=15 +CONFIG_OPENTHREAD_NETWORK_PANID=0x1234 +CONFIG_OPENTHREAD_NETWORK_EXTPANID="dead00beef00cafe" +CONFIG_OPENTHREAD_NETWORK_MASTERKEY="00112233445566778899aabbccddeeff" +CONFIG_OPENTHREAD_NETWORK_PSKC="104810e2315100afd6bc9215a6bfac53" +# end of OpenThread + +# +# Zigbee +# +CONFIG_ZB_ZED=y +CONFIG_ZB_RADIO_NATIVE=y +# end of Zigbee diff --git a/configs/defconfig.esp32p4 b/configs/defconfig.esp32p4 new file mode 100644 index 000000000..9cc5e8392 --- /dev/null +++ b/configs/defconfig.esp32p4 @@ -0,0 +1,19 @@ +CONFIG_IDF_EXPERIMENTAL_FEATURES=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +# CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 is not set +CONFIG_LWIP_TCP_SACK_OUT=y + +CONFIG_SLAVE_IDF_TARGET_ESP32C6=y +CONFIG_ESP_SDIO_BUS_WIDTH=4 +CONFIG_ESP_SDIO_CLOCK_FREQ_KHZ=40000 +CONFIG_ESP_SDIO_PIN_CMD=19 +CONFIG_ESP_SDIO_PIN_CLK=18 +CONFIG_ESP_SDIO_PIN_D0=14 +CONFIG_ESP_SDIO_PIN_D1=15 +CONFIG_ESP_SDIO_PIN_D2=16 +CONFIG_ESP_SDIO_PIN_D3=17 + +# RGB Display Optimizations +CONFIG_LCD_RGB_ISR_IRAM_SAFE=y +CONFIG_LCD_RGB_RESTART_IN_VSYNC=y diff --git a/configs/defconfig.esp32s2 b/configs/defconfig.esp32s2 index 43568afcc..3b0af548a 100644 --- a/configs/defconfig.esp32s2 +++ b/configs/defconfig.esp32s2 @@ -6,4 +6,12 @@ CONFIG_ESP32S2_KEEP_USB_ALIVE=y # CONFIG_USE_WAKENET is not set # CONFIG_USE_MULTINET is not set CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y -CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n \ No newline at end of file +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n + +# ULP Setting for IDF 5.x +CONFIG_ULP_COPROC_ENABLED=y +# end of ULP COPROC_ENABLE +# Choose FSM or RISCV exclusively! Never both. +CONFIG_ULP_COPROC_TYPE_FSM=y +# CONFIG_ULP_COPROC_TYPE_RISCV=y +CONFIG_ULP_COPROC_RESERVE_MEM=512 diff --git a/configs/defconfig.esp32s3 b/configs/defconfig.esp32s3 index 6670bc2cb..36fa6b6a5 100644 --- a/configs/defconfig.esp32s3 +++ b/configs/defconfig.esp32s3 @@ -8,4 +8,16 @@ CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES_TWO=y # CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 is not set CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120 -CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n \ No newline at end of file +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n + +# ULP Setting for IDF 5.x +CONFIG_ULP_COPROC_ENABLED=y +# end of ULP COPROC_ENABLE +# Choose FSM or RISCV exclusively! Never both. +CONFIG_ULP_COPROC_TYPE_FSM=y +# CONFIG_ULP_COPROC_TYPE_RISCV=y +CONFIG_ULP_COPROC_RESERVE_MEM=512 + +# RGB Display Optimizations +# CONFIG_LCD_RGB_ISR_IRAM_SAFE is not set +CONFIG_LCD_RGB_RESTART_IN_VSYNC=y diff --git a/configs/pio_end.txt b/configs/pioarduino_end.txt similarity index 87% rename from configs/pio_end.txt rename to configs/pioarduino_end.txt index e7544f1dd..d9b5d5467 100644 --- a/configs/pio_end.txt +++ b/configs/pioarduino_end.txt @@ -1,5 +1,6 @@ "ARDUINO_ARCH_ESP32", - "ESP32", + "CHIP_HAVE_CONFIG_H", + ("ESP32", "ESP32"), ("F_CPU", "$BOARD_F_CPU"), ("ARDUINO", 10812), ("ARDUINO_VARIANT", '\\"%s\\"' % board_config.get("build.variant").replace('"', "")), diff --git a/configs/pio_start.txt b/configs/pioarduino_start.txt similarity index 70% rename from configs/pio_start.txt rename to configs/pioarduino_start.txt index 9a38020fb..ec10a10dc 100644 --- a/configs/pio_start.txt +++ b/configs/pioarduino_start.txt @@ -22,7 +22,7 @@ kinds of creative coding, interactive objects, spaces or physical experiences. http://arduino.cc/en/Reference/HomePage """ -# Extends: https://github.com/platformio/platform-espressif32/blob/develop/builder/main.py +# Extends: https://github.com/pioarduino/platform-espressif32/blob/develop/builder/main.py from os.path import basename, join @@ -37,4 +37,26 @@ FRAMEWORK_SDK_DIR = env.PioPlatform().get_package_dir( board_config = env.BoardConfig() +flatten_cppdefines = env.Flatten(env['CPPDEFINES']) + +# +# zigbee libs +# +if "ZIGBEE_MODE_ZCZR" in flatten_cppdefines: + env.Append( + LIBS=[ + "-lesp_zb_api.zczr", + "-lzboss_stack.zczr", + "-lzboss_port.native" + ] + ) +if "ZIGBEE_MODE_ED" in flatten_cppdefines: + env.Append( + LIBS=[ + "-lesp_zb_api.ed", + "-lzboss_stack.ed", + "-lzboss_port.native" + ] + ) + env.Append( diff --git a/main/idf_component.yml b/main/idf_component.yml index 011906d27..a85eca541 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -1,6 +1,6 @@ dependencies: # Required IDF version - idf: ">=5.1" + idf: ">=5.3" espressif/esp32-camera: version: "master" git: https://github.com/espressif/esp32-camera.git @@ -12,54 +12,12 @@ dependencies: require: public rules: - if: "target not in [esp32c2]" - espressif/esp-dl: - version: "master" - git: https://github.com/espressif/esp-dl.git - require: public - rules: - - if: "target in [esp32s3, esp32s2, esp32]" espressif/esp-sr: version: ">=1.4.2" rules: - if: "target in [esp32s3]" - - # esp-sr: "^1.3.1" - # esp32-camera: "^2.0.4" - # esp-dl: - # git: https://github.com/espressif/esp-dl.git - # espressif/esp_rainmaker: - # path: components/esp_rainmaker - # git: https://github.com/espressif/esp-rainmaker.git - - # # Defining a dependency from the registry: - # # https://components.espressif.com/component/example/cmp - # example/cmp: "^3.3.3" # Automatically update minor releases - # - # # Other ways to define dependencies - # - # # For components maintained by Espressif only name can be used. - # # Same as `espressif/cmp` - # component: "~1.0.0" # Automatically update bugfix releases - # - # # Or in a longer form with extra parameters - # component2: - # version: ">=2.0.0" - # - # # For transient dependencies `public` flag can be set. - # # `public` flag doesn't have an effect for the `main` component. - # # All dependencies of `main` are public by default. - # public: true - # - # # For components hosted on non-default registry: - # service_url: "https://componentregistry.company.com" - # - # # For components in git repository: - # test_component: - # path: test_component - # git: ssh://git@gitlab.com/user/components.git - # - # # For test projects during component development - # # components can be used from a local directory - # # with relative or absolute path - # some_local_component: - # path: ../../projects/component + espressif/esp_matter: + version: "1.4.1" + require: public + rules: + - if: "target not in [esp32c2, esp32h2, esp32p4]" diff --git a/patches/lwip_max_tcp_pcb.diff b/patches/lwip_max_tcp_pcb.diff new file mode 100644 index 000000000..6b9e73cb6 --- /dev/null +++ b/patches/lwip_max_tcp_pcb.diff @@ -0,0 +1,118 @@ +diff --git a/components/lwip/lwip/src/core/memp.c b/components/lwip/lwip/src/core/memp.c +index 352ce5a55127a658b6b3c9d8541298c42df332ff..39433cf476b3456b046e337e9b1f016299964a84 100644 +--- a/components/lwip/lwip/src/core/memp.c ++++ b/components/lwip/lwip/src/core/memp.c +@@ -240,6 +240,10 @@ memp_init(void) + #endif /* MEMP_OVERFLOW_CHECK >= 2 */ + } + ++#if MEMP_MEM_MALLOC && ESP_LWIP && LWIP_TCP ++static u32_t num_tcp_pcb = 0; ++#endif ++ + static void * + #if !MEMP_OVERFLOW_CHECK + do_memp_malloc_pool(const struct memp_desc *desc) +@@ -251,6 +255,16 @@ do_memp_malloc_pool_fn(const struct memp_desc *desc, const char *file, const int + SYS_ARCH_DECL_PROTECT(old_level); + + #if MEMP_MEM_MALLOC ++#if ESP_LWIP ++#if LWIP_TCP ++ if(desc == memp_pools[MEMP_TCP_PCB]){ ++ if(num_tcp_pcb >= MEMP_NUM_TCP_PCB){ ++ return NULL; ++ } ++ } ++#endif ++#endif ++ + memp = (struct memp *)mem_malloc(MEMP_SIZE + MEMP_ALIGN_SIZE(desc->size)); + SYS_ARCH_PROTECT(old_level); + #else /* MEMP_MEM_MALLOC */ +@@ -260,6 +274,12 @@ do_memp_malloc_pool_fn(const struct memp_desc *desc, const char *file, const int + #endif /* MEMP_MEM_MALLOC */ + + if (memp != NULL) { ++#if MEMP_MEM_MALLOC && ESP_LWIP && LWIP_TCP ++ if (desc == memp_pools[MEMP_TCP_PCB]) { ++ num_tcp_pcb++; ++ } ++#endif ++ + #if !MEMP_MEM_MALLOC + #if MEMP_OVERFLOW_CHECK == 1 + memp_overflow_check_element(memp, desc); +@@ -369,6 +389,12 @@ do_memp_free_pool(const struct memp_desc *desc, void *mem) + + SYS_ARCH_PROTECT(old_level); + ++#if MEMP_MEM_MALLOC && ESP_LWIP && LWIP_TCP ++ if (desc == memp_pools[MEMP_TCP_PCB]) { ++ num_tcp_pcb--; ++ } ++#endif ++ + #if MEMP_OVERFLOW_CHECK == 1 + memp_overflow_check_element(memp, desc); + #endif /* MEMP_OVERFLOW_CHECK */ +diff --git a/components/lwip/lwip/src/core/tcp.c b/components/lwip/lwip/src/core/tcp.c +index 3fbdd89ae07807208ff7466abb50f90b5e7727e4..fe6baaf250927cb4b89f8d1dbd41c73def88692b 100644 +--- a/components/lwip/lwip/src/core/tcp.c ++++ b/components/lwip/lwip/src/core/tcp.c +@@ -1765,7 +1765,9 @@ tcp_kill_state(enum tcp_state state) + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + ++#if !ESP_LWIP + LWIP_ASSERT("invalid state", (state == CLOSING) || (state == LAST_ACK)); ++#endif + + inactivity = 0; + inactive = NULL; +@@ -1870,17 +1872,41 @@ tcp_alloc(u8_t prio) + tcp_kill_state(CLOSING); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); ++#if ESP_LWIP + if (pcb == NULL) { +- /* Try killing oldest active connection with lower priority than the new one. */ +- LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing oldest connection with prio lower than %d\n", prio)); +- tcp_kill_prio(prio); +- /* Try to allocate a tcp_pcb again. */ ++ /* Try killing oldest connection in FIN_WAIT_2. */ ++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest FIN_WAIT_2 connection\n")); ++ tcp_kill_state(FIN_WAIT_2); + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); ++ if (pcb == NULL) { ++ /* Try killing oldest connection in FIN_WAIT_1. */ ++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest FIN_WAIT_1 connection\n")); ++ tcp_kill_state(FIN_WAIT_1); ++ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); ++#endif ++ if (pcb == NULL) { ++ /* Try killing oldest active connection with lower priority than the new one. */ ++ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing oldest connection with prio lower than %d\n", prio)); ++ tcp_kill_prio(prio); ++ /* Try to allocate a tcp_pcb again. */ ++ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); ++ if (pcb != NULL) { ++ /* adjust err stats: memp_malloc failed multiple times before */ ++ MEMP_STATS_DEC(err, MEMP_TCP_PCB); ++ } ++ } ++#if ESP_LWIP ++ if (pcb != NULL) { ++ /* adjust err stats: memp_malloc failed multiple times before */ ++ MEMP_STATS_DEC(err, MEMP_TCP_PCB); ++ } ++ } + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed multiple times before */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } ++#endif + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed multiple times before */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); diff --git a/tools/add_sdk_json.py b/tools/add_sdk_json.py index d2deb4a87..7b12289bb 100644 --- a/tools/add_sdk_json.py +++ b/tools/add_sdk_json.py @@ -66,7 +66,7 @@ def add_system(systems, host, url, filename, sha, size): dep_skip = False for dep in farray['packages'][0]['platforms'][0]['toolsDependencies']: if dep['name'] == tool_name: - if dep['version'] == tool_version: + if dep['version'] == tool_version and not tool_name.startswith('esp32-arduino-libs'): print('Skipping {0}. Same version {1}'.format(tool_name, tool_version)) dep_skip = True break diff --git a/tools/check-deploy-needed.sh b/tools/check-deploy-needed.sh index 52401942a..e33cc169c 100755 --- a/tools/check-deploy-needed.sh +++ b/tools/check-deploy-needed.sh @@ -4,6 +4,11 @@ source ./tools/config.sh IDF_COMMIT=`github_last_commit "$IDF_REPO" "$IDF_BRANCH"` +if [ -z $IDF_COMMIT ]; then + echo "Failed to get IDF commit for branch $IDF_BRANCH" + exit 1 +fi + if [ -z $GITHUB_HEAD_REF ]; then current_branch=`git branch --show-current` else @@ -31,18 +36,42 @@ AR_NEW_BRANCH_NAME="idf-$IDF_BRANCH" AR_NEW_COMMIT_MESSAGE="IDF $IDF_BRANCH $IDF_COMMIT" AR_NEW_PR_TITLE="IDF $IDF_BRANCH" -LIBS_VERSION="idf-"${IDF_BRANCH//\//_}"-$IDF_COMMIT" +LIBS_RELEASE_TAG="idf-"${IDF_BRANCH//\//_}"" +LIBS_VERSION_PREFIX="$LIBS_RELEASE_TAG-$IDF_COMMIT-v" +VERSION_COUNTER=1 AR_HAS_BRANCH=`github_branch_exists "$AR_REPO" "$AR_NEW_BRANCH_NAME"` if [ "$AR_HAS_BRANCH" == "1" ]; then - AR_HAS_COMMIT=`github_commit_exists "$AR_REPO" "$AR_NEW_BRANCH_NAME" "$IDF_COMMIT"` + LATEST_LIBS_IDF=`github_get_libs_idf "$AR_REPO" "$AR_NEW_BRANCH_NAME" "$AR_NEW_PR_TITLE"` else - AR_HAS_COMMIT=`github_commit_exists "$AR_REPO" "$AR_BRANCH" "$IDF_COMMIT"` + LATEST_LIBS_IDF=`github_get_libs_idf "$AR_REPO" "$AR_BRANCH" "$AR_NEW_PR_TITLE"` fi + +echo "Current IDF commit: $IDF_COMMIT" +echo "Latest IDF commit in $AR_BRANCH of $AR_REPO: $LATEST_LIBS_IDF" + +AR_HAS_COMMIT=`if [ "$LATEST_LIBS_IDF" == "$IDF_COMMIT" ]; then echo "1"; else echo "0"; fi` AR_HAS_PR=`github_pr_exists "$AR_REPO" "$AR_NEW_BRANCH_NAME"` -LIBS_HAS_BRANCH=`github_branch_exists "$AR_LIBS_REPO" "$AR_NEW_BRANCH_NAME"` -LIBS_HAS_COMMIT=`github_commit_exists "$AR_LIBS_REPO" "$AR_NEW_BRANCH_NAME" "$IDF_COMMIT"` +LIBS_RELEASE_ID=`github_release_id "$AR_LIBS_REPO" "$LIBS_RELEASE_TAG"` +LIBS_HAS_RELEASE=`if [ -n "$LIBS_RELEASE_ID" ]; then echo "1"; else echo "0"; fi` + +if [ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]; then + echo "Workflow dispatch event. Generating new libs." + while true; do + LIBS_ASSET_ID=`github_release_asset_id "$AR_LIBS_REPO" "$LIBS_RELEASE_ID" "esp32-arduino-libs-$LIBS_VERSION_PREFIX$VERSION_COUNTER.zip"` + if [ -n "$LIBS_ASSET_ID" ]; then + VERSION_COUNTER=$((VERSION_COUNTER+1)) + else + break + fi + done +else + LIBS_ASSET_ID=`github_release_asset_id "$AR_LIBS_REPO" "$LIBS_RELEASE_ID" "esp32-arduino-libs-$LIBS_VERSION_PREFIX$VERSION_COUNTER.zip"` +fi + +LIBS_VERSION="$LIBS_VERSION_PREFIX$VERSION_COUNTER" +LIBS_HAS_ASSET=`if [ -n "$LIBS_ASSET_ID" ]; then echo "1"; else echo "0"; fi` export IDF_COMMIT @@ -54,9 +83,25 @@ export AR_HAS_COMMIT export AR_HAS_BRANCH export AR_HAS_PR +export LIBS_RELEASE_TAG export LIBS_VERSION -export LIBS_HAS_COMMIT -export LIBS_HAS_BRANCH +export LIBS_RELEASE_ID +export LIBS_HAS_RELEASE +export LIBS_ASSET_ID +export LIBS_HAS_ASSET + +if [ "$LIBS_HAS_RELEASE" == "1" ]; then + if [ "$LIBS_HAS_ASSET" == "0" ] || [ "$AR_HAS_COMMIT" == "0" ]; then + echo "Deploy needed" + export DEPLOY_NEEDED="1" + else + echo "Deploy not needed. Skipping..." + export DEPLOY_NEEDED="0" + fi +else + echo "Release for tag \"$LIBS_RELEASE_TAG\" not found. Please create the release first." + exit 1 +fi echo "IDF_COMMIT: $IDF_COMMIT" echo "AR_BRANCH: $AR_BRANCH" @@ -66,9 +111,13 @@ echo "AR_NEW_PR_TITLE: $AR_NEW_PR_TITLE" echo "AR_HAS_COMMIT: $AR_HAS_COMMIT" echo "AR_HAS_BRANCH: $AR_HAS_BRANCH" echo "AR_HAS_PR: $AR_HAS_PR" +echo "LIBS_RELEASE_TAG: $LIBS_RELEASE_TAG" echo "LIBS_VERSION: $LIBS_VERSION" -echo "LIBS_HAS_COMMIT: $LIBS_HAS_COMMIT" -echo "LIBS_HAS_BRANCH: $LIBS_HAS_BRANCH" +echo "LIBS_RELEASE_ID: $LIBS_RELEASE_ID" +echo "LIBS_HAS_RELEASE: $LIBS_HAS_RELEASE" +echo "LIBS_ASSET_ID: $LIBS_ASSET_ID" +echo "LIBS_HAS_ASSET: $LIBS_HAS_ASSET" +echo "DEPLOY_NEEDED: $DEPLOY_NEEDED" if [ ! -x $GITHUB_OUTPUT ]; then echo "idf_commit=$IDF_COMMIT" >> "$GITHUB_OUTPUT" @@ -79,7 +128,12 @@ if [ ! -x $GITHUB_OUTPUT ]; then echo "ar_has_commit=$AR_HAS_COMMIT" >> "$GITHUB_OUTPUT" echo "ar_has_branch=$AR_HAS_BRANCH" >> "$GITHUB_OUTPUT" echo "ar_has_pr=$AR_HAS_PR" >> "$GITHUB_OUTPUT" + echo "libs_release_tag=$LIBS_RELEASE_TAG" >> "$GITHUB_OUTPUT" echo "libs_version=$LIBS_VERSION" >> "$GITHUB_OUTPUT" - echo "libs_has_commit=$LIBS_HAS_COMMIT" >> "$GITHUB_OUTPUT" - echo "libs_has_branch=$LIBS_HAS_BRANCH" >> "$GITHUB_OUTPUT" + echo "libs_release_id=$LIBS_RELEASE_ID" >> "$GITHUB_OUTPUT" + echo "libs_has_release=$LIBS_HAS_RELEASE" >> "$GITHUB_OUTPUT" + echo "libs_asset_id=$LIBS_ASSET_ID" >> "$GITHUB_OUTPUT" + echo "libs_has_asset=$LIBS_HAS_ASSET" >> "$GITHUB_OUTPUT" + echo "deploy_needed=$DEPLOY_NEEDED" >> "$GITHUB_OUTPUT" fi + diff --git a/tools/combine-artifacts.sh b/tools/combine-artifacts.sh new file mode 100755 index 000000000..8cb1c25e2 --- /dev/null +++ b/tools/combine-artifacts.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -e +mkdir -p out + +libs_folder="out/tools/esp32-arduino-libs" + +files=$(find dist -name 'arduino-esp32-libs-esp*.tar.gz') +for file in $files; do + echo "Extracting $file" + tar zxf $file -C out + cat $libs_folder/versions.txt >> $libs_folder/versions_full.txt +done + +# Merge versions.txt files +awk -i inplace '!seen[$0]++' $libs_folder/versions_full.txt +mv -f $libs_folder/versions_full.txt $libs_folder/versions.txt + +echo "Creating zip file" +cd out/tools && zip -q -r ../../dist/esp32-arduino-libs.zip * && cd ../.. diff --git a/tools/config.sh b/tools/config.sh index 74de3b8c5..79d4feef5 100755 --- a/tools/config.sh +++ b/tools/config.sh @@ -6,7 +6,7 @@ if [ -z $IDF_PATH ]; then fi if [ -z $IDF_BRANCH ]; then - IDF_BRANCH="release/v5.1" + IDF_BRANCH="release/v5.4" fi if [ -z $AR_PR_TARGET_BRANCH ]; then @@ -25,12 +25,12 @@ if [ -z $IDF_TARGET ]; then fi # Owner of the target ESP32 Arduino repository -AR_USER="espressif" +AR_USER="${GITHUB_REPOSITORY_OWNER:-espressif}" # The full name of the repository AR_REPO="$AR_USER/arduino-esp32" IDF_REPO="$AR_USER/esp-idf" -AR_LIBS_REPO="$AR_USER/esp32-arduino-libs" +AR_LIBS_REPO="$AR_USER/esp32-arduino-lib-builder" AR_REPO_URL="https://github.com/$AR_REPO.git" IDF_REPO_URL="https://github.com/$IDF_REPO.git" @@ -45,12 +45,12 @@ AR_COMPS="$AR_ROOT/components" AR_MANAGED_COMPS="$AR_ROOT/managed_components" AR_OUT="$AR_ROOT/out" AR_TOOLS="$AR_OUT/tools" +AR_PATCHES="$AR_ROOT/patches" AR_PLATFORM_TXT="$AR_OUT/platform.txt" AR_GEN_PART_PY="$AR_TOOLS/gen_esp32part.py" AR_SDK="$AR_TOOLS/esp32-arduino-libs/$IDF_TARGET" -PIO_SDK="FRAMEWORK_SDK_DIR, \"$IDF_TARGET\"" +PIOARDUINO_SDK="FRAMEWORK_SDK_DIR, \"$IDF_TARGET\"" TOOLS_JSON_OUT="$AR_TOOLS/esp32-arduino-libs" -IDF_LIBS_DIR="$AR_ROOT/../esp32-arduino-libs" if [ -d "$IDF_PATH" ]; then export IDF_COMMIT=$(git -C "$IDF_PATH" rev-parse --short HEAD) @@ -58,7 +58,7 @@ if [ -d "$IDF_PATH" ]; then fi function get_os(){ - OSBITS=`arch` + OSBITS=`uname -m` if [[ "$OSTYPE" == "linux"* ]]; then if [[ "$OSBITS" == "i686" ]]; then echo "linux32" @@ -99,12 +99,55 @@ if [[ "$AR_OS" == "macos" ]]; then export SSTAT="stat -f %z" fi +function github_get_libs_idf(){ # github_get_libs_idf + local repo_path="$1" + local branch_name="$2" + local message_prefix="$3" + message_prefix=$(echo $message_prefix | sed 's/[]\/$*.^|[]/\\&/g') # Escape special characters + local page=1 + local version_found="" + local libs_version="" + + while [[ "$libs_version" == "" && "$page" -le 3 ]]; do + # Get the latest commit message that matches the prefix and extract the hash from the last commit message + version_found=`curl -s -k -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3.raw+json" "https://api.github.com/repos/$repo_path/commits?sha=$branch_name&per_page=100&page=$page" | \ + jq -r --arg prefix "$message_prefix" '[ .[] | select(.commit.message | test($prefix + " [a-f0-9]{8}")) ][0] | .commit.message' | \ + grep -Eo "$message_prefix [a-f0-9]{8}" | \ + awk 'END {print $NF}'` + if [[ "$version_found" != "" && "$version_found" != "null" ]]; then + libs_version=$version_found + else + page=$((page+1)) + fi + done + + if [ ! "$libs_version" == "" ] && [ ! "$libs_version" == "null" ]; then echo $libs_version; else echo ""; fi +} + function github_commit_exists(){ #github_commit_exists local repo_path="$1" local branch_name="$2" local commit_message="$3" - local commits_found=`curl -s -k -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3.raw+json" "https://api.github.com/repos/$repo_path/commits?sha=$branch_name" | jq -r '.[].commit.message' | grep "$commit_message" | wc -l` - if [ ! "$commits_found" == "" ] && [ ! "$commits_found" == "null" ] && [ ! "$commits_found" == "0" ]; then echo $commits_found; else echo 0; fi + local page=1 + local commits_found=0 + + while [ "$page" -le 3 ]; do + local response=`curl -s -k -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3.raw+json" "https://api.github.com/repos/$repo_path/commits?sha=$branch_name&per_page=100&page=$page"` + + if [[ -z "$response" || "$response" == "[]" ]]; then + break + fi + + local commits=`echo "$response" | jq -r '.[].commit.message' | grep "$commit_message" | wc -l` + if [ "$commits" -gt 0 ]; then + commits_found=1 + break + fi + + page=$((page+1)) + done + + echo $commits_found } function github_last_commit(){ # github_last_commit @@ -132,6 +175,40 @@ function github_pr_exists(){ # github_pr_exists if [ ! "$pr_num" == "" ] && [ ! "$pr_num" == "null" ]; then echo 1; else echo 0; fi } +function github_release_id(){ # github_release_id + local repo_path="$1" + local release_tag="$2" + local release=`curl -s -k -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3.raw+json" "https://api.github.com/repos/$repo_path/releases" | jq --arg release_tag "$release_tag" -r '.[] | select(.tag_name == $release_tag) | .id'` + if [ ! "$release" == "" ] && [ ! "$release" == "null" ]; then echo "$release"; else echo ""; fi +} + +function github_release_asset_id(){ # github_release_asset_id + local repo_path="$1" + local release_id="$2" + local release_file="$3" + local release_asset=`curl -s -k -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3.raw+json" "https://api.github.com/repos/$repo_path/releases/$release_id/assets" | jq --arg release_file "$release_file" -r '.[] | select(.name == $release_file) | .id'` + if [ ! "$release_asset" == "" ] && [ ! "$release_asset" == "null" ]; then echo "$release_asset"; else echo ""; fi +} + +function github_release_asset_upload(){ # github_release_asset_upload + local repo_path="$1" + local release_id="$2" + local release_file_name="$3" + local release_file_path="$4" + local file_extension="${release_file_name##*.}" + local release_asset=`curl -s -k -X POST -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3.raw+json" -H "Content-Type: application/$file_extension" --data-binary "@$release_file_path" "https://uploads.github.com/repos/$repo_path/releases/$release_id/assets?name=$release_file_name" | jq -r '.id'` + if [ ! "$release_asset" == "" ] && [ ! "$release_asset" == "null" ]; then echo "$release_asset"; else echo ""; fi +} + +function github_release_asset_delete(){ # github_release_asset_delete + local repo_path="$1" + local release_asset_id="$2" + local res + local return_code + res=$(curl -s -k -o /dev/null -w "%{http_code}" -X DELETE -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3.raw+json" "https://api.github.com/repos/$repo_path/releases/assets/$release_asset_id") + return_code=$? + if [ "$res" -eq 204 ] && [ "$return_code" -eq 0 ] ; then echo 1; else echo 0; fi +} function git_branch_exists(){ # git_branch_exists diff --git a/tools/config_editor/.gitignore b/tools/config_editor/.gitignore new file mode 100644 index 000000000..a230a78ae --- /dev/null +++ b/tools/config_editor/.gitignore @@ -0,0 +1,2 @@ +.venv/ +__pycache__/ diff --git a/tools/config_editor/README.md b/tools/config_editor/README.md new file mode 100644 index 000000000..9abd93fc7 --- /dev/null +++ b/tools/config_editor/README.md @@ -0,0 +1,40 @@ +# Arduino Static Libraries Configuration Editor + +This is a simple application to configure the static libraries for the ESP32 Arduino core. +It allows the user to select the targets to compile, change the configuration options and compile the libraries. +It has mouse support and can be pre-configured using command line arguments. + +## Requirements + - Python 3.9 or later + - Install the required packages using `pip install -r requirements.txt` + - The requirements from esp32-arduino-lib-builder + +## Troubleshooting + +In some cases, the UI might not look as expected. This can happen due to the terminal emulator not supporting the required features. + +### WSL + +If you are using WSL, it is recommended to use the Windows Terminal to visualize the application. Otherwise, the application layout and colors might not be displayed correctly. +The Windows Terminal can be installed from the Microsoft Store. + +### MacOS + +If you are using MacOS and the application looks weird, check [this guide from Textual](https://textual.textualize.io/FAQ/#why-doesnt-textual-look-good-on-macos) to fix it. + +## Usage + +These command line arguments can be used to pre-configure the application: + +``` +Command line arguments: + -t, --target Comma-separated list of targets to be compiled. + Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2. Default: all except esp32c2 + --copy, --no-copy Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default + -c, --arduino-path Path to arduino-esp32 directory. Default: OS dependent + -A, --arduino-branch Branch of the arduino-esp32 repository to be used. Default: set by the build script + -I, --idf-branch Branch of the ESP-IDF repository to be used. Default: set by the build script + -i, --idf-commit Commit of the ESP-IDF repository to be used. Default: set by the build script + -D, --debug-level Debug level to be set to ESP-IDF. + Choose from: default, none, error, warning, info, debug, verbose. Default: default +``` diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py new file mode 100755 index 000000000..dbe24ca05 --- /dev/null +++ b/tools/config_editor/app.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python + +""" +Arduino Static Libraries Configuration Editor + +This is a simple application to configure the static libraries for the ESP32 Arduino core. +It allows the user to select the targets to compile, change the configuration options and compile the libraries. + +Requires Python 3.9 or later. + +The application is built using the "textual" library, which is a Python library for building text-based user interfaces. + +Note that this application still needs the requirements from esp32-arduino-lib-builder to be installed. + +Command line arguments: + -t, --target Comma-separated list of targets to be compiled. + Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2. Default: all except esp32c2 + --copy, --no-copy Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default + -c, --arduino-path Path to arduino-esp32 directory. Default: OS dependent + -A, --arduino-branch Branch of the arduino-esp32 repository to be used. Default: set by the build script + -I, --idf-branch Branch of the ESP-IDF repository to be used. Default: set by the build script + -i, --idf-commit Commit of the ESP-IDF repository to be used. Default: set by the build script + -D, --debug-level Debug level to be set to ESP-IDF. + Choose from: default, none, error, warning, info, debug, verbose. Default: default + +""" + +import argparse +import json +import os +import platform +import sys + +from pathlib import Path + +try: + from textual.app import App, ComposeResult + from textual.binding import Binding + from textual.containers import VerticalScroll + from textual.screen import Screen + from textual.widgets import Button, Header, Label, Footer +except ImportError: + print("Please install the \"textual\" package before running this script.") + exit(1) + +from settings import SettingsScreen +from editor import EditorScreen +from compile import CompileScreen + +class MainScreen(Screen): + # Main screen class + + # Set the key bindings + BINDINGS = [ + Binding("c", "app.push_screen('compile')", "Compile"), + Binding("e", "app.push_screen('editor')", "Editor"), + Binding("s", "app.push_screen('settings')", "Settings"), + Binding("q", "app.quit", "Quit"), + ] + + def on_button_pressed(self, event: Button.Pressed) -> None: + # Event handler called when a button is pressed + if event.button.id == "compile-button": + print("Compile button pressed") + self.app.push_screen("compile") + elif event.button.id == "settings-button": + print("Settings button pressed") + self.app.push_screen("settings") + elif event.button.id == "editor-button": + print("Editor button pressed") + self.app.push_screen("editor") + elif event.button.id == "quit-button": + print("Quit button pressed") + self.app.exit() + + def compose(self) -> ComposeResult: + # Compose main menu + yield Header() + with VerticalScroll(id="main-menu-container"): + yield Label("ESP32 Arduino Static Libraries Configuration Editor", id="main-menu-title") + yield Button("Compile Static Libraries", id="compile-button", classes="main-menu-button") + yield Button("Sdkconfig Editor", id="editor-button", classes="main-menu-button") + yield Button("Settings", id="settings-button", classes="main-menu-button") + yield Button("Quit", id="quit-button", classes="main-menu-button") + yield Footer() + + def on_mount(self) -> None: + # Event handler called when the app is mounted for the first time + self.sub_title = "Main Menu" + print("Main screen mounted.") + +class ConfigEditorApp(App): + # Main application class + + # Set the root and script paths + SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__)) + ROOT_PATH = os.path.abspath(os.path.join(SCRIPT_PATH, "..", "..")) + + # Set the application options + supported_targets = [] + setting_enable_copy = True + + # Options to be set by the command line arguments + setting_target = "" + setting_arduino_path = "" + setting_output_permissions = "" + setting_arduino_branch = "" + setting_idf_branch = "" + setting_idf_commit = "" + setting_debug_level = "" + + ENABLE_COMMAND_PALETTE = False + CSS_PATH = "style.tcss" + SCREENS = { + "main": MainScreen, + "settings": SettingsScreen, + "compile": CompileScreen, + "editor": EditorScreen, + } + + def on_mount(self) -> None: + print("Application mounted. Initial options:") + print("Python version: " + sys.version) + print("Root path: " + self.ROOT_PATH) + print("Script path: " + self.SCRIPT_PATH) + print("Supported Targets: " + ", ".join(self.supported_targets)) + print("Default targets: " + self.setting_target) + print("Enable Copy: " + str(self.setting_enable_copy)) + print("Arduino Path: " + str(self.setting_arduino_path)) + print("Arduino Branch: " + str(self.setting_arduino_branch)) + print("IDF Branch: " + str(self.setting_idf_branch)) + print("IDF Commit: " + str(self.setting_idf_commit)) + print("IDF Debug Level: " + str(self.setting_debug_level)) + self.title = "Configurator" + self.push_screen("main") + +def arduino_default_path(): + sys_name = platform.system() + home = str(Path.home()) + if sys_name == "Linux": + return os.path.join(home, "Arduino", "hardware", "espressif", "esp32") + else: # Windows and MacOS + return os.path.join(home, "Documents", "Arduino", "hardware", "espressif", "esp32") + +def check_arduino_path(path): + return os.path.isdir(path) + +def main() -> None: + # Set the PYTHONUNBUFFERED environment variable to "1" to disable the output buffering + os.environ['PYTHONUNBUFFERED'] = "1" + + # Check Python version + if sys.version_info < (3, 9): + print("This script requires Python 3.9 or later") + exit(1) + + app = ConfigEditorApp() + + # List of tuples for the target choices containing the target name and if it is enabled by default + target_choices = [] + + # Parse build JSON file + build_json_path = os.path.join(app.ROOT_PATH, "configs", "builds.json") + if os.path.isfile(build_json_path): + with open(build_json_path, "r") as build_json_file: + build_json = json.load(build_json_file) + for target in build_json["targets"]: + try: + default = False if target["skip"] else True + except: + default = True + target_choices.append((target["target"], default)) + else: + print("Error: configs/builds.json file not found.") + exit(1) + + target_choices.sort(key=lambda x: x[0]) + + parser = argparse.ArgumentParser(description="Configure and compile the ESP32 Arduino static libraries") + + parser.add_argument("-t", "--target", + metavar="", + type=str, + default="default", + required=False, + help="Comma-separated list of targets to be compiled. Choose from: " + ", ".join([x[0] for x in target_choices]) + + ". Default: All except " + ", ".join([x[0] for x in target_choices if not x[1]])) + + parser.add_argument("--copy", + type=bool, + action=argparse.BooleanOptionalAction, + default=True, + required=False, + help="Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default") + + parser.add_argument("-c", "--arduino-path", + metavar="", + type=str, + default=arduino_default_path(), + required=False, + help="Path to arduino-esp32 directory. Default: " + arduino_default_path()) + + parser.add_argument("--output-permissions", + metavar="", + type=str, + default="", + required=False, + help=argparse.SUPPRESS) # Hidden option. It is only supposed to be used by the docker container + + parser.add_argument("-A", "--arduino-branch", + metavar="", + type=str, + default="", + required=False, + help="Branch of the arduino-esp32 repository to be used") + + parser.add_argument("-I", "--idf-branch", + metavar="", + type=str, + default="", + required=False, + help="Branch of the ESP-IDF repository to be used") + + parser.add_argument("-i", "--idf-commit", + metavar="", + type=str, + default="", + required=False, + help="Commit of the ESP-IDF repository to be used") + + debug_level_choices = ("default", "none", "error", "warning", "info", "debug", "verbose") + parser.add_argument("-D", "--debug-level", + metavar="", + type=str, + default="default", + choices=debug_level_choices, + required=False, + help="Debug level to be set to ESP-IDF. Choose from: " + ", ".join(debug_level_choices)) + + args = parser.parse_args() + + # Force targets to be lower case + args.target = args.target.lower() + + # Check if the target is valid + if args.target == "default": + args.target = ",".join([x[0] for x in target_choices if x[1]]) + elif args.target == "all": + args.target = ",".join([x[0] for x in target_choices]) + + app.supported_targets = [x[0] for x in target_choices] + + for target in args.target.split(","): + if target not in app.supported_targets: + print("Invalid target: " + target) + exit(1) + + app.setting_target = args.target + + # Check if the Arduino path is valid + if args.copy: + if check_arduino_path(args.arduino_path): + app.setting_enable_copy = True + elif args.arduino_path == arduino_default_path(): + print("Warning: Default Arduino path not found. Disabling copy to Arduino.") + app.setting_enable_copy = False + elif args.arduino_path == "/arduino-esp32": # Docker mount point + print("Warning: Docker mount point not found. Disabling copy to Arduino.") + app.setting_enable_copy = False + else: + print("Error: Invalid path to Arduino core: " + os.path.abspath(args.arduino_path)) + exit(1) + else: + app.setting_enable_copy = False + + # Set the other options + app.setting_arduino_path = os.path.abspath(args.arduino_path) + app.setting_output_permissions = args.output_permissions + app.setting_arduino_branch = args.arduino_branch + app.setting_idf_branch = args.idf_branch + app.setting_idf_commit = args.idf_commit + app.setting_debug_level = args.debug_level + + # Change to the root directory of the app to the root of the project + os.chdir(app.ROOT_PATH) + + # Main function to run the app + app.run() + + # Propagate the exit code from the app + exit(app.return_code or 0) + +if __name__ == "__main__": + # If this script is run directly, start the app + main() diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py new file mode 100644 index 000000000..efb812174 --- /dev/null +++ b/tools/config_editor/compile.py @@ -0,0 +1,203 @@ +import sys +import subprocess +import os +import re + +from rich.console import RenderableType + +from textual import on, work +from textual.app import ComposeResult +from textual.binding import Binding +from textual.events import ScreenResume +from textual.containers import Container +from textual.screen import Screen +from textual.widgets import Header, Static, RichLog, Button, Footer + +class CompileScreen(Screen): + # Compile screen + + # Set the key bindings + BINDINGS = [ + Binding("escape", "back", "Back") + ] + + # Child process running the libraries compilation + child_process = None + + log_widget: RichLog + button_widget: Button + + def action_back(self) -> None: + self.workers.cancel_all() + if self.child_process: + # Terminate the child process if it is running + print("Terminating child process") + self.child_process.terminate() + try: + self.child_process.stdout.close() + self.child_process.stderr.close() + except: + pass + self.child_process.wait() + self.dismiss() + + def print_output(self, renderable: RenderableType, style=None) -> None: + # Print output to the RichLog widget + if style is None: + self.log_widget.write(renderable) + else: + # Check the available styles at https://rich.readthedocs.io/en/stable/style.html + self.log_widget.write("[" + str(style) + "]" + renderable) + + def print_error(self, error: str) -> None: + # Print error to the RichLog widget + self.log_widget.write("[b bright_red]" + error) + self.button_widget.add_class("-error") + #print("Error: " + error) # For debugging + + def print_success(self, message: str) -> None: + # Print success message to the RichLog widget + self.log_widget.write("[b bright_green]" + message) + self.button_widget.add_class("-success") + #print("Success: " + message) # For debugging + + def print_warning(self, message: str) -> None: + # Print warning message to the RichLog widget + self.log_widget.write("[b bright_yellow]" + message) + #print("Warning: " + message) # For debugging + + def print_info(self, message: str) -> None: + # Print info message to the RichLog widget + self.log_widget.write("[b bright_cyan]" + message) + #print("Info: " + message) # For debugging + + @work(name="compliation_worker", group="compilation", exclusive=True, thread=True) + def compile_libs(self) -> None: + # Compile the libraries + print("Starting compilation process") + + label = self.query_one("#compile-title", Static) + self.child_process = None + + if not self.app.setting_target: + self.print_error("No target selected") + label.update("No target selected") + return + elif self.app.setting_target == ",".join(self.app.supported_targets): + target = "all targets" + else: + target = self.app.setting_target.replace(",", ", ").upper() + + label.update("Compiling for " + target) + self.print_info("======== Compiling for " + target + " ========") + + command = ["./build.sh", "-t", self.app.setting_target, "-D", self.app.setting_debug_level] + + #command.append("--help") # For testing output without compiling + + if self.app.setting_enable_copy: + if os.path.isdir(self.app.setting_arduino_path): + command.extend(["-c", self.app.setting_arduino_path]) + else: + self.print_error("Invalid path to Arduino core: " + self.app.setting_arduino_path) + label.update("Invalid path to Arduino core") + return + + if self.app.setting_arduino_branch: + command.extend(["-A", self.app.setting_arduino_branch]) + + if self.app.setting_idf_branch: + command.extend(["-I", self.app.setting_idf_branch]) + + if self.app.setting_idf_commit: + command.extend(["-i", self.app.setting_idf_commit]) + + self.print_info("Running: " + " ".join(command) + "\n") + self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + try: + for output in self.child_process.stdout: + if output == '' and self.child_process.poll() is not None: + break + if output: + self.print_output(output.strip()) # Update RichLog widget with subprocess output + self.child_process.stdout.close() + except Exception as e: + print("Error reading child process output: " + str(e)) + print("Process might have terminated") + + if not self.child_process: + self.print_error("Compilation failed for " + target + "Child process failed to start") + label.update("Compilation failed for " + target + "Child process failed to start") + return + else: + self.child_process.wait() + + if self.child_process.returncode != 0: + self.print_error("Compilation failed for " + target + ". Return code: " + str(self.child_process.returncode)) + self.print_error("Errors:") + try: + for error in self.child_process.stderr: + if error: + self.print_error(error.strip()) + self.child_process.stderr.close() + except Exception as e: + print("Error reading child process errors: " + str(e)) + label.update("Compilation failed for " + target) + else: + if self.app.setting_output_permissions: + regex = r"^[1-9][0-9]*:[1-9][0-9]*$" # Regex to match the uid:gid format. Note that 0:0 (root) is not allowed + if re.match(regex, self.app.setting_output_permissions): + print_info("Setting permissions to: " + self.app.setting_output_permissions) + chown_process = None + try: + chown_process = subprocess.run(["chown", "-R", self.app.setting_output_permissions, self.app.setting_arduino_path]) + chown_process.wait() + except Exception as e: + print("Error changing permissions: " + str(e)) + + if chown_process and chown_process.returncode != 0: + self.print_error("Error changing permissions") + self.print_error("Please change the ownership of generated files manually") + else: + self.print_success("Permissions changed successfully") + elif self.app.setting_output_permissions == "0:0": + self.print_warning("Permissions settings are set to root (0:0)") + self.print_warning("Please change the ownership of generated files manually") + self.print_warning("If you are compiling for Windows, you may ignore this warning") + else: + self.print_error("Invalid permissions format: " + self.app.setting_output_permissions) + self.print_error("Please change the ownership of generated files manually") + + self.print_success("Compilation successful for " + target) + label.update("Compilation successful for " + target) + + def on_button_pressed(self, event: Button.Pressed) -> None: + # Event handler called when a button is pressed + self.action_back() + + @on(ScreenResume) + def on_resume(self) -> None: + # Event handler called every time the screen is activated + print("Compile screen resumed. Clearing logs and starting compilation process") + self.button_widget.remove_class("-error") + self.button_widget.remove_class("-success") + self.log_widget.clear() + self.log_widget.focus() + self.compile_libs() + + def compose(self) -> ComposeResult: + # Compose the compilation screen + yield Header() + with Container(id="compile-log-container"): + self.log_widget = RichLog(markup=True, id="compile-log") + yield self.log_widget + with Container(id="compile-status-container"): + yield Static("Compiling for ...", id="compile-title") + self.button_widget = Button("Back", id="compile-back-button") + yield self.button_widget + yield Footer() + + def on_mount(self) -> None: + # Event handler called when the screen is mounted + print("Compile screen mounted") + self.sub_title = "Compilation" diff --git a/tools/config_editor/editor.py b/tools/config_editor/editor.py new file mode 100644 index 000000000..87217f49d --- /dev/null +++ b/tools/config_editor/editor.py @@ -0,0 +1,86 @@ +import os + +from textual import on +from textual.app import ComposeResult +from textual.binding import Binding +from textual.containers import Container, VerticalScroll, Horizontal +from textual.screen import Screen +from textual.events import ScreenResume +from textual.widgets import DirectoryTree, Header, TextArea, Button, Footer + +class EditorScreen(Screen): + # Configuration file editor screen + + # Set the key bindings + BINDINGS = [ + Binding("ctrl+s", "save", "Save", priority=True), + Binding("escape", "app.pop_screen", "Discard") + ] + + # Current file being edited + current_file = "" + + def action_save(self) -> None: + code_view = self.query_one("#code", TextArea) + current_text = code_view.text + try: + file = open(self.curent_file, "w") + file.write(current_text) + file.close() + except Exception: + print("Error saving file: " + self.curent_file) + self.sub_title = "ERROR" + else: + print("File saved: " + self.curent_file) + self.sub_title = self.curent_file + self.dismiss() + + def on_button_pressed(self, event: Button.Pressed) -> None: + # Event handler called when a button is pressed + if event.button.id == "save-editor-button" and self.curent_file != "": + print("Save button pressed. Trying to save file: " + self.curent_file) + self.action_save() + elif event.button.id == "cancel-editor-button": + print("Cancel button pressed") + self.dismiss() + + def on_directory_tree_file_selected(self, event: DirectoryTree.FileSelected) -> None: + # Called when the user click a file in the directory tree + event.stop() + code_view = self.query_one("#code", TextArea) + code_view.clear() + self.curent_file = str(event.path) + try: + print("Opening file: " + self.curent_file) + file = open(self.curent_file, "r") + file_content = file.read() + file.close() + except Exception: + print("Error opening file: " + self.curent_file) + self.sub_title = "ERROR" + else: + print("File opened: " + self.curent_file) + code_view.insert(file_content) + self.sub_title = self.curent_file + + @on(ScreenResume) + def on_resume(self) -> None: + # Event handler called every time the screen is activated + print("Editor screen resumed. Clearing code view") + self.sub_title = "Select a file" + self.query_one(DirectoryTree).focus() + self.query_one(TextArea).clear() + self.curent_file = "" + + def compose(self) -> ComposeResult: + # Compose editor screen + path = os.path.join(self.app.ROOT_PATH, 'configs') + yield Header() + with Container(): + yield DirectoryTree(path, id="tree-view") + with VerticalScroll(id="code-view"): + yield TextArea.code_editor("", id="code") + with Horizontal(id="editor-buttons-container"): + yield Button("Save", id="save-editor-button", classes="editor-button") + yield Button("Cancel", id="cancel-editor-button", classes="editor-button") + yield Footer() diff --git a/tools/config_editor/requirements.txt b/tools/config_editor/requirements.txt new file mode 100644 index 000000000..f3cac7be0 --- /dev/null +++ b/tools/config_editor/requirements.txt @@ -0,0 +1 @@ +textual==0.79.0 diff --git a/tools/config_editor/settings.py b/tools/config_editor/settings.py new file mode 100644 index 000000000..c92358374 --- /dev/null +++ b/tools/config_editor/settings.py @@ -0,0 +1,149 @@ +import math + +from textual import on +from textual.app import ComposeResult +from textual.binding import Binding +from textual.containers import VerticalScroll, Container, Horizontal +from textual.screen import Screen +from textual.events import ScreenResume +from textual.widgets import Header, Button, Switch, Label, Footer, Checkbox + +from widgets import LabelledInput, LabelledSelect + +class SettingsScreen(Screen): + # Settings screen + + # Set the key bindings + BINDINGS = [ + Binding("s", "save", "Save"), + Binding("escape", "app.pop_screen", "Discard") + ] + + enable_copy_switch: Switch + arduino_path_input: LabelledInput + arduino_branch_input: LabelledInput + idf_branch_input: LabelledInput + idf_commit_input: LabelledInput + idf_debug_select: LabelledSelect + + def action_save(self) -> None: + checkboxes = self.query(Checkbox) + self.app.setting_target = "" + for checkbox in checkboxes: + if checkbox.value: + if self.app.setting_target: + self.app.setting_target += "," + self.app.setting_target += checkbox.id.replace("-checkbox", "") + print("Target setting updated: " + self.app.setting_target) + + self.app.setting_enable_copy = self.enable_copy_switch.value + print("Enable copy setting updated: " + str(self.app.setting_enable_copy)) + + if self.enable_copy_switch.value: + self.app.setting_arduino_path = self.arduino_path_input.get_input_value() + print("Arduino path setting updated: " + self.app.setting_arduino_path) + + self.app.setting_arduino_branch = self.arduino_branch_input.get_input_value() + print("Arduino branch setting updated: " + self.app.setting_arduino_branch) + + self.app.setting_idf_branch = self.idf_branch_input.get_input_value() + print("IDF branch setting updated: " + self.app.setting_idf_branch) + + self.app.setting_idf_commit = self.idf_commit_input.get_input_value() + print("IDF commit setting updated: " + self.app.setting_idf_commit) + + self.app.setting_debug_level = self.idf_debug_select.get_select_value() + print("Debug level setting updated: " + self.app.setting_debug_level) + + def on_button_pressed(self, event: Button.Pressed) -> None: + # Event handler called when a button is pressed + if event.button.id == "save-settings-button": + print("Save button pressed") + self.action_save() + elif event.button.id == "cancel-settings-button": + print("Cancel button pressed") + self.dismiss() + + @on(ScreenResume) + def on_resume(self) -> None: + # Event handler called every time the screen is activated + print("Settings screen resumed. Updating settings.") + targets = self.app.setting_target.split(",") + checkboxes = self.query(Checkbox) + for checkbox in checkboxes: + checkbox.value = False + if checkbox.id.replace("-checkbox", "") in targets: + checkbox.value = True + self.enable_copy_switch.value = self.app.setting_enable_copy + if self.app.setting_enable_copy: + self.arduino_path_input.visible = True + else: + self.arduino_path_input.visible = False + self.arduino_path_input.set_input_value(self.app.setting_arduino_path) + self.arduino_branch_input.set_input_value(self.app.setting_arduino_branch) + self.idf_branch_input.set_input_value(self.app.setting_idf_branch) + self.idf_commit_input.set_input_value(self.app.setting_idf_commit) + self.idf_debug_select.set_select_value(self.app.setting_debug_level) + + def on_switch_changed(self, event: Switch.Changed) -> None: + # Event handler called when a switch is changed + if event.switch.id == "enable-copy-switch": + if event.switch.value: + self.arduino_path_input.visible = True + else: + self.arduino_path_input.visible = False + + def compose(self) -> ComposeResult: + # Compose the target selection screen + yield Header() + with VerticalScroll(id="settings-scroll-container"): + + yield Label("Compilation Targets", id="settings-target-label") + with Container(id="settings-target-container"): + for target in self.app.supported_targets: + yield Checkbox(target.upper(), id=target + "-checkbox") + + with Horizontal(classes="settings-switch-container"): + self.enable_copy_switch = Switch(value=self.app.setting_enable_copy, id="enable-copy-switch") + yield self.enable_copy_switch + + yield Label("Copy to arduino-esp32 after compilation") + + self.arduino_path_input = LabelledInput("Arduino-esp32 Path", placeholder="Path to your arduino-esp32 installation", value=self.app.setting_arduino_path, id="arduino-path-input") + yield self.arduino_path_input + + self.arduino_branch_input = LabelledInput("Arduino-esp32 Branch", placeholder="Leave empty to use default", value=self.app.setting_arduino_branch, id="arduino-branch-input") + yield self.arduino_branch_input + + self.idf_branch_input = LabelledInput("ESP-IDF Branch", placeholder="Leave empty to use default", value=self.app.setting_idf_branch, id="idf-branch-input") + yield self.idf_branch_input + + self.idf_commit_input = LabelledInput("ESP-IDF Commit", placeholder="Leave empty to use default", value=self.app.setting_idf_commit, id="idf-commit-input") + yield self.idf_commit_input + + debug_options = [ + ("Default", "default"), + ("None", "none"), + ("Error", "error"), + ("Warning", "warning"), + ("Info", "info"), + ("Debug", "debug"), + ("Verbose", "verbose") + ] + self.idf_debug_select = LabelledSelect("ESP-IDF Debug Level", debug_options, allow_blank=False, id="idf-debug-select") + yield self.idf_debug_select + + with Horizontal(id="settings-button-container"): + yield Button("Save", id="save-settings-button", classes="settings-button") + yield Button("Cancel", id="cancel-settings-button", classes="settings-button") + yield Footer() + + def on_mount(self) -> None: + # Event handler called when the screen is mounted for the first time + self.sub_title = "Settings" + target_container = self.query_one("#settings-target-container") + # Height needs to be 3 for each row of targets + 1 + height_value = str(int(math.ceil(len(self.app.supported_targets) / int(target_container.styles.grid_size_columns)) * 3 + 1)) + print("Target container height: " + height_value) + target_container.styles.height = height_value + print("Settings screen mounted") diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss new file mode 100644 index 000000000..4359e58e0 --- /dev/null +++ b/tools/config_editor/style.tcss @@ -0,0 +1,202 @@ +# General + +Screen { + background: $surface-darken-1; +} + +Button { + width: auto; + min-width: 16; + height: auto; + color: $text; + border: none; + background: #038c8c; + border-top: tall #026868; + border-bottom: tall #6ab8b8; + text-align: center; + content-align: center middle; + text-style: bold; + + &:focus { + text-style: bold reverse; + } + &:hover { + border-top: tall #014444; + border-bottom: tall #3d8080; + background: #025b5b; + color: $text; + } + &.-active { + background: #025b5b; + border-bottom: tall #3d8080; + border-top: tall #014444; + tint: $background 30%; + } + + &.-success { + background: $success; + color: $text; + border-top: tall $success-lighten-2; + border-bottom: tall $success-darken-3; + + &:hover { + background: $success-darken-2; + color: $text; + border-top: tall $success; + } + + &.-active { + background: $success; + border-bottom: tall $success-lighten-2; + border-top: tall $success-darken-2; + } + } + + &.-error { + background: $error; + color: $text; + border-top: tall $error-lighten-2; + border-bottom: tall $error-darken-3; + + &:hover { + background: $error-darken-1; + color: $text; + border-top: tall $error; + } + + &.-active { + background: $error; + border-bottom: tall $error-lighten-2; + border-top: tall $error-darken-2; + } + } +} + +# Main Screen + +.main-menu-button { + margin-bottom: 1; + min-width: 100%; + max-width: 0.4fr; +} + +#main-menu-container { + align: center middle; + width: 1fr; +} + +#main-menu-title { + text-align: center; + margin-bottom: 4; + text-style: bold; + color: auto; + width: 0.4fr; +} + +# Compile Screen + +#compile-status-container { + layout: horizontal; + padding: 0 2; + height: 4; +} + +#compile-title { + dock: left; +} + +#compile-back-button { + dock: right; +} + +#compile-log { + background: $surface; + padding: 0 1 1 1; + margin: 1 2; +} + +# Settings Screen + +#settings-scroll-container { + padding: 1; +} + +#settings-button-container { + width: 100%; + max-height: 20%; + min-height: 5; + align: center middle; +} + +#settings-target-label { + margin-left: 1; +} + +#settings-target-container { + layout: grid; + grid-size: 4; +} + +#settings-target-container Checkbox { + width: 100%; + margin-right: -1; +} + +.settings-button { + margin: 1; + min-width: 100%; + max-width: 0.2fr; + align: center middle; +} + +.settings-switch-container { + height: 4; +} + +.settings-switch-container Switch { + margin-right: 2; +} + +.settings-switch-container Label { + margin-top: 1; +} + +# Editor Screen + +#tree-view { + display: none; + scrollbar-gutter: stable; + overflow: auto; + width: auto; + height: 100%; + dock: left; + display: block; + max-width: 50%; +} + +#code-view { + overflow: auto scroll; + min-width: 100%; +} + +#code { + width: 100%; +} + +.editor-button { + width: 20%; +} + +#save-editor-button { + dock: left; + margin: 1; +} + +#cancel-editor-button { + dock: right; + margin: 1 3; +} + +#editor-buttons-container { + height: 5; +} diff --git a/tools/config_editor/widgets.py b/tools/config_editor/widgets.py new file mode 100644 index 000000000..afec3297f --- /dev/null +++ b/tools/config_editor/widgets.py @@ -0,0 +1,95 @@ +from textual.widget import Widget + +from textual.widgets import Input, Label, Select + +class LabelledInput(Widget): + DEFAULT_CSS = """ + LabelledInput { + height: 4; + margin-bottom: 1; + } + LabelledInput Label { + padding-left: 1; + } + """ + + label_widget: Label + input_widget: Input + + def set_input_value(self, value): + self.input_widget.value = value + + def get_input_value(self): + return self.input_widget.value + + def __init__(self, + label, + *, + placeholder="", + value="", + name=None, + id=None, + classes=None, + disabled=False): + super().__init__(name=name, id=id, classes=classes, disabled=disabled) + self.__label = label + self.__placeholder = placeholder + self.__init_value = value + + def compose(self): + self.label_widget = Label(f"{self.__label}:") + self.input_widget = Input(placeholder=self.__placeholder, value=self.__init_value) + yield self.label_widget + yield self.input_widget + + +class LabelledSelect(Widget): + DEFAULT_CSS = """ + LabelledSelect { + height: 4; + margin-bottom: 1; + } + LabelledSelect Label { + padding-left: 1; + } + """ + + label_widget: Label + select_widget: Select + + def set_select_options(self, options): + self.__options = options + self.select_widget.options = options + + def get_select_options(self): + return self.__options + + def set_select_value(self, value): + self.select_widget.value = value + + def get_select_value(self): + return self.select_widget.value + + def __init__(self, + label, + options, + *, + prompt="Select", + allow_blank=True, + value=Select.BLANK, + name=None, + id=None, + classes=None, + disabled=False): + super().__init__(name=name, id=id, classes=classes, disabled=disabled) + self.__label = label + self.__options = options + self.__init_value = value + self.__prompt = prompt + self.__allow_blank = allow_blank + + def compose(self): + self.label_widget = Label(f"{self.__label}:") + self.select_widget = Select(options=self.__options, value=self.__init_value, prompt=self.__prompt, allow_blank=self.__allow_blank) + yield self.label_widget + yield self.select_widget diff --git a/tools/copy-libs.sh b/tools/copy-libs.sh index 960120a16..7c302ab66 100755 --- a/tools/copy-libs.sh +++ b/tools/copy-libs.sh @@ -36,13 +36,20 @@ fi if [ -e "$AR_SDK/$MEMCONF" ]; then rm -rf "$AR_SDK/$MEMCONF" fi -if [ -e "$AR_SDK/platformio-build.py" ]; then - rm -rf "$AR_SDK/platformio-build.py" +if [ -e "$AR_SDK/pioarduino-build.py" ]; then + rm -rf "$AR_SDK/pioarduino-build.py" fi + mkdir -p "$AR_SDK" +mkdir -p "$AR_SDK/lib" function get_actual_path(){ - p="$PWD"; cd "$1"; r="$PWD"; cd "$p"; echo "$r"; + d="$1"; + if [ -d "$d" ]; then + p="$PWD"; cd "$d"; r="$PWD"; cd "$p"; echo "$r"; + else + echo ""; + fi } # @@ -56,6 +63,8 @@ AS_FLAGS="" INCLUDES="" DEFINES="" +EXCLUDE_LIBS=";" + LD_FLAGS="" LD_LIBS="" LD_LIB_FILES="" @@ -63,13 +72,31 @@ LD_LIBS_SEARCH="" LD_SCRIPTS="" LD_SCRIPT_DIRS="" -PIO_CC_FLAGS="" -PIO_C_FLAGS="" -PIO_CXX_FLAGS="" -PIO_AS_FLAGS="" -PIO_LD_FLAGS="" -PIO_LD_FUNCS="" -PIO_LD_SCRIPTS="" +PIOARDUINO_CC_FLAGS="" +PIOARDUINO_C_FLAGS="" +PIOARDUINO_CXX_FLAGS="" +PIOARDUINO_AS_FLAGS="" +PIOARDUINO_LD_FLAGS="" +PIOARDUINO_LD_FUNCS="" +PIOARDUINO_LD_SCRIPTS="" + +TOOLCHAIN_PREFIX="" +if [ "$IS_XTENSA" = "y" ]; then + TOOLCHAIN="xtensa-$IDF_TARGET-elf" +else + TOOLCHAIN="riscv32-esp-elf" +fi + +# copy zigbee + zboss lib +if [ -d "managed_components/espressif__esp-zigbee-lib/lib/$IDF_TARGET/" ]; then + cp -r "managed_components/espressif__esp-zigbee-lib/lib/$IDF_TARGET"/* "$AR_SDK/lib/" + EXCLUDE_LIBS+="esp_zb_api.ed;esp_zb_api.zczr;" +fi + +if [ -d "managed_components/espressif__esp-zboss-lib/lib/$IDF_TARGET/" ]; then + cp -r "managed_components/espressif__esp-zboss-lib/lib/$IDF_TARGET"/* "$AR_SDK/lib/" + EXCLUDE_LIBS+="zboss_stack.ed;zboss_stack.zczr;zboss_port.native;zboss_port.native.debug;zboss_port.remote;zboss_port.remote.debug;" +fi #collect includes, defines and c-flags str=`cat build/compile_commands.json | grep arduino-lib-builder-gcc.c | grep command | cut -d':' -f2 | cut -d',' -f1` @@ -95,13 +122,13 @@ for item in "${@:2:${#@}-5}"; do INCLUDES+="$item " fi elif [ "$prefix" = "-D" ]; then - if [[ "${item:2:7}" != "ARDUINO" ]] && [[ "$item" != "-DESP32" ]]; then #skip ARDUINO defines + if [[ "${item:2:7}" != "ARDUINO" ]] && [[ "$item" != "-DESP32=ESP32" ]]; then #skip ARDUINO defines DEFINES+="$item " fi elif [ "$prefix" = "-O" ]; then - PIO_CC_FLAGS+="$item " + PIOARDUINO_CC_FLAGS+="$item " elif [[ "$item" != "-Wall" && "$item" != "-Werror=all" && "$item" != "-Wextra" ]]; then - if [[ "${item:0:23}" != "-mfix-esp32-psram-cache" && "${item:0:18}" != "-fmacro-prefix-map" && "${item:0:20}" != "-fdiagnostics-color=" ]]; then + if [[ "${item:0:23}" != "-mfix-esp32-psram-cache" && "${item:0:18}" != "-fmacro-prefix-map" && "${item:0:20}" != "-fdiagnostics-color=" && "${item:0:19}" != "-fdebug-prefix-map=" ]]; then C_FLAGS+="$item " fi fi @@ -115,12 +142,12 @@ set -- $str for item in "${@:2:${#@}-5}"; do prefix="${item:0:2}" if [[ "$prefix" != "-I" && "$prefix" != "-D" && "$item" != "-Wall" && "$item" != "-Werror=all" && "$item" != "-Wextra" && "$prefix" != "-O" ]]; then - if [[ "${item:0:23}" != "-mfix-esp32-psram-cache" && "${item:0:18}" != "-fmacro-prefix-map" && "${item:0:20}" != "-fdiagnostics-color=" ]]; then + if [[ "${item:0:23}" != "-mfix-esp32-psram-cache" && "${item:0:18}" != "-fmacro-prefix-map" && "${item:0:20}" != "-fdiagnostics-color=" && "${item:0:19}" != "-fdebug-prefix-map=" ]]; then AS_FLAGS+="$item " if [[ $C_FLAGS == *"$item"* ]]; then - PIO_CC_FLAGS+="$item " + PIOARDUINO_CC_FLAGS+="$item " else - PIO_AS_FLAGS+="$item " + PIOARDUINO_AS_FLAGS+="$item " fi fi fi @@ -134,10 +161,10 @@ set -- $str for item in "${@:2:${#@}-5}"; do prefix="${item:0:2}" if [[ "$prefix" != "-I" && "$prefix" != "-D" && "$item" != "-Wall" && "$item" != "-Werror=all" && "$item" != "-Wextra" && "$prefix" != "-O" ]]; then - if [[ "${item:0:23}" != "-mfix-esp32-psram-cache" && "${item:0:18}" != "-fmacro-prefix-map" && "${item:0:20}" != "-fdiagnostics-color=" ]]; then + if [[ "${item:0:23}" != "-mfix-esp32-psram-cache" && "${item:0:18}" != "-fmacro-prefix-map" && "${item:0:20}" != "-fdiagnostics-color=" && "${item:0:19}" != "-fdebug-prefix-map=" ]]; then CPP_FLAGS+="$item " - if [[ $PIO_CC_FLAGS != *"$item"* ]]; then - PIO_CXX_FLAGS+="$item " + if [[ $PIOARDUINO_CC_FLAGS != *"$item"* ]]; then + PIOARDUINO_CXX_FLAGS+="$item " fi fi fi @@ -145,8 +172,8 @@ done set -- $C_FLAGS for item; do - if [[ $PIO_CC_FLAGS != *"$item"* ]]; then - PIO_C_FLAGS+="$item " + if [[ $PIOARDUINO_CC_FLAGS != *"$item"* ]]; then + PIOARDUINO_C_FLAGS+="$item " fi done @@ -173,7 +200,7 @@ else fi if [ "$IDF_TARGET" = "esp32" ]; then LD_SCRIPTS+="-T esp32.rom.redefined.ld " - PIO_LD_SCRIPTS+="esp32.rom.redefined.ld " + PIOARDUINO_LD_SCRIPTS+="esp32.rom.redefined.ld " fi set -- $str for item; do @@ -193,20 +220,22 @@ for item; do add_next=1 LD_FLAGS+="$item " elif [ "${item:0:2}" = "-l" ]; then # -l[lib_name] - LD_LIBS+="$item " - exclude_libs=";m;c;gcc;stdc++;" short_name="${item:2}" - if [[ $exclude_libs != *";$short_name;"* && $LD_LIBS_SEARCH != *"lib$short_name.a"* ]]; then - LD_LIBS_SEARCH+="lib$short_name.a " - #echo "lib add: $item" + if [[ $EXCLUDE_LIBS != *";$short_name;"* ]]; then + LD_LIBS+="$item " + exclude_libs=";m;c;gcc;stdc++;" + if [[ $exclude_libs != *";$short_name;"* && $LD_LIBS_SEARCH != *"lib$short_name.a"* ]]; then + LD_LIBS_SEARCH+="lib$short_name.a " + #echo "1. lib add: $item" + fi fi elif [ "$item" = "-o" ]; then add_next=0 is_script=0 is_dir=0 - elif [[ "${item:0:23}" != "-mfix-esp32-psram-cache" && "${item:0:18}" != "-fmacro-prefix-map" && "${item:0:17}" != "-Wl,--start-group" && "${item:0:15}" != "-Wl,--end-group" ]]; then + elif [[ "${item:0:23}" != "-mfix-esp32-psram-cache" && "${item:0:18}" != "-fmacro-prefix-map" && "${item:0:19}" != "-fdebug-prefix-map=" && "${item:0:17}" != "-Wl,--start-group" && "${item:0:15}" != "-Wl,--end-group" ]]; then LD_FLAGS+="$item " - PIO_LD_FLAGS+="$item " + PIOARDUINO_LD_FLAGS+="$item " fi fi else @@ -218,10 +247,10 @@ for item; do elif [ "$is_script" = "1" ]; then is_script=0 LD_SCRIPTS+="$item " - PIO_LD_SCRIPTS+="$item " + PIOARDUINO_LD_SCRIPTS+="$item " else LD_FLAGS+="$item " - PIO_LD_FUNCS+="$item " + PIOARDUINO_LD_FUNCS+="$item " fi else if [ "${item:${#item}-2:2}" = ".a" ]; then @@ -237,30 +266,38 @@ for item; do if [[ $LD_LIB_FILES != *"$item"* ]]; then # do we already have lib with the same name? if [[ $LD_LIBS != *"-l$lname"* ]]; then - # echo "collecting lib '$lname' and file: $item" - LD_LIB_FILES+="$item " - LD_LIBS+="-l$lname " + if [[ $EXCLUDE_LIBS != *";$lname;"* ]]; then + #echo "2. collecting lib '$lname' and file: $item" + LD_LIB_FILES+="$item " + LD_LIBS+="-l$lname " + fi else # echo "!!! need to rename: '$lname'" for i in {2..9}; do n_item="${item:0:${#item}-2}_$i.a" n_name=$lname"_$i" if [ -f "$n_item" ]; then - # echo "renamed add: -l$n_name" - LD_LIBS+="-l$n_name " + if [[ $EXCLUDE_LIBS != *";$lname;"* ]]; then + #echo "3. renamed add: -l$n_name" + LD_LIBS+="-l$n_name " + fi break elif [[ $LD_LIB_FILES != *"$n_item"* && $LD_LIBS != *"-l$n_name"* ]]; then - echo "Renaming '$lname' to '$n_name': $item" - cp -f "$item" "$n_item" - LD_LIB_FILES+="$n_item " - LD_LIBS+="-l$n_name " + if [[ $EXCLUDE_LIBS != *";$lname;"* ]]; then + #echo "4. Renaming '$lname' to '$n_name': $item" + cp -f "$item" "$n_item" + LD_LIB_FILES+="$n_item " + LD_LIBS+="-l$n_name " + fi break fi done fi else - # echo "just add: -l$lname" - LD_LIBS+="-l$lname " + if [[ $EXCLUDE_LIBS != *";$lname;"* ]]; then + #echo "5. just add: -l$lname" + LD_LIBS+="-l$lname " + fi fi else echo "*** Skipping $(basename $item): size too small $lsize" @@ -281,82 +318,82 @@ done mkdir -p "$AR_SDK" -# start generation of platformio-build.py -AR_PLATFORMIO_PY="$AR_SDK/platformio-build.py" -cat configs/pio_start.txt > "$AR_PLATFORMIO_PY" +# start generation of pioarduino-build.py +AR_PIOARDUINO_PY="$AR_SDK/pioarduino-build.py" +cat configs/pioarduino_start.txt > "$AR_PIOARDUINO_PY" -echo " ASFLAGS=[" >> "$AR_PLATFORMIO_PY" +echo " ASFLAGS=[" >> "$AR_PIOARDUINO_PY" if [ "$IS_XTENSA" = "y" ]; then - echo " \"-mlongcalls\"" >> "$AR_PLATFORMIO_PY" + echo " \"-mlongcalls\"" >> "$AR_PIOARDUINO_PY" else - echo " \"-march=rv32imc\"" >> "$AR_PLATFORMIO_PY" + echo " \"-march=rv32imc\"" >> "$AR_PIOARDUINO_PY" fi -echo " ]," >> "$AR_PLATFORMIO_PY" -echo "" >> "$AR_PLATFORMIO_PY" +echo " ]," >> "$AR_PIOARDUINO_PY" +echo "" >> "$AR_PIOARDUINO_PY" -echo " ASPPFLAGS=[" >> "$AR_PLATFORMIO_PY" -set -- $PIO_AS_FLAGS +echo " ASPPFLAGS=[" >> "$AR_PIOARDUINO_PY" +set -- $PIOARDUINO_AS_FLAGS for item; do - echo " \"$item\"," >> "$AR_PLATFORMIO_PY" + echo " \"$item\"," >> "$AR_PIOARDUINO_PY" done -echo " \"-x\", \"assembler-with-cpp\"" >> "$AR_PLATFORMIO_PY" -echo " ]," >> "$AR_PLATFORMIO_PY" -echo "" >> "$AR_PLATFORMIO_PY" +echo " \"-x\", \"assembler-with-cpp\"" >> "$AR_PIOARDUINO_PY" +echo " ]," >> "$AR_PIOARDUINO_PY" +echo "" >> "$AR_PIOARDUINO_PY" -echo " CFLAGS=[" >> "$AR_PLATFORMIO_PY" -set -- $PIO_C_FLAGS +echo " CFLAGS=[" >> "$AR_PIOARDUINO_PY" +set -- $PIOARDUINO_C_FLAGS last_item="${@: -1}" for item in "${@:0:${#@}}"; do if [ "${item:0:1}" != "/" ]; then - echo " \"$item\"," >> "$AR_PLATFORMIO_PY" + echo " \"$item\"," >> "$AR_PIOARDUINO_PY" fi done -echo " \"$last_item\"" >> "$AR_PLATFORMIO_PY" -echo " ]," >> "$AR_PLATFORMIO_PY" -echo "" >> "$AR_PLATFORMIO_PY" +echo " \"$last_item\"" >> "$AR_PIOARDUINO_PY" +echo " ]," >> "$AR_PIOARDUINO_PY" +echo "" >> "$AR_PIOARDUINO_PY" -echo " CXXFLAGS=[" >> "$AR_PLATFORMIO_PY" -set -- $PIO_CXX_FLAGS +echo " CXXFLAGS=[" >> "$AR_PIOARDUINO_PY" +set -- $PIOARDUINO_CXX_FLAGS last_item="${@: -1}" for item in "${@:0:${#@}}"; do if [ "${item:0:1}" != "/" ]; then - echo " \"$item\"," >> "$AR_PLATFORMIO_PY" + echo " \"$item\"," >> "$AR_PIOARDUINO_PY" fi done -echo " \"$last_item\"" >> "$AR_PLATFORMIO_PY" -echo " ]," >> "$AR_PLATFORMIO_PY" -echo "" >> "$AR_PLATFORMIO_PY" +echo " \"$last_item\"" >> "$AR_PIOARDUINO_PY" +echo " ]," >> "$AR_PIOARDUINO_PY" +echo "" >> "$AR_PIOARDUINO_PY" -echo " CCFLAGS=[" >> "$AR_PLATFORMIO_PY" -set -- $PIO_CC_FLAGS +echo " CCFLAGS=[" >> "$AR_PIOARDUINO_PY" +set -- $PIOARDUINO_CC_FLAGS for item; do - echo " \"$item\"," >> "$AR_PLATFORMIO_PY" + echo " \"$item\"," >> "$AR_PIOARDUINO_PY" done -echo " \"-MMD\"" >> "$AR_PLATFORMIO_PY" -echo " ]," >> "$AR_PLATFORMIO_PY" -echo "" >> "$AR_PLATFORMIO_PY" +echo " \"-MMD\"" >> "$AR_PIOARDUINO_PY" +echo " ]," >> "$AR_PIOARDUINO_PY" +echo "" >> "$AR_PIOARDUINO_PY" -echo " LINKFLAGS=[" >> "$AR_PLATFORMIO_PY" -set -- $PIO_LD_FLAGS +echo " LINKFLAGS=[" >> "$AR_PIOARDUINO_PY" +set -- $PIOARDUINO_LD_FLAGS for item; do - echo " \"$item\"," >> "$AR_PLATFORMIO_PY" + echo " \"$item\"," >> "$AR_PIOARDUINO_PY" done -set -- $PIO_LD_SCRIPTS +set -- $PIOARDUINO_LD_SCRIPTS for item; do - echo " \"-T\", \"$item\"," >> "$AR_PLATFORMIO_PY" + echo " \"-T\", \"$item\"," >> "$AR_PIOARDUINO_PY" done -set -- $PIO_LD_FUNCS +set -- $PIOARDUINO_LD_FUNCS for item; do - echo " \"-u\", \"$item\"," >> "$AR_PLATFORMIO_PY" + echo " \"-u\", \"$item\"," >> "$AR_PIOARDUINO_PY" done -echo " '-Wl,-Map=\"%s\"' % join(\"\${BUILD_DIR}\", \"\${PROGNAME}.map\")" >> "$AR_PLATFORMIO_PY" +echo " '-Wl,-Map=\"%s\"' % join(\"\${BUILD_DIR}\", \"\${PROGNAME}.map\")" >> "$AR_PIOARDUINO_PY" -echo " ]," >> "$AR_PLATFORMIO_PY" -echo "" >> "$AR_PLATFORMIO_PY" +echo " ]," >> "$AR_PIOARDUINO_PY" +echo "" >> "$AR_PIOARDUINO_PY" # include dirs REL_INC="" -echo " CPPPATH=[" >> "$AR_PLATFORMIO_PY" +echo " CPPPATH=[" >> "$AR_PIOARDUINO_PY" set -- $INCLUDES @@ -384,11 +421,11 @@ for item; do out_cpath="$AR_SDK/include/$fname$out_sub" REL_INC+="-iwithprefixbefore $fname$out_sub " if [ "$out_sub" = "" ]; then - echo " join($PIO_SDK, \"include\", \"$fname\")," >> "$AR_PLATFORMIO_PY" + echo " join($PIOARDUINO_SDK, \"include\", \"$fname\")," >> "$AR_PIOARDUINO_PY" else - pio_sub="${out_sub:1}" - pio_sub=`echo $pio_sub | sed 's/\//\\", \\"/g'` - echo " join($PIO_SDK, \"include\", \"$fname\", \"$pio_sub\")," >> "$AR_PLATFORMIO_PY" + pioarduino_sub="${out_sub:1}" + pioarduino_sub=`echo $pioarduino_sub | sed 's/\//\\", \\"/g'` + echo " join($PIOARDUINO_SDK, \"include\", \"$fname\", \"$pioarduino_sub\")," >> "$AR_PIOARDUINO_PY" fi for f in `find "$item" -name '*.h'`; do rel_f=${f#*$item} @@ -402,6 +439,12 @@ for item; do mkdir -p "$out_cpath$rel_p" cp -n $f "$out_cpath$rel_p/" done + for f in `find "$item" -name '*.inc'`; do + rel_f=${f#*$item} + rel_p=${rel_f%/*} + mkdir -p "$out_cpath$rel_p" + cp -n $f "$out_cpath$rel_p/" + done # Temporary measure to fix issues caused by https://github.com/espressif/esp-idf/commit/dc4731101dd567cc74bbe4d0f03afe52b7db9afb#diff-1d2ce0d3989a80830fdf230bcaafb3117f32046d16cf46616ac3d55b4df2a988R17 if [[ "$fname" == "bt" && "$out_sub" == "/include/$IDF_TARGET/include" && -f "$ipath/controller/$IDF_TARGET/esp_bt_cfg.h" ]]; then mkdir -p "$AR_SDK/include/$fname/controller/$IDF_TARGET" @@ -409,21 +452,19 @@ for item; do fi fi done -echo " join($PIO_SDK, board_config.get(\"build.arduino.memory_type\", (board_config.get(\"build.flash_mode\", \"dio\") + \"_$OCT_PSRAM\")), \"include\")," >> "$AR_PLATFORMIO_PY" -echo " join(FRAMEWORK_DIR, \"cores\", board_config.get(\"build.core\"))" >> "$AR_PLATFORMIO_PY" -echo " ]," >> "$AR_PLATFORMIO_PY" -echo "" >> "$AR_PLATFORMIO_PY" - -mkdir -p "$AR_SDK/lib" +echo " join($PIOARDUINO_SDK, board_config.get(\"build.arduino.memory_type\", (board_config.get(\"build.flash_mode\", \"dio\") + \"_$OCT_PSRAM\")), \"include\")," >> "$AR_PIOARDUINO_PY" +echo " join(FRAMEWORK_DIR, \"cores\", board_config.get(\"build.core\"))" >> "$AR_PIOARDUINO_PY" +echo " ]," >> "$AR_PIOARDUINO_PY" +echo "" >> "$AR_PIOARDUINO_PY" AR_LIBS="$LD_LIBS" -PIO_LIBS="" +PIOARDUINO_LIBS="" set -- $LD_LIBS for item; do - if [ "$PIO_LIBS" != "" ]; then - PIO_LIBS+=", " + if [ "$PIOARDUINO_LIBS" != "" ]; then + PIOARDUINO_LIBS+=", " fi - PIO_LIBS+="\"$item\"" + PIOARDUINO_LIBS+="\"$item\"" done set -- $LD_LIB_FILES @@ -431,19 +472,19 @@ for item; do cp "$item" "$AR_SDK/lib/" done -echo " LIBPATH=[" >> "$AR_PLATFORMIO_PY" -echo " join($PIO_SDK, \"lib\")," >> "$AR_PLATFORMIO_PY" -echo " join($PIO_SDK, \"ld\")," >> "$AR_PLATFORMIO_PY" -echo " join($PIO_SDK, board_config.get(\"build.arduino.memory_type\", (board_config.get(\"build.flash_mode\", \"dio\") + \"_$OCT_PSRAM\")))" >> "$AR_PLATFORMIO_PY" -echo " ]," >> "$AR_PLATFORMIO_PY" -echo "" >> "$AR_PLATFORMIO_PY" +echo " LIBPATH=[" >> "$AR_PIOARDUINO_PY" +echo " join($PIOARDUINO_SDK, \"lib\")," >> "$AR_PIOARDUINO_PY" +echo " join($PIOARDUINO_SDK, \"ld\")," >> "$AR_PIOARDUINO_PY" +echo " join($PIOARDUINO_SDK, board_config.get(\"build.arduino.memory_type\", (board_config.get(\"build.flash_mode\", \"dio\") + \"_$OCT_PSRAM\")))" >> "$AR_PIOARDUINO_PY" +echo " ]," >> "$AR_PIOARDUINO_PY" +echo "" >> "$AR_PIOARDUINO_PY" -echo " LIBS=[" >> "$AR_PLATFORMIO_PY" -echo " $PIO_LIBS" >> "$AR_PLATFORMIO_PY" -echo " ]," >> "$AR_PLATFORMIO_PY" -echo "" >> "$AR_PLATFORMIO_PY" +echo " LIBS=[" >> "$AR_PIOARDUINO_PY" +echo " $PIOARDUINO_LIBS" >> "$AR_PIOARDUINO_PY" +echo " ]," >> "$AR_PIOARDUINO_PY" +echo "" >> "$AR_PIOARDUINO_PY" -echo " CPPDEFINES=[" >> "$AR_PLATFORMIO_PY" +echo " CPPDEFINES=[" >> "$AR_PIOARDUINO_PY" set -- $DEFINES for item; do item="${item:2}" #remove -D @@ -451,17 +492,17 @@ for item; do item=(${item//=/ }) re='^[+-]?[0-9]+([.][0-9]+)?$' if [[ ${item[1]} =~ $re ]]; then - echo " (\"${item[0]}\", ${item[1]})," >> "$AR_PLATFORMIO_PY" + echo " (\"${item[0]}\", ${item[1]})," >> "$AR_PIOARDUINO_PY" else - echo " (\"${item[0]}\", '${item[1]}')," >> "$AR_PLATFORMIO_PY" + echo " (\"${item[0]}\", '${item[1]}')," >> "$AR_PIOARDUINO_PY" fi else - echo " \"$item\"," >> "$AR_PLATFORMIO_PY" + echo " \"$item\"," >> "$AR_PIOARDUINO_PY" fi done -# end generation of platformio-build.py -cat configs/pio_end.txt >> "$AR_PLATFORMIO_PY" +# end generation of pioarduino-build.py +cat configs/pioarduino_end.txt >> "$AR_PIOARDUINO_PY" # replace double backslashes with single one DEFINES=`echo "$DEFINES" | tr -s '\'` @@ -478,14 +519,17 @@ echo -n "$LD_FLAGS" > "$FLAGS_DIR/ld_flags" echo -n "$LD_SCRIPTS" > "$FLAGS_DIR/ld_scripts" echo -n "$AR_LIBS" > "$FLAGS_DIR/ld_libs" -# copy zigbee + zboss lib -if [ -d "managed_components/espressif__esp-zigbee-lib/lib/$IDF_TARGET/" ]; then - cp -r "managed_components/espressif__esp-zigbee-lib/lib/$IDF_TARGET"/* "$AR_SDK/lib/" -fi - -if [ -d "managed_components/espressif__esp-zboss-lib/lib/$IDF_TARGET/" ]; then - cp -r "managed_components/espressif__esp-zboss-lib/lib/$IDF_TARGET"/* "$AR_SDK/lib/" -fi +# Matter Library adjustments +for flag_file in "c_flags" "cpp_flags" "S_flags"; do + echo "Fixing $FLAGS_DIR/$flag_file" + sed 's/\\\"-DCHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=\\\"/-DCHIP_HAVE_CONFIG_H/' $FLAGS_DIR/$flag_file > $FLAGS_DIR/$flag_file.temp + mv $FLAGS_DIR/$flag_file.temp $FLAGS_DIR/$flag_file +done +# this is not necessary for Matter 1.4, but it is for Matter 1.3 +#CHIP_RESOLVE_DIR="$AR_SDK/include/espressif__esp_matter/connectedhomeip/connectedhomeip/src/lib/address_resolve" +#sed 's/CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER//' $CHIP_RESOLVE_DIR/AddressResolve.h > $CHIP_RESOLVE_DIR/AddressResolve_temp.h +#mv $CHIP_RESOLVE_DIR/AddressResolve_temp.h $CHIP_RESOLVE_DIR/AddressResolve.h +# End of Matter Library adjustments # sdkconfig cp -f "sdkconfig" "$AR_SDK/sdkconfig" @@ -517,6 +561,13 @@ for item; do done done +for lib in "openthread" "espressif__esp-tflite-micro" "bt" "espressif__esp_matter"; do + if [ -f "$AR_SDK/lib/lib$lib.a" ]; then + echo "Stripping $AR_SDK/lib/lib$lib.a" + "$TOOLCHAIN-strip" -g "$AR_SDK/lib/lib$lib.a" + fi +done + # Handle Mem Variants mkdir -p "$AR_SDK/$MEMCONF/include" mv "$PWD/build/config/sdkconfig.h" "$AR_SDK/$MEMCONF/include/sdkconfig.h" diff --git a/tools/cron.sh b/tools/cron.sh index 000fdc4dc..1ca166717 100755 --- a/tools/cron.sh +++ b/tools/cron.sh @@ -1,8 +1,7 @@ #!/bin/bash -if [ ! "$GITHUB_EVENT_NAME" == "schedule" ]; then - echo "Wrong event '$GITHUB_EVENT_NAME'!" - exit 1 +if [ -z "$TARGET" ]; then + TARGET="all" fi -bash ./build.sh -d +bash ./build.sh -e -t $TARGET diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile new file mode 100644 index 000000000..699f0bd8e --- /dev/null +++ b/tools/docker/Dockerfile @@ -0,0 +1,79 @@ +# To Do: Check if it is worth to use espressif/idf as base image (image size will be much bigger) +FROM ubuntu:latest + +# switch to root, let the entrypoint drop back to host user +USER root +SHELL ["/bin/bash", "-c"] + +ARG DEBIAN_FRONTEND=noninteractive + +RUN : \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + bison \ + ccache \ + cmake \ + curl \ + flex \ + git \ + gperf \ + jq \ + libncurses-dev \ + libssl-dev \ + libusb-1.0 \ + ninja-build \ + patch \ + python3 \ + python3-click \ + python3-cryptography \ + python3-future \ + python3-pip \ + python3-pyelftools \ + python3-pyparsing \ + python3-serial \ + python3-setuptools \ + python3-venv \ + wget \ + && apt-get autoremove -y \ + && rm -rf /var/lib/apt/lists/* \ + && : + +# To build the image for a branch or a tag of the lib-builder, pass --build-arg LIBBUILDER_CLONE_BRANCH_OR_TAG=name. +# To build the image with a specific commit ID of lib-builder, pass --build-arg LIBBUILDER_CHECKOUT_REF=commit-id. +# It is possibe to combine both, e.g.: +# LIBBUILDER_CLONE_BRANCH_OR_TAG=release/vX.Y +# LIBBUILDER_CHECKOUT_REF=. +# Use LIBBUILDER_CLONE_SHALLOW=1 to peform shallow clone (i.e. --depth=1 --shallow-submodules) +# Use LIBBUILDER_CLONE_SHALLOW_DEPTH=X to define the depth if LIBBUILDER_CLONE_SHALLOW is used (i.e. --depth=X) + +ARG LIBBUILDER_CLONE_URL=https://github.com/espressif/esp32-arduino-lib-builder +ARG LIBBUILDER_CLONE_BRANCH_OR_TAG=master +ARG LIBBUILDER_CHECKOUT_REF= +ARG LIBBUILDER_CLONE_SHALLOW= +ARG LIBBUILDER_CLONE_SHALLOW_DEPTH=1 + +ENV LIBBUILDER_PATH=/opt/esp/lib-builder +# Ccache is installed, enable it by default +ENV IDF_CCACHE_ENABLE=1 + +RUN echo LIBBUILDER_CHECKOUT_REF=$LIBBUILDER_CHECKOUT_REF LIBBUILDER_CLONE_BRANCH_OR_TAG=$LIBBUILDER_CLONE_BRANCH_OR_TAG && \ + git clone --recursive \ + ${LIBBUILDER_CLONE_SHALLOW:+--depth=${LIBBUILDER_CLONE_SHALLOW_DEPTH} --shallow-submodules} \ + ${LIBBUILDER_CLONE_BRANCH_OR_TAG:+-b $LIBBUILDER_CLONE_BRANCH_OR_TAG} \ + $LIBBUILDER_CLONE_URL $LIBBUILDER_PATH && \ + git config --system --add safe.directory $LIBBUILDER_PATH && \ + if [ -n "$LIBBUILDER_CHECKOUT_REF" ]; then \ + cd $LIBBUILDER_PATH && \ + if [ -n "$LIBBUILDER_CLONE_SHALLOW" ]; then \ + git fetch origin --depth=${LIBBUILDER_CLONE_SHALLOW_DEPTH} --recurse-submodules ${LIBBUILDER_CHECKOUT_REF}; \ + fi && \ + git checkout $LIBBUILDER_CHECKOUT_REF && \ + git submodule update --init --recursive; \ + fi && \ + pip3 install --break-system-packages --upgrade -r $LIBBUILDER_PATH/tools/config_editor/requirements.txt + +COPY entrypoint.sh $LIBBUILDER_PATH/entrypoint.sh + +WORKDIR /opt/esp/lib-builder +ENTRYPOINT [ "/opt/esp/lib-builder/entrypoint.sh" ] +CMD [ "python3", "tools/config_editor/app.py" ] diff --git a/tools/docker/README.md b/tools/docker/README.md new file mode 100644 index 000000000..d6a213129 --- /dev/null +++ b/tools/docker/README.md @@ -0,0 +1,50 @@ + + +# ESP-IDF Docker Image + +This is a Docker image for the [ESP32 Arduino Lib Builder](https://github.com/espressif/esp32-arduino-lib-builder). It is intended for building the static libraries of ESP-IDF components for use in Arduino projects. + +This image contains a copy of the esp32-arduino-lib-builder repository and already include or will obtain all the required tools and dependencies to build the Arduino static libraries. + +Currently supported architectures are: + - `amd64` + - `arm64` + +## Tags + +Multiple tags of this image are maintained: + + - `latest`: tracks `master` branch of esp32-arduino-lib-builder + - `release-vX.Y`: tracks `release/vX.Y` branch of esp32-arduino-lib-builder + +## Basic Usage + +```bash +docker run --rm -it -e "TERM=xterm-256color" -v :/arduino-esp32 espressif/esp32-arduino-lib-builder:release-v5.3 +``` + +The above command explained: + + - `docker run`: Runs a command in a new container. + - `--rm`: Optional. Automatically removes the container when it exits. Remove this flag if you plan to use the container multiple times. + - `-i`: Runs the container in interactive mode. + - `-t`: Allocates a pseudo-TTY. + - `-e "TERM=xterm-256color"`: Optional. Sets the terminal type to `xterm-256color` to display colors correctly. + - `-v :/arduino-esp32`: Optional. Mounts the Arduino Core for ESP32 repository at `/arduino-esp32` inside the container. Replace `` with the path to the repository on the host machine. If not provided, the container will not copy the compiled libraries to the host machine. + - `espressif/esp32-arduino-lib-builder:release-v5.3`: The Docker image to use. + +After running the above command, you will be inside the container and can build the libraries using the user interface. + +By default the docker container will run the user interface script. If you want to run a specific command, you can pass it as an argument to the docker run command. For example, to run a terminal inside the container, you can run: + +```bash +docker run -it espressif/esp32-arduino-lib-builder:release-v5.3 /bin/bash +``` + +## Documentation + +For more information about this image and the detailed usage instructions, please refer to the [Arduino Core for ESP32 documentation](https://docs.espressif.com/projects/arduino-esp32/en/latest/lib_builder.html#docker-image). diff --git a/tools/docker/entrypoint.sh b/tools/docker/entrypoint.sh new file mode 100755 index 000000000..4b3826713 --- /dev/null +++ b/tools/docker/entrypoint.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -e + +# LIBBUILDER_GIT_SAFE_DIR has the same format as system PATH environment variable. +# All path specified in LIBBUILDER_GIT_SAFE_DIR will be added to user's +# global git config as safe.directory paths. For more information +# see git-config manual page. +if [ -n "${LIBBUILDER_GIT_SAFE_DIR+x}" ] +then + echo "Adding following directories into git's safe.directory" + echo "$LIBBUILDER_GIT_SAFE_DIR" | tr ':' '\n' | while read -r dir + do + git config --global --add safe.directory "$dir" + echo " $dir" + done +fi + +# Check if the mount point /arduino-esp32 exists +if [ -d "/arduino-esp32" ] && [[ "$@" == "python3 tools/config_editor/app.py"* ]]; then + # Running UI with mount point detected, adding -c and --output-permissions arguments + echo "Output folder permissions: `stat -c "%u:%g" /arduino-esp32`" + exec "$@" -c /arduino-esp32 --output-permissions `stat -c "%u:%g" /arduino-esp32` +else + # Running UI without mount point detected or running another command + exec "$@" +fi diff --git a/tools/docker/run.ps1 b/tools/docker/run.ps1 new file mode 100644 index 000000000..5ecb01a64 --- /dev/null +++ b/tools/docker/run.ps1 @@ -0,0 +1,64 @@ +# This is an example of how to run the docker container. +# This script is not part of the container, it is meant to be run on the host machine. +# Note that this file will build the release/v5.3 branch. For other branches, change the tag accordingly. +# You can check the available tags at https://hub.docker.com/r/espressif/esp32-arduino-lib-builder/tags +# As this script is unsigned, you may need to run `Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass` before running it. +# Usage: .\run.ps1 + +# Exit on error +$ErrorActionPreference = "stop" + +# https://devblogs.microsoft.com/scripting/use-a-powershell-function-to-see-if-a-command-exists/ +# Check if command exists +Function Test-CommandExists +{ + Param ($command) + try { + if (Get-Command $command) { + RETURN $true + } + } + catch { + RETURN $false + } +} + +# Check if path exists +Function Test-PathExists +{ + Param ($path) + try { + if (Test-Path -Path $path) { + RETURN $true + } + } + catch { + RETURN $false + } +} + +if (-not (Test-CommandExists docker)) { + Write-Host "ERROR: Docker is not installed! Please install docker first." -ForegroundColor red + exit 1 +} + +if ($args.Count -gt 0) { + $ARDUINO_DIR = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($args[0]) +} + +$DOCKER_ARGS = @() +$DOCKER_ARGS += '-it' +$DOCKER_ARGS += '-e', 'TERM=xterm-256color' + +if ((Test-PathExists $ARDUINO_DIR)) { + $DOCKER_ARGS += '-v', "${ARDUINO_DIR}:/arduino-esp32" +} else { + Write-Output "Warning: Invalid arduino directory: '$ARDUINO_DIR'. Ignoring it." +} + +if ($env:LIBBUILDER_GIT_SAFE_DIR) { + $DOCKER_ARGS += '-e', "LIBBUILDER_GIT_SAFE_DIR=$env:LIBBUILDER_GIT_SAFE_DIR" +} + +Write-Output "Running: docker run $($DOCKER_ARGS -join ' ') espressif/esp32-arduino-lib-builder" +docker run @($DOCKER_ARGS) espressif/esp32-arduino-lib-builder:release-v5.3 diff --git a/tools/docker/run.sh b/tools/docker/run.sh new file mode 100755 index 000000000..c7f97a6f5 --- /dev/null +++ b/tools/docker/run.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# This is an example of how to run the docker container. +# This script is not part of the container, it is meant to be run on the host machine. +# Note that this file will build the release/v5.3 branch. For other branches, change the tag accordingly. +# You can check the available tags at https://hub.docker.com/r/espressif/esp32-arduino-lib-builder/tags +# Usage: ./run.sh + +if ! [ -x "$(command -v docker)" ]; then + echo "ERROR: Docker is not installed! Please install docker first." + exit 1 +fi + +if [ -n "$1" ]; then + ARDUINO_DIR=$(realpath "$1") +else + ARDUINO_DIR="" +fi + +DOCKER_ARGS=() + +DOCKER_ARGS+=(-it) +DOCKER_ARGS+=(-e TERM=xterm-256color) + +if [ -d "$ARDUINO_DIR" ]; then + DOCKER_ARGS+=(-v $ARDUINO_DIR:/arduino-esp32) +else + echo "Warning: Invalid arduino directory: '$ARDUINO_DIR'. Ignoring it." +fi + +if [ -n "$LIBBUILDER_GIT_SAFE_DIR" ]; then + DOCKER_ARGS+=(-e LIBBUILDER_GIT_SAFE_DIR=$LIBBUILDER_GIT_SAFE_DIR) +fi + +echo "Running: docker run ${DOCKER_ARGS[@]} espressif/esp32-arduino-lib-builder" +docker run ${DOCKER_ARGS[@]} espressif/esp32-arduino-lib-builder:release-v5.3 diff --git a/tools/gen_platformio_manifest.py b/tools/gen_pioarduino_manifest.py similarity index 91% rename from tools/gen_platformio_manifest.py rename to tools/gen_pioarduino_manifest.py index 2d031b687..9d2b99c19 100644 --- a/tools/gen_platformio_manifest.py +++ b/tools/gen_pioarduino_manifest.py @@ -11,7 +11,7 @@ "license": "LGPL-2.1-or-later", "repository": { "type": "git", - "url": "https://github.com/espressif/esp32-arduino-libs", + "url": "https://github.com/espressif/esp32-arduino-lib-builder", }, } @@ -53,7 +53,7 @@ def main(dst_dir, version_string, commit_hash): json.dump(MANIFEST_DATA, fp, indent=2) print( - f"Generated PlatformIO manifest file '{manifest_file_path}' with '{converted_version}' version" + f"Generated pioarduino manifest file '{manifest_file_path}' with '{converted_version}' version" ) return 0 diff --git a/tools/gen_tools_json.py b/tools/gen_tools_json.py index f0fc67579..f03b73d78 100644 --- a/tools/gen_tools_json.py +++ b/tools/gen_tools_json.py @@ -29,6 +29,8 @@ def replace_if_xz(system): new_url = system['url'].replace(".tar.xz", ".tar.gz") new_name = system['archiveFileName'].replace(".tar.xz", ".tar.gz") + new_signed_url = system['url'].replace(".tar.xz", "_signed.tar.gz") + new_signed_name = system['archiveFileName'].replace(".tar.xz", "_signed.tar.gz") new_checksum = "" new_size = 0 @@ -76,6 +78,12 @@ def replace_if_xz(system): release_manifests.append(manifest) # find the new file in the list and get it's size and checksum + for file in release_manifests[manifest_index]['files']: + if file['name'] == new_signed_name: + print("Found a signed version of the file") + new_url = new_signed_url + new_name = new_signed_name + break for file in release_manifests[manifest_index]['files']: if file['name'] == new_name: system['url'] = new_url @@ -107,7 +115,7 @@ def replace_if_xz(system): out_path = args.out_path; # settings - arduino_tools = ["xtensa-esp32-elf","xtensa-esp32s2-elf","xtensa-esp32s3-elf","xtensa-esp-elf-gdb","riscv32-esp-elf","riscv32-esp-elf-gdb","openocd-esp32"] + arduino_tools = ["xtensa-esp-elf","xtensa-esp-elf-gdb","riscv32-esp-elf","riscv32-esp-elf-gdb","openocd-esp32"] # code start farray = {"packages":[{"platforms":[{"toolsDependencies":[]}],"tools":[]}]} diff --git a/tools/install-arduino.sh b/tools/install-arduino.sh index 51e1162a4..1a0ba4d39 100755 --- a/tools/install-arduino.sh +++ b/tools/install-arduino.sh @@ -41,22 +41,8 @@ fi if [ "$AR_BRANCH" ]; then echo "AR_BRANCH='$AR_BRANCH'" + git -C "$AR_COMPS/arduino" fetch --all && \ git -C "$AR_COMPS/arduino" checkout "$AR_BRANCH" && \ - git -C "$AR_COMPS/arduino" fetch && \ git -C "$AR_COMPS/arduino" pull --ff-only fi if [ $? -ne 0 ]; then exit 1; fi - -# -# CLONE/UPDATE ESP32-ARDUINO-LIBS -# -if [ ! -d "$IDF_LIBS_DIR" ]; then - echo "Cloning esp32-arduino-libs..." - git clone "$AR_LIBS_REPO_URL" "$IDF_LIBS_DIR" -else - echo "Updating esp32-arduino-libs..." - git -C "$IDF_LIBS_DIR" fetch && \ - git -C "$IDF_LIBS_DIR" pull --ff-only -fi -if [ $? -ne 0 ]; then exit 1; fi - diff --git a/tools/install-esp-idf.sh b/tools/install-esp-idf.sh index 7ab329589..0519ae67a 100755 --- a/tools/install-esp-idf.sh +++ b/tools/install-esp-idf.sh @@ -17,6 +17,8 @@ if [ ! -d "$IDF_PATH" ]; then idf_was_installed="1" fi +git -C "$IDF_PATH" fetch --all --tags + if [ "$IDF_TAG" ]; then git -C "$IDF_PATH" checkout "tags/$IDF_TAG" idf_was_installed="1" @@ -37,7 +39,8 @@ if [ ! -x $idf_was_installed ] || [ ! -x $commit_predefined ]; then # Temporarily patch the ESP32-S2 I2C LL driver to keep the clock source cd $IDF_PATH - patch -p1 -N -i ../patches/esp32s2_i2c_ll_master_init.diff + patch -p1 -N -i $AR_PATCHES/esp32s2_i2c_ll_master_init.diff + patch -p1 -N -i $AR_PATCHES/lwip_max_tcp_pcb.diff cd - fi @@ -46,52 +49,3 @@ fi # source $IDF_PATH/export.sh - -# -# SETUP ARDUINO DEPLOY -# - -if [ "$GITHUB_EVENT_NAME" == "schedule" ] || [ "$GITHUB_EVENT_NAME" == "repository_dispatch" -a "$GITHUB_EVENT_ACTION" == "deploy" ]; then - # format new branch name and pr title - if [ -x $commit_predefined ]; then #commit was not specified at build time - AR_NEW_BRANCH_NAME="idf-$IDF_BRANCH" - AR_NEW_COMMIT_MESSAGE="IDF $IDF_BRANCH $IDF_COMMIT" - AR_NEW_PR_TITLE="IDF $IDF_BRANCH" - else - AR_NEW_BRANCH_NAME="idf-$IDF_COMMIT" - AR_NEW_COMMIT_MESSAGE="IDF $IDF_COMMIT" - AR_NEW_PR_TITLE="$AR_NEW_COMMIT_MESSAGE" - fi - LIBS_VERSION="idf-"${IDF_BRANCH//\//_}"-$IDF_COMMIT" - - AR_HAS_COMMIT=`git_commit_exists "$AR_COMPS/arduino" "$AR_NEW_COMMIT_MESSAGE"` - AR_HAS_BRANCH=`git_branch_exists "$AR_COMPS/arduino" "$AR_NEW_BRANCH_NAME"` - AR_HAS_PR=`github_pr_exists "$AR_REPO" "$AR_NEW_BRANCH_NAME"` - - LIBS_HAS_COMMIT=`git_commit_exists "$IDF_LIBS_DIR" "$AR_NEW_COMMIT_MESSAGE"` - LIBS_HAS_BRANCH=`git_branch_exists "$IDF_LIBS_DIR" "$AR_NEW_BRANCH_NAME"` - - if [ "$LIBS_HAS_COMMIT" == "1" ]; then - echo "Commit '$AR_NEW_COMMIT_MESSAGE' Already Exists in esp32-arduino-libs" - fi - - if [ "$AR_HAS_COMMIT" == "1" ]; then - echo "Commit '$AR_NEW_COMMIT_MESSAGE' Already Exists in arduino-esp32" - fi - - if [ "$LIBS_HAS_COMMIT" == "1" ] && [ "$AR_HAS_COMMIT" == "1" ]; then - exit 0 - fi - - export AR_NEW_BRANCH_NAME - export AR_NEW_COMMIT_MESSAGE - export AR_NEW_PR_TITLE - - export AR_HAS_COMMIT - export AR_HAS_BRANCH - export AR_HAS_PR - - export LIBS_VERSION - export LIBS_HAS_COMMIT - export LIBS_HAS_BRANCH -fi diff --git a/tools/patch-tinyusb.sh b/tools/patch-tinyusb.sh new file mode 100755 index 000000000..eeaa4d43b --- /dev/null +++ b/tools/patch-tinyusb.sh @@ -0,0 +1,4 @@ +#!/bin/bash +mv components/arduino_tinyusb/src/dcd_dwc2.c components/arduino_tinyusb/src/dcd_dwc2.c.prev +cp components/arduino_tinyusb/tinyusb/src/portable/synopsys/dwc2/dcd_dwc2.c components/arduino_tinyusb/src/dcd_dwc2.c +patch -p1 -N -i components/arduino_tinyusb/patches/dcd_dwc2.patch diff --git a/tools/push-to-arduino.sh b/tools/push-to-arduino.sh index f4d794d0c..8b9b552a7 100755 --- a/tools/push-to-arduino.sh +++ b/tools/push-to-arduino.sh @@ -1,16 +1,12 @@ #!/bin/bash -source ./tools/config.sh + +source ./tools/install-arduino.sh if [ -x $GITHUB_TOKEN ]; then echo "ERROR: GITHUB_TOKEN was not defined" exit 1 fi -if ! [ -d "$AR_COMPS/arduino" ]; then - echo "ERROR: Target arduino folder does not exist!" - exit 1 -fi - # setup git for pushing git config --global github.user "$GITHUB_ACTOR" git config --global user.name "$GITHUB_ACTOR" @@ -24,79 +20,106 @@ git config --global user.email "$GITHUB_ACTOR@github.com" # esp32-arduino-libs # -if [ $LIBS_HAS_COMMIT == "0" ] || [ $AR_HAS_COMMIT == "0" ]; then +LIBS_ZIP_FILENAME="esp32-arduino-libs-$LIBS_VERSION.zip" +LIBS_JSON_FILENAME="package-$LIBS_VERSION.json" +IDF_LIBS_ZIP_URL="https://github.com/$AR_LIBS_REPO/releases/download/$LIBS_RELEASE_TAG/$LIBS_ZIP_FILENAME" +IDF_LIBS_JSON_URL="https://github.com/$AR_LIBS_REPO/releases/download/$LIBS_RELEASE_TAG/$LIBS_JSON_FILENAME" + +if [ $AR_HAS_COMMIT == "0" ] || [ $LIBS_HAS_ASSET == "0" ]; then cd "$AR_ROOT" - # create branch if necessary - if [ "$LIBS_HAS_BRANCH" == "1" ]; then - echo "Branch '$AR_NEW_BRANCH_NAME' Already Exists" - echo "Switching to esp32-arduino-libs branch '$AR_NEW_BRANCH_NAME'..." - git -C "$IDF_LIBS_DIR" checkout $AR_NEW_BRANCH_NAME - else - echo "Creating esp32-arduino-libs branch '$AR_NEW_BRANCH_NAME'..." - git -C "$IDF_LIBS_DIR" checkout -b $AR_NEW_BRANCH_NAME - fi - if [ $? -ne 0 ]; then - echo "ERROR: Checkout of branch '$AR_NEW_BRANCH_NAME' failed" + mkdir -p dist + + # check if the release exists + if [ $LIBS_HAS_RELEASE == "0" ]; then + echo "Release for tag \"$LIBS_RELEASE_TAG\" not found. Please create the release first." exit 1 fi - # make changes to the files - echo "Patching files in esp32-arduino-libs branch '$AR_NEW_BRANCH_NAME'..." - rm -rf $IDF_LIBS_DIR/* && cp -Rf $AR_TOOLS/esp32-arduino-libs/* $IDF_LIBS_DIR/ - - cd $IDF_LIBS_DIR - if [ -f "README.md" ]; then - rm -rf "README.md" + # Delete old assets for the version + if [ $LIBS_HAS_ASSET == "1" ]; then + echo "Deleting existing assets for version '$LIBS_VERSION'..." + if [ `github_release_asset_delete "$AR_LIBS_REPO" "$LIBS_ASSET_ID"` == "0" ]; then + echo "ERROR: Failed to delete asset '$LIBS_ZIP_FILENAME'" + fi + JSON_ASSET_ID=`github_release_asset_id "$AR_LIBS_REPO" "$LIBS_RELEASE_ID" "$LIBS_JSON_FILENAME"` + if [ "$JSON_ASSET_ID" != "" ] && [ `github_release_asset_delete "$AR_LIBS_REPO" "$JSON_ASSET_ID"` == "0" ]; then + echo "ERROR: Failed to delete asset '$LIBS_JSON_FILENAME'" + fi fi - # did any of the files change? - if [ -n "$(git status --porcelain)" ]; then - echo "Pushing changes to esp32-arduino-libs branch '$AR_NEW_BRANCH_NAME'..." - git add . && git commit --message "$AR_NEW_COMMIT_MESSAGE" && git push -u origin $AR_NEW_BRANCH_NAME - if [ $? -ne 0 ]; then - echo "ERROR: Pushing to branch '$AR_NEW_BRANCH_NAME' failed" + sleep 5 + echo "Creating asset '$LIBS_ZIP_FILENAME'..." + mv -f "dist/esp32-arduino-libs.zip" "dist/$LIBS_ZIP_FILENAME" + + LIBS_ASSET_ID=`github_release_asset_upload "$AR_LIBS_REPO" "$LIBS_RELEASE_ID" "$LIBS_ZIP_FILENAME" "dist/$LIBS_ZIP_FILENAME"` + if [ -z "$LIBS_ASSET_ID" ]; then + echo "ERROR: Failed to upload asset '$LIBS_ZIP_FILENAME. Retrying..." + LIBS_ASSET_ID=`github_release_asset_upload "$AR_LIBS_REPO" "$LIBS_RELEASE_ID" "$LIBS_ZIP_FILENAME" "dist/$LIBS_ZIP_FILENAME"` + if [ -z "$LIBS_ASSET_ID" ]; then + echo "ERROR: Failed to upload asset '$LIBS_ZIP_FILENAME'" exit 1 fi - IDF_LIBS_COMMIT=`git rev-parse --verify HEAD` - IDF_LIBS_DL_URL="https://codeload.github.com/espressif/esp32-arduino-libs/zip/$IDF_LIBS_COMMIT" - # ToDo: this URL needs to get into Arduino's package.json + fi - # Download the file - filename="esp32-arduino-libs-$IDF_LIBS_COMMIT.zip" - curl -s -o "$filename" "$IDF_LIBS_DL_URL" + echo "Finished uploading asset '$LIBS_ZIP_FILENAME'. Asset ID: $LIBS_ASSET_ID" + sleep 5 - # Check if the download was successful - if [ $? -ne 0 ]; then - echo "Error downloading file from $IDF_LIBS_DL_URL" - exit 1 - fi + # Calculate the local file checksum and size + local_checksum=$(sha256sum "dist/$LIBS_ZIP_FILENAME" | awk '{print $1}') + local_size=$(stat -c%s "dist/$LIBS_ZIP_FILENAME") - # Calculate the size in bytes and SHA-256 sum - size=$(stat -c%s "$filename") - sha256sum=$(sha256sum "$filename" | awk '{print $1}') - - # Clean up the downloaded file - rm "$filename" - - # Print the results - echo "Tool: esp32-arduino-libs" - echo "Version: $LIBS_VERSION" - echo "URL: $IDF_LIBS_DL_URL" - echo "File: $filename" - echo "Size: $size bytes" - echo "SHA-256: $sha256sum" - echo "JSON: $AR_OUT/package_esp32_index.template.json" - cd "$AR_ROOT" - python3 tools/add_sdk_json.py -j "$AR_OUT/package_esp32_index.template.json" -n "esp32-arduino-libs" -v "$LIBS_VERSION" -u "$IDF_LIBS_DL_URL" -f "$filename" -s "$size" -c "$sha256sum" - if [ $? -ne 0 ]; then exit 1; fi + echo "Downloading asset '$LIBS_ZIP_FILENAME' and checking integrity..." - else - echo "No changes in esp32-arduino-libs branch '$AR_NEW_BRANCH_NAME'" - if [ $LIBS_HAS_BRANCH == "0" ]; then - echo "Delete created branch '$AR_NEW_BRANCH_NAME'" - git branch -d $AR_NEW_BRANCH_NAME - fi - exit 0 + # Download the file + remote_file="remote-$LIBS_ZIP_FILENAME" + curl -s -L -o "$remote_file" "$IDF_LIBS_ZIP_URL" + + # Check if the download was successful + if [ $? -ne 0 ]; then + echo "Error downloading file from $IDF_LIBS_ZIP_URL" + exit 1 + fi + + # Calculate the remote file checksum and size + remote_checksum=$(sha256sum "$remote_file" | awk '{print $1}') + remote_size=$(stat -c%s "$remote_file") + + echo "Local: $local_size bytes, $local_checksum" + echo "Remote: $remote_size bytes, $remote_checksum" + + # Check if the checksums match + if [ "$local_checksum" != "$remote_checksum" ]; then + echo "Checksum mismatch for downloaded file" + echo "Deleting asset and exiting..." + if [ `github_release_asset_delete "$AR_LIBS_REPO" "$LIBS_ASSET_ID"` == "0" ]; then + echo "ERROR: Failed to delete asset '$LIBS_ZIP_FILENAME'" + fi + exit 1 + fi + + # Clean up the downloaded file + rm "$remote_file" + + # Print the results + echo "Tool: esp32-arduino-libs" + echo "Version: $LIBS_VERSION" + echo "URL: $IDF_LIBS_ZIP_URL" + echo "File: $LIBS_ZIP_FILENAME" + echo "Size: $local_size bytes" + echo "SHA-256: $local_checksum" + echo "JSON: $AR_OUT/package_esp32_index.template.json" + cd "$AR_ROOT" + python3 tools/add_sdk_json.py -j "$AR_OUT/package_esp32_index.template.json" -n "esp32-arduino-libs" -v "$LIBS_VERSION" -u "$IDF_LIBS_ZIP_URL" -f "$LIBS_ZIP_FILENAME" -s "$local_size" -c "$local_checksum" + if [ $? -ne 0 ]; then exit 1; fi + + JSON_ASSET_ID=`github_release_asset_upload "$AR_LIBS_REPO" "$LIBS_RELEASE_ID" "$LIBS_JSON_FILENAME" "$AR_OUT/package_esp32_index.template.json"` + if [ -z "$JSON_ASSET_ID" ]; then + echo "ERROR: Failed to upload asset '$LIBS_JSON_FILENAME'. Retrying..." + JSON_ASSET_ID=`github_release_asset_upload "$AR_LIBS_REPO" "$LIBS_RELEASE_ID" "$LIBS_JSON_FILENAME" "$AR_OUT/package_esp32_index.template.json"` + if [ -z "$JSON_ASSET_ID" ]; then + echo "ERROR: Failed to upload asset '$LIBS_JSON_FILENAME'" + exit 1 + fi fi fi @@ -104,7 +127,7 @@ fi # esp32-arduino # -if [ $AR_HAS_COMMIT == "0" ]; then +if [ $AR_HAS_COMMIT == "0" ] || [ $LIBS_HAS_ASSET == "0" ]; then cd "$AR_ROOT" # create or checkout the branch if [ ! $AR_HAS_BRANCH == "0" ]; then @@ -115,31 +138,31 @@ if [ $AR_HAS_COMMIT == "0" ]; then git -C "$AR_COMPS/arduino" checkout -b $AR_NEW_BRANCH_NAME fi if [ $? -ne 0 ]; then - echo "ERROR: Checkout of branch '$AR_NEW_BRANCH_NAME' failed" + echo "ERROR: Checkout of branch '$AR_NEW_BRANCH_NAME' failed" exit 1 fi # make changes to the files echo "Patching files in branch '$AR_NEW_BRANCH_NAME'..." rm -rf "$AR_COMPS/arduino/package/package_esp32_index.template.json" && cp -f "$AR_OUT/package_esp32_index.template.json" "$AR_COMPS/arduino/package/package_esp32_index.template.json" - + cd $AR_COMPS/arduino # did any of the files change? if [ -n "$(git status --porcelain)" ]; then echo "Pushing changes to branch '$AR_NEW_BRANCH_NAME'..." - git add . && git commit --message "$AR_NEW_COMMIT_MESSAGE" && git push -u origin $AR_NEW_BRANCH_NAME + git add . && git commit --message "$AR_NEW_COMMIT_MESSAGE" && git push -u origin $AR_NEW_BRANCH_NAME if [ $? -ne 0 ]; then - echo "ERROR: Pushing to branch '$AR_NEW_BRANCH_NAME' failed" + echo "ERROR: Pushing to branch '$AR_NEW_BRANCH_NAME' failed" exit 1 fi else - echo "No changes in branch '$AR_NEW_BRANCH_NAME'" - if [ $AR_HAS_BRANCH == "0" ]; then - echo "Delete created branch '$AR_NEW_BRANCH_NAME'" - git branch -d $AR_NEW_BRANCH_NAME - fi - exit 0 + echo "No changes in branch '$AR_NEW_BRANCH_NAME'" + if [ $AR_HAS_BRANCH == "0" ]; then + echo "Delete created branch '$AR_NEW_BRANCH_NAME'" + git branch -d $AR_NEW_BRANCH_NAME + fi + exit 0 fi # CREATE PULL REQUEST