├── .cz.toml ├── .github └── workflows │ ├── dangerjs.yml │ ├── issue_comment.yml │ ├── launchpad.yml │ ├── new_issues.yml │ ├── new_prs.yml │ ├── new_release.yml │ └── pre_commit.yml ├── .gitignore ├── .gitlab-ci.yml ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── astyle-rules.yml ├── check_copyright_config.yaml ├── components ├── DAP │ ├── CMakeLists.txt │ ├── Config │ │ └── DAP_config.h │ ├── Include │ │ └── DAP.h │ └── Source │ │ ├── DAP.c │ │ ├── DAP_vendor.c │ │ ├── JTAG_DP.c │ │ └── SW_DP.c └── platform │ ├── CMakeLists.txt │ ├── compiler.h │ ├── esp_io.c │ ├── esp_io.h │ └── xtensa_gcc.h ├── images ├── concept.png ├── concept.svg └── schematics.pdf ├── launchpad.toml ├── main ├── CMakeLists.txt ├── Kconfig.projbuild ├── esp_usb_jtag.c ├── eub_debug_probe.h ├── eub_swd.c ├── eub_vendord.c ├── eub_vendord.h ├── idf_component.yml ├── main.c ├── msc.c ├── msc.h ├── noflash.lf ├── public_include │ └── tusb_config.h ├── serial.c ├── serial.h ├── usb_defs.h ├── util.c └── util.h ├── sdkconfig.defaults ├── sdkconfig.defaults.esp32s2 ├── sdkconfig.defaults.esp32s3 ├── sdkconfig.defaults.esp_prog2 ├── sdkconfig.jtag.defaults └── sdkconfig.swd.defaults /.cz.toml: -------------------------------------------------------------------------------- 1 | [tool.commitizen] 2 | name = "czespressif" 3 | version = "1.1.0" 4 | update_changelog_on_bump = true 5 | tag_format = "v$version" 6 | changelog_merge_prerelease = true 7 | annotated_tag = true 8 | changelog_start_rev = "v1.0.0" 9 | bump_message = "change: Update version to $new_version" 10 | -------------------------------------------------------------------------------- /.github/workflows/dangerjs.yml: -------------------------------------------------------------------------------- 1 | name: DangerJS Check 2 | on: 3 | pull_request_target: 4 | types: [opened, edited, reopened, synchronize] 5 | 6 | permissions: 7 | pull-requests: write 8 | contents: write 9 | 10 | jobs: 11 | pull-request-style-linter: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check out PR head 15 | uses: actions/checkout@v4 16 | with: 17 | ref: ${{ github.event.pull_request.head.sha }} 18 | 19 | - name: DangerJS pull request linter 20 | uses: espressif/github-actions/danger_pr_review@master 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /.github/workflows/issue_comment.yml: -------------------------------------------------------------------------------- 1 | name: Sync issue comments to JIRA 2 | 3 | # This workflow will be triggered when new issue comment is created (including PR comments) 4 | on: issue_comment 5 | 6 | jobs: 7 | sync_issue_comments_to_jira: 8 | name: Sync Issue Comments to Jira 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@master 12 | - name: Sync issue comments to JIRA 13 | uses: espressif/github-actions/sync_issues_to_jira@master 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | JIRA_PASS: ${{ secrets.JIRA_PASS }} 17 | JIRA_PROJECT: EUB 18 | JIRA_COMPONENT: GitHub 19 | JIRA_URL: ${{ secrets.JIRA_URL }} 20 | JIRA_USER: ${{ secrets.JIRA_USER }} 21 | -------------------------------------------------------------------------------- /.github/workflows/launchpad.yml: -------------------------------------------------------------------------------- 1 | name: Build and deploy to Launchpad 2 | 3 | # Trigger the workflow on push to master branch or manually from the Actions tab 4 | on: 5 | release: 6 | types: [published] 7 | 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | container: espressif/idf:v5.4 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | # Install external dependencies 18 | - name: Install dependencies 19 | run: | 20 | apt-get update 21 | apt-get install -y tree 22 | 23 | # Build the application using ESP-IDF, merge the binaries and save as esp-prog2.bin 24 | - name: Build application 25 | run: | 26 | . $IDF_PATH/export.sh 27 | export SDKCONFIG_DEFAULTS="sdkconfig.defaults.esp_prog2;sdkconfig.defaults.esp32s3" 28 | idf.py build 29 | mkdir -p binaries 30 | idf.py merge-bin -o esp-prog2.bin 31 | 32 | # Create page files for deployment 33 | # Copy the built binaries and the launchpad.toml file to the binaries directory 34 | # and generate an index.html file with the file tree of the binaries directory. 35 | - name: Create page files 36 | run: | 37 | cp build/esp-prog2.bin binaries/ 38 | cp launchpad.toml binaries/ 39 | cd binaries 40 | tree -q -H '.' -L 1 -T 'esp-usb-bridge Launchpad Artifacts' -shi --charset utf-8 -I "index.html" -o index.html 41 | 42 | - name: Upload Artifact 43 | uses: actions/upload-artifact@v4 44 | with: 45 | name: binaries 46 | path: binaries/ 47 | 48 | # Job to deploy the application to GitHub Pages 49 | deploy: 50 | needs: build 51 | 52 | # Necessary permissions to deploy to GitHub Pages 53 | permissions: 54 | pages: write 55 | id-token: write 56 | 57 | environment: 58 | name: github-pages 59 | url: ${{ steps.deployment.outputs.page_url }} 60 | 61 | runs-on: ubuntu-latest 62 | steps: 63 | - uses: actions/checkout@v4 64 | 65 | # Download the built binaries artifact 66 | - name: Download built files 67 | uses: actions/download-artifact@v4 68 | with: 69 | name: binaries 70 | path: binaries/ 71 | 72 | - name: Setup GitHub Pages 73 | uses: actions/configure-pages@v4 74 | 75 | - name: Upload built files to GitHub pages 76 | uses: actions/upload-pages-artifact@v3 77 | with: 78 | name: github-pages 79 | path: binaries/ 80 | 81 | - name: Deploy to GitHub Pages 82 | id: deployment 83 | uses: actions/deploy-pages@v4 84 | -------------------------------------------------------------------------------- /.github/workflows/new_issues.yml: -------------------------------------------------------------------------------- 1 | name: Sync issues to Jira 2 | 3 | # This workflow will be triggered when a new issue is opened 4 | on: issues 5 | 6 | jobs: 7 | sync_issues_to_jira: 8 | name: Sync issues to Jira 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@master 12 | - name: Sync GitHub issues to Jira project 13 | uses: espressif/github-actions/sync_issues_to_jira@master 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | JIRA_PASS: ${{ secrets.JIRA_PASS }} 17 | JIRA_PROJECT: EUB 18 | JIRA_COMPONENT: GitHub 19 | JIRA_URL: ${{ secrets.JIRA_URL }} 20 | JIRA_USER: ${{ secrets.JIRA_USER }} 21 | -------------------------------------------------------------------------------- /.github/workflows/new_prs.yml: -------------------------------------------------------------------------------- 1 | name: Sync remain PRs to Jira 2 | 3 | # This workflow will be triggered every hour, to sync remaining PRs (i.e. PRs with zero comment) to Jira project 4 | # Note that, PRs can also get synced when new PR comment is created 5 | on: 6 | schedule: 7 | - cron: "0 * * * *" 8 | 9 | jobs: 10 | sync_prs_to_jira: 11 | name: Sync PRs to Jira 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@master 15 | - name: Sync PRs to Jira project 16 | uses: espressif/github-actions/sync_issues_to_jira@master 17 | with: 18 | cron_job: true 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | JIRA_PASS: ${{ secrets.JIRA_PASS }} 22 | JIRA_PROJECT: EUB 23 | JIRA_COMPONENT: GitHub 24 | JIRA_URL: ${{ secrets.JIRA_URL }} 25 | JIRA_USER: ${{ secrets.JIRA_USER }} 26 | -------------------------------------------------------------------------------- /.github/workflows/new_release.yml: -------------------------------------------------------------------------------- 1 | name: Create a new release from a tag 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | create_release: 10 | name: Create GitHub release 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | steps: 15 | - name: Get version 16 | id: get_version 17 | run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT 18 | shell: bash 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | - name: Install dependencies 24 | run: pip install commitizen czespressif>=1.3.1 25 | - name: Generate changelog 26 | run: | 27 | cz changelog ${{ steps.get_version.outputs.VERSION }} --file-name changelog_body.md 28 | cat changelog_body.md 29 | - name: Create release 30 | uses: softprops/action-gh-release@v1 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | with: 34 | body_path: changelog_body.md 35 | name: Version ${{ steps.get_version.outputs.VERSION }} 36 | draft: true 37 | prerelease: false 38 | -------------------------------------------------------------------------------- /.github/workflows/pre_commit.yml: -------------------------------------------------------------------------------- 1 | name: Check pre-commit rules 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened, synchronize] 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | pre_commit_check: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | - name: Fetch head and base refs 17 | # This is necessary for pre-commit to check the changes in the PR branch 18 | run: | 19 | git fetch origin ${{ github.base_ref }}:base_ref 20 | git fetch origin pull/${{ github.event.pull_request.number }}/head:pr_ref 21 | - name: Set up Python environment 22 | uses: actions/setup-python@master 23 | with: 24 | python-version: v3.11 25 | - name: Install python packages 26 | run: | 27 | pip install pre-commit 28 | pre-commit install-hooks 29 | - name: Run pre-commit and check for any changes 30 | run: | 31 | echo "Commits being checked:" 32 | git log --oneline --no-decorate base_ref..pr_ref 33 | echo "" 34 | if ! pre-commit run --from-ref base_ref --to-ref pr_ref --show-diff-on-failure ; then 35 | echo "" 36 | echo "::notice::It looks like the commits in this PR have been made without having pre-commit hooks installed." 37 | echo "::notice::Please see https://github.com/espressif/esp-usb-bridge?tab=readme-ov-file#contributing for instructions." 38 | echo "" 39 | exit 1 40 | fi 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | managed_components/ 3 | sdkconfig 4 | sdkconfig.old 5 | dependencies.lock 6 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - pre-check 3 | - build 4 | 5 | # Global variables 6 | variables: 7 | EUB_APP_NAME: "bridge" 8 | 9 | 10 | # WORKFLOW RULES (global rules for when a pipeline should be triggered or not) 11 | workflow: 12 | rules: 13 | - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' # Run pipeline for merge request events. 14 | - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS' 15 | when: never # Do not run pipeline on push if there are open merge requests for the branch. 16 | - if: '$CI_COMMIT_BRANCH' # Run pipeline for any push to the default branch. 17 | 18 | ############## 19 | # PRE CHECK # 20 | ############## 21 | 22 | # Shared-CI-DangerJS (CI from external Gitlab repository) 23 | include: 24 | - project: espressif/shared-ci-dangerjs 25 | ref: master 26 | file: danger.yaml 27 | run-danger-mr-linter: 28 | stage: pre-check 29 | tags: 30 | - dangerjs 31 | variables: 32 | ENABLE_CHECK_UPDATED_CHANGELOG: 'false' 33 | 34 | pre_commit: 35 | stage: pre-check 36 | tags: 37 | - build 38 | image: python:3.11-bookworm 39 | before_script: 40 | - apt-get update 41 | - apt-get install -y git 42 | script: 43 | - pip install pre-commit 44 | - echo "Merge request is from ${CI_COMMIT_REF_NAME} into ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}" 45 | - git fetch origin ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME} ${CI_COMMIT_REF_NAME} 46 | - export from_sha=$(git merge-base HEAD origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}) 47 | - echo "Checking changes from ${from_sha} to ${CI_COMMIT_SHA}:" 48 | - git log --oneline ${from_sha}..${CI_COMMIT_SHA} 49 | - echo "Modified files:" 50 | - git diff-tree --no-commit-id --name-only -r ${from_sha} ${CI_COMMIT_SHA} 51 | - echo "Running pre-commit:" 52 | - pre-commit run --from ${from_sha} --to ${CI_COMMIT_SHA} 53 | rules: 54 | - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' 55 | 56 | ################ 57 | # BUILD TESTS # 58 | ################ 59 | 60 | .build_template: 61 | tags: 62 | - build 63 | script: 64 | - PEDANTIC_FLAGS="-DIDF_CI_BUILD -Werror -Werror=deprecated-declarations -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function" 65 | - export PEDANTIC_CFLAGS="${PEDANTIC_FLAGS} -Wstrict-prototypes" 66 | - export PEDANTIC_CXXFLAGS="${PEDANTIC_FLAGS}" 67 | - idf.py -DIDF_TARGET=${CHIP_NAME} -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.${EUB_DEBUG_IFACE}.defaults" build 68 | - cd build 69 | - esptool.py --chip ${CHIP_NAME} merge_bin -o ${EUB_APP_NAME}_merged.bin @flash_args 70 | artifacts: 71 | paths: 72 | - build/flasher_args.json 73 | - build/${EUB_APP_NAME}.elf 74 | - build/${EUB_APP_NAME}.bin 75 | - build/bootloader/bootloader.bin 76 | - build/bootloader/bootloader.elf 77 | - build/partition_table/partition-table.bin 78 | - build/${EUB_APP_NAME}_merged.bin 79 | expire_in: 2 weeks 80 | needs: 81 | - job: pre_commit 82 | optional: true 83 | 84 | build_matrix: 85 | extends: .build_template 86 | stage: build 87 | parallel: 88 | matrix: 89 | - CHIP_NAME: ["esp32s2", "esp32s3"] 90 | EUB_DEBUG_IFACE: ["jtag", "swd"] 91 | IDF_VERSION: [ 92 | "release-v5.0", 93 | "release-v5.1", 94 | "release-v5.2", 95 | "release-v5.3", 96 | "release-v5.4", 97 | "release-v5.5", 98 | "latest" 99 | ] 100 | image: espressif/idf:${IDF_VERSION} 101 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Run `pre-commit autoupdate` to update to the latest pre-commit hooks version. 2 | minimum_pre_commit_version: 3.3.0 3 | default_install_hook_types: [pre-commit, commit-msg] 4 | 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v5.0.0 8 | hooks: 9 | - id: trailing-whitespace 10 | exclude: &whitespace_excludes | 11 | (?x)^.*\.(md|rst|map|bin)$ 12 | - id: end-of-file-fixer 13 | exclude: *whitespace_excludes 14 | - id: mixed-line-ending 15 | args: ['-f=lf'] 16 | - id: double-quote-string-fixer 17 | - repo: https://github.com/espressif/astyle_py.git 18 | rev: v1.1.0 19 | hooks: 20 | - id: astyle_py 21 | args: ['--astyle-version=3.4.7', '--rules=astyle-rules.yml'] 22 | - repo: https://github.com/espressif/check-copyright/ 23 | rev: v1.1.1 24 | hooks: 25 | - id: check-copyright 26 | args: ['--config', 'check_copyright_config.yaml'] 27 | - repo: https://github.com/espressif/conventional-precommit-linter 28 | rev: v1.10.0 29 | hooks: 30 | - id: conventional-precommit-linter # Lints commit messages for conventional format 31 | stages: [commit-msg] 32 | - repo: https://github.com/codespell-project/codespell 33 | rev: v2.4.1 34 | hooks: 35 | - id: codespell # Code spell checker 36 | args: ["--write-changes"] 37 | - repo: https://github.com/executablebooks/mdformat 38 | rev: 0.7.22 39 | hooks: 40 | - id: mdformat 41 | args: [--number] # Keep numbering for ordered lists 42 | additional_dependencies: 43 | - mdformat-gfm # Support for GitHub Flavored Markdown (GFM), including tables, task lists, strikethroughs, and autolinks. 44 | - mdformat-ruff # Formats Python code blocks in Markdown files according to the `ruff` linter's style. 45 | - mdformat-simple-breaks # Ensures that single line breaks in Markdown are converted to `
` t 46 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v1.1.0 (2025-04-23) 2 | 3 | ### ✨ New Features 4 | 5 | - **uf2**: Add UF2 IDs for C5, C61, H21, and H4 *(Radim Karniš - 3db3d07)* 6 | 7 | ### 🐛 Bug Fixes 8 | 9 | - **tinyusb**: fix zero-length packet handling *(Jaroslav Burian - 974fb04)* 10 | - **msc**: Fix flashing when blocks are not in numerical order *(Jaroslav Burian - 382530e)* 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following five lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | 7 | if(${IDF_VERSION_MAJOR} LESS 5) 8 | message(FATAL_ERROR "ESP-IDF v${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR} is not supported! " 9 | "Please use v5.0 or newer.") 10 | endif() 11 | 12 | project(bridge) 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP USB Bridge 2 | 3 | The ESP USB Bridge is an [ESP-IDF](https://github.com/espressif/esp-idf) project utilizing an ESP32-S2 or an ESP32-S3 chip to create a bridge between a computer (PC) and a target microcontroller (MCU). It can serve as a replacement for USB-to-UART chips (e.g. CP210x) or a debugger. 4 | 5 | The concept of ESP USB Bridge is shown in the following figure. 6 | 7 | ![ESP USB Bridge concept](images/concept.png) 8 | 9 | ESP USB Bridge creates a composite USB device accessible from the PC when they are connected through a USB cable. The main features are the following. 10 | 11 | - *Serial bridge*: The developer can run [esptool](https://github.com/espressif/esptool) or connect a terminal program to the serial port provided by the USB CDC. The communication is transferred in both directions between the PC and the target MCU through the ESP USB bridge. 12 | - *JTAG/SWD bridge*: [openocd-esp32](https://github.com/espressif/openocd-esp32) can be run on the PC which will connect to the ESP USB Bridge. The bridge MCU acts again as bridge between the PC and the MCU, and transfers JTAG/SWD communication between them in both directions. 13 | - *Mass storage device*: USB Mass storage device is created which can be accessed by a file explorer in the PC. Binaries in UF2 format can be copied to this disk and the bridge MCU will use them to flash the target MCU. Currently ESP USB Bridge is capable of flashing various Espressif microcontrollers. 14 | 15 | ## How to Compile the Project 16 | 17 | [ESP-IDF](https://github.com/espressif/esp-idf) v5.0 or newer can be used to compile the project. Please read the 18 | documentation of ESP-IDF for setting up the environment. 19 | 20 | - `idf.py set-target` is used to select the chip the firmware is going to be built for. ESP32S2 and ESP32S3 are supported at the moment. 21 | - `idf.py menuconfig` can be used to change the default configuration. The project-specific settings are in the "Bridge Configuration" sub-menu. 22 | - `idf.py build` will build the project binaries. 23 | - `idf.py -p PORT flash monitor` will flash the Bridge MCU and open the terminal program for monitoring. Please note that PORT is the serial port created by an USB-to-UART chip connected to the serial interface of the bridge MCU (not the direct USB interface provided by bridge MCU). This serial connection has to be established only for flashing. The ESP USB Bridge can work through the USB interface after that. 24 | 25 | The initial flashing can be done by other means as well as it is pointed out in [this guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-guides/usb-otg-console.html#uploading-the-application). 26 | 27 | ## Development Board 28 | 29 | A simple development board connecting an ESP32-S2 to an ESP32 serving as target MCU is shown in the [schematics](images/schematics.pdf). The default configurations of the project were tested with this setup. 30 | 31 | Similar boards can be manufactured and flashed with the ESP USB Bridge. The pin numbers, vendor and product identifiers and other settings can be changed in `idf.py menuconfig`. 32 | 33 | Please note that every board should have its own vendor and product identifiers. There is also a possibility to register a product identifier under the [Espressif vendor identifier](https://github.com/espressif/usb-pids). 34 | 35 | ## Serial Bridge 36 | 37 | The USB stack of ESP USB Bridge creates a virtual serial port through which the serial port of the target MCU is accessible. For example, this port can be `/dev/ttyACMx` or `COMx` depending on the operating system and is different from the PORT used for flashing the ESP USB Bridge. 38 | 39 | For example, an ESP32 target MCU can be flashed and monitored with the following commands. 40 | 41 | ```bash 42 | cd AN_ESP32_PROJECT 43 | idf.py build 44 | idf.py -p /dev/ttyACMx flash monitor 45 | ``` 46 | 47 | Please note that [esptool](https://github.com/espressif/esptool) or any terminal program can connect to the virtual serial port as well. 48 | 49 | ## JTAG Bridge 50 | 51 | The ESP USB Bridge provides a JTAG device. The following command can be used to connect to an ESP32 target MCU. 52 | 53 | ```bash 54 | idf.py openocd --openocd-commands "-f board/esp32-bridge.cfg" 55 | ``` 56 | 57 | [Openocd-esp32](https://github.com/espressif/openocd-esp32) version v0.11.0-esp32-20211220 or newer can be used as well to achieve the same: 58 | 59 | ```bash 60 | openocd -f board/esp32-bridge.cfg 61 | ``` 62 | 63 | Please note that the ESP usb bridge protocol has to be selected to communicate with the target MCU. `idf.py openocd` without additional arguments would establish connection with the bridge MCU (if the JTAG pins are connected through a USB-to-JTAG bridge to the PC). 64 | 65 | You might want to make your own copy of `esp_usb_bridge.cfg` with the appropriate product and vendor identifiers of your custom hardware: 66 | 67 | ``` 68 | adapter driver esp_usb_jtag 69 | espusbjtag vid_pid 0x303a 0x1002 70 | espusbjtag caps_descriptor 0x030A # string descriptor index:10 71 | ``` 72 | 73 | The JTAG interface might need some additional setup to work. Please consult the [documentation of ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/jtag-debugging/configure-ft2232h-jtag.html) for achieving this. 74 | 75 | ## SWD Bridge 76 | 77 | The ESP USB Bridge also provides an ARM Serial Wire Debug (SWD) device using the CMSIS-DAP protocol. 78 | 79 | On the OpenOCD side, CMSIS-DAP-related updates are continuously synced from the mainline to the Espressif fork. Therefore, it is always recommended to use the latest release or the latest master branch. 80 | 81 | The following command can be used to access the SWD port on a target MCU 82 | 83 | ```bash 84 | openocd -s tcl -f interface/cmsis-dap.cfg -f target/.cfg -c 'adapter speed 5000' 85 | ``` 86 | 87 | Make sure to replace `target.cfg` with the actual target config file. 88 | 89 | Currently, the ESP USB bridge supports only USB-bulk transfers as a backend. The HID backend is not supported. 90 | 91 | ## Mass Storage Device 92 | 93 | A mass storage device will show up in the PC connected to the ESP USB bridge. This can be accessed as any other USB storage disk. Binaries built in [the UF2 format](https://github.com/microsoft/uf2) can be copied to this disk and the bridge MCU will flash the target MCU accordingly. 94 | 95 | Binary `uf2.bin` will be generated and placed into the `AN_ESP32_PROJECT/build` directory by running the following commands. 96 | 97 | ```bash 98 | cd AN_ESP32_PROJECT 99 | idf.py uf2 100 | ``` 101 | 102 | ## License 103 | 104 | The code in this project Copyright 2020-2022 Espressif Systems (Shanghai) Co Ltd., and is licensed under the Apache License Version 2.0. The copy of the license can be found in the [LICENSE](LICENSE) file. 105 | 106 | ## Contributing 107 | 108 | We welcome contributions to this project in the form of bug reports, feature requests and pull requests. 109 | 110 | Issue reports and feature requests can be submitted using Github Issues: https://github.com/espressif/esp-usb-bridge/issues. Please check if the issue has already been reported before opening a new one. 111 | 112 | Contributions in the form of pull requests should follow ESP-IDF project's [contribution guidelines](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/contribute/index.html). 113 | 114 | Additionally please install [pre-commit](https://pre-commit.com/#install) hooks before committing code: 115 | 116 | ```bash 117 | pip install pre-commit 118 | pre-commit install 119 | ``` 120 | 121 | --- 122 | 123 | This project is universal and can be used with various hardware setups. For detailed hardware setup requirements, please refer to the [Development Board](#development-board) section. However, if you own an ESP-Prog2, you can use the following link to try to flash it using ESP Launchpad. 124 | 125 | 126 | Try it with ESP Launchpad 127 | 128 | -------------------------------------------------------------------------------- /astyle-rules.yml: -------------------------------------------------------------------------------- 1 | DEFAULT: 2 | options: "--style=otbs --attach-namespaces --attach-classes --indent=spaces=4 --convert-tabs --align-pointer=name --align-reference=name --keep-one-line-statements --pad-header --pad-oper --unpad-paren --pad-comma" 3 | 4 | not_formatted: 5 | check: false 6 | include: 7 | - "components/DAP/" 8 | - "components/DAP/Config/" 9 | - "components/DAP/Include/" 10 | - "components/DAP/Source/" 11 | - "images/" 12 | -------------------------------------------------------------------------------- /check_copyright_config.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the check_copyright pre-commit hook 2 | DEFAULT: 3 | perform_check: yes # should the check be performed? 4 | # Sections setting this to 'no' don't need to include any other options as they are ignored 5 | # When a file is using a section with the option set to 'no', no checks are performed. 6 | 7 | # what licenses (or license expressions) are allowed for files in this section 8 | # when setting this option in a section, you need to list all the allowed licenses 9 | allowed_licenses: 10 | - Apache-2.0 11 | license_for_new_files: Apache-2.0 # license to be used when inserting a new copyright notice 12 | new_notice_c: | # notice for new C, CPP, H, HPP and LD files 13 | /* 14 | * SPDX-FileCopyrightText: {years} Espressif Systems (Shanghai) CO LTD 15 | * 16 | * SPDX-License-Identifier: {license} 17 | */ 18 | new_notice_python: | # notice for new python files 19 | # SPDX-FileCopyrightText: {years} Espressif Systems (Shanghai) CO LTD 20 | # SPDX-License-Identifier: {license} 21 | 22 | # comment lines matching: 23 | # SPDX-FileCopyrightText: year[-year] Espressif Systems 24 | # or 25 | # SPDX-FileContributor: year[-year] Espressif Systems 26 | # are replaced with this template prefixed with the correct comment notation (# or // or *) and SPDX- notation 27 | espressif_copyright: '{years} Espressif Systems (Shanghai) CO LTD' 28 | 29 | ignore: # You can also select ignoring files here 30 | perform_check: no # Don't check files from that block 31 | include: 32 | - components/DAP/ 33 | -------------------------------------------------------------------------------- /components/DAP/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(sources 2 | "Source/DAP_vendor.c" 3 | "Source/DAP.c" 4 | "Source/JTAG_DP.c" 5 | "Source/SW_DP.c" 6 | ) 7 | 8 | set(include_dirs 9 | "Include" 10 | "Config" 11 | ) 12 | 13 | set(dependencies 14 | "esp_timer" 15 | "platform" 16 | ) 17 | 18 | idf_component_register( 19 | SRCS 20 | ${sources} 21 | INCLUDE_DIRS 22 | ${include_dirs} 23 | REQUIRES 24 | ${dependencies} 25 | ) 26 | -------------------------------------------------------------------------------- /components/DAP/Config/DAP_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2021 ARM Limited. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the License); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an AS IS BASIS, WITHOUT 14 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ---------------------------------------------------------------------- 19 | * 20 | * $Date: 16. June 2021 21 | * $Revision: V2.1.0 22 | * 23 | * Project: CMSIS-DAP Configuration 24 | * Title: DAP_config.h CMSIS-DAP Configuration File (Template) 25 | * 26 | *---------------------------------------------------------------------------*/ 27 | 28 | #ifndef __DAP_CONFIG_H__ 29 | #define __DAP_CONFIG_H__ 30 | 31 | 32 | //************************************************************************************************** 33 | /** 34 | \defgroup DAP_Config_Debug_gr CMSIS-DAP Debug Unit Information 35 | \ingroup DAP_ConfigIO_gr 36 | @{ 37 | Provides definitions about the hardware and configuration of the Debug Unit. 38 | 39 | This information includes: 40 | - Definition of Cortex-M processor parameters used in CMSIS-DAP Debug Unit. 41 | - Debug Unit Identification strings (Vendor, Product, Serial Number). 42 | - Debug Unit communication packet size. 43 | - Debug Access Port supported modes and settings (JTAG/SWD and SWO). 44 | - Optional information about a connected Target Device (for Evaluation Boards). 45 | */ 46 | #include 47 | #include "sdkconfig.h" 48 | #include "compiler.h" 49 | #include "esp_io.h" 50 | #include "esp_timer.h" 51 | 52 | /// Processor Clock of the Cortex-M MCU used in the Debug Unit. 53 | /// This value is used to calculate the SWD/JTAG clock speed. 54 | #define CPU_CLOCK CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ * 1000000U ///< Specifies the CPU Clock in Hz. 55 | 56 | /// Number of processor cycles for I/O Port write operations. 57 | /// This value is used to calculate the SWD/JTAG clock speed that is generated with I/O 58 | /// Port write operations in the Debug Unit by a Cortex-M MCU. Most Cortex-M processors 59 | /// require 2 processor cycles for a I/O Port Write operation. If the Debug Unit uses 60 | /// a Cortex-M0+ processor with high-speed peripheral I/O only 1 processor cycle might be 61 | /// required. 62 | #define IO_PORT_WRITE_CYCLES 1U ///< I/O Cycles: 2=default, 1=Cortex-M0+ fast I/0. 63 | 64 | /// Indicate that Serial Wire Debug (SWD) communication mode is available at the Debug Access Port. 65 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 66 | #if CONFIG_BRIDGE_DEBUG_IFACE_SWD 67 | #define DAP_SWD 1 ///< SWD Mode: 1 = available, 0 = not available. 68 | #else 69 | #define DAP_SWD 0 70 | #endif 71 | 72 | /// Indicate that JTAG communication mode is available at the Debug Port. 73 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 74 | #if CONFIG_BRIDGE_DEBUG_IFACE_JTAG 75 | #define DAP_JTAG 1 ///< JTAG Mode: 1 = available, 0 = not available. 76 | #else 77 | #define DAP_JTAG 0 78 | #endif 79 | 80 | /// Configure maximum number of JTAG devices on the scan chain connected to the Debug Access Port. 81 | /// This setting impacts the RAM requirements of the Debug Unit. Valid range is 1 .. 255. 82 | #define DAP_JTAG_DEV_CNT 1U ///< Maximum number of JTAG devices on scan chain. 83 | 84 | /// Default communication mode on the Debug Access Port. 85 | /// Used for the command \ref DAP_Connect when Port Default mode is selected. 86 | #define DAP_DEFAULT_PORT 1U ///< Default JTAG/SWJ Port Mode: 1 = SWD, 2 = JTAG. 87 | 88 | /// Default communication speed on the Debug Access Port for SWD and JTAG mode. 89 | /// Used to initialize the default SWD/JTAG clock frequency. 90 | /// The command \ref DAP_SWJ_Clock can be used to overwrite this default setting. 91 | #define DAP_DEFAULT_SWJ_CLOCK 1000000U ///< Default SWD/JTAG clock frequency in Hz. 92 | 93 | /// Maximum Package Size for Command and Response data. 94 | /// This configuration settings is used to optimize the communication performance with the 95 | /// debugger and depends on the USB peripheral. Typical vales are 64 for Full-speed USB HID or WinUSB, 96 | /// 1024 for High-speed USB HID and 512 for High-speed USB WinUSB. 97 | #define DAP_PACKET_SIZE 64U ///< Specifies Packet Size in bytes. 98 | 99 | /// Maximum Package Buffers for Command and Response data. 100 | /// This configuration settings is used to optimize the communication performance with the 101 | /// debugger and depends on the USB peripheral. For devices with limited RAM or USB buffer the 102 | /// setting can be reduced (valid range is 1 .. 255). 103 | #define DAP_PACKET_COUNT 8U ///< Specifies number of packets buffered. 104 | 105 | /// Indicate that UART Serial Wire Output (SWO) trace is available. 106 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 107 | #define SWO_UART 0 ///< SWO UART: 1 = available, 0 = not available. 108 | 109 | /// USART Driver instance number for the UART SWO. 110 | #define SWO_UART_DRIVER 0 ///< USART Driver instance number (Driver_USART#). 111 | 112 | /// Maximum SWO UART Baudrate. 113 | #define SWO_UART_MAX_BAUDRATE 10000000U ///< SWO UART Maximum Baudrate in Hz. 114 | 115 | /// Indicate that Manchester Serial Wire Output (SWO) trace is available. 116 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 117 | #define SWO_MANCHESTER 0 ///< SWO Manchester: 1 = available, 0 = not available. 118 | 119 | /// SWO Trace Buffer Size. 120 | #define SWO_BUFFER_SIZE 4096U ///< SWO Trace Buffer Size in bytes (must be 2^n). 121 | 122 | /// SWO Streaming Trace. 123 | #define SWO_STREAM 0 ///< SWO Streaming Trace: 1 = available, 0 = not available. 124 | 125 | /// Clock frequency of the Test Domain Timer. Timer value is returned with \ref TIMESTAMP_GET. 126 | #define TIMESTAMP_CLOCK 0U ///< Timestamp clock in Hz (0 = timestamps not supported). 127 | 128 | /// Indicate that UART Communication Port is available. 129 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 130 | #define DAP_UART 0 ///< DAP UART: 1 = available, 0 = not available. 131 | 132 | /// USART Driver instance number for the UART Communication Port. 133 | #define DAP_UART_DRIVER 0 ///< USART Driver instance number (Driver_USART#). 134 | 135 | /// UART Receive Buffer Size. 136 | #define DAP_UART_RX_BUFFER_SIZE 1024U ///< Uart Receive Buffer Size in bytes (must be 2^n). 137 | 138 | /// UART Transmit Buffer Size. 139 | #define DAP_UART_TX_BUFFER_SIZE 1024U ///< Uart Transmit Buffer Size in bytes (must be 2^n). 140 | 141 | /// Indicate that UART Communication via USB COM Port is available. 142 | /// This information is returned by the command \ref DAP_Info as part of Capabilities. 143 | #define DAP_UART_USB_COM_PORT 0 ///< USB COM Port: 1 = available, 0 = not available. 144 | 145 | /// Debug Unit is connected to fixed Target Device. 146 | /// The Debug Unit may be part of an evaluation board and always connected to a fixed 147 | /// known device. In this case a Device Vendor, Device Name, Board Vendor and Board Name strings 148 | /// are stored and may be used by the debugger or IDE to configure device parameters. 149 | #define TARGET_FIXED 0 ///< Target: 1 = known, 0 = unknown; 150 | 151 | #define TARGET_DEVICE_VENDOR "Arm" ///< String indicating the Silicon Vendor 152 | #define TARGET_DEVICE_NAME "Cortex-M" ///< String indicating the Target Device 153 | #define TARGET_BOARD_VENDOR "Arm" ///< String indicating the Board Vendor 154 | #define TARGET_BOARD_NAME "Arm board" ///< String indicating the Board Name 155 | 156 | #if TARGET_FIXED != 0 157 | #include 158 | static const char TargetDeviceVendor [] = TARGET_DEVICE_VENDOR; 159 | static const char TargetDeviceName [] = TARGET_DEVICE_NAME; 160 | static const char TargetBoardVendor [] = TARGET_BOARD_VENDOR; 161 | static const char TargetBoardName [] = TARGET_BOARD_NAME; 162 | #endif 163 | 164 | /** Get Vendor Name string. 165 | \param str Pointer to buffer to store the string (max 60 characters). 166 | \return String length (including terminating NULL character) or 0 (no string). 167 | */ 168 | __STATIC_INLINE uint8_t DAP_GetVendorString (char *str) { 169 | uint8_t len; 170 | const char *Vendor = "Espressif"; 171 | strcpy(str, Vendor); 172 | len = (uint8_t)(strlen(Vendor) + 1U); 173 | return (len); 174 | } 175 | 176 | /** Get Product Name string. 177 | \param str Pointer to buffer to store the string (max 60 characters). 178 | \return String length (including terminating NULL character) or 0 (no string). 179 | */ 180 | __STATIC_INLINE uint8_t DAP_GetProductString (char *str) { 181 | uint8_t len; 182 | const char *Product = "ESP USB Bridge"; 183 | strcpy(str, Product); 184 | len = (uint8_t)(strlen(Product) + 1U); 185 | return (len); 186 | } 187 | 188 | /** Get Serial Number string. 189 | \param str Pointer to buffer to store the string (max 60 characters). 190 | \return String length (including terminating NULL character) or 0 (no string). 191 | */ 192 | __STATIC_INLINE uint8_t DAP_GetSerNumString (char *str) { 193 | (void)str; 194 | return (0U); 195 | } 196 | 197 | /** Get Target Device Vendor string. 198 | \param str Pointer to buffer to store the string (max 60 characters). 199 | \return String length (including terminating NULL character) or 0 (no string). 200 | */ 201 | __STATIC_INLINE uint8_t DAP_GetTargetDeviceVendorString (char *str) { 202 | #if TARGET_FIXED != 0 203 | uint8_t len; 204 | 205 | strcpy(str, TargetDeviceVendor); 206 | len = (uint8_t)(strlen(TargetDeviceVendor) + 1U); 207 | return (len); 208 | #else 209 | (void)str; 210 | return (0U); 211 | #endif 212 | } 213 | 214 | /** Get Target Device Name string. 215 | \param str Pointer to buffer to store the string (max 60 characters). 216 | \return String length (including terminating NULL character) or 0 (no string). 217 | */ 218 | __STATIC_INLINE uint8_t DAP_GetTargetDeviceNameString (char *str) { 219 | #if TARGET_FIXED != 0 220 | uint8_t len; 221 | 222 | strcpy(str, TargetDeviceName); 223 | len = (uint8_t)(strlen(TargetDeviceName) + 1U); 224 | return (len); 225 | #else 226 | (void)str; 227 | return (0U); 228 | #endif 229 | } 230 | 231 | /** Get Target Board Vendor string. 232 | \param str Pointer to buffer to store the string (max 60 characters). 233 | \return String length (including terminating NULL character) or 0 (no string). 234 | */ 235 | __STATIC_INLINE uint8_t DAP_GetTargetBoardVendorString (char *str) { 236 | #if TARGET_FIXED != 0 237 | uint8_t len; 238 | 239 | strcpy(str, TargetBoardVendor); 240 | len = (uint8_t)(strlen(TargetBoardVendor) + 1U); 241 | return (len); 242 | #else 243 | (void)str; 244 | return (0U); 245 | #endif 246 | } 247 | 248 | /** Get Target Board Name string. 249 | \param str Pointer to buffer to store the string (max 60 characters). 250 | \return String length (including terminating NULL character) or 0 (no string). 251 | */ 252 | __STATIC_INLINE uint8_t DAP_GetTargetBoardNameString (char *str) { 253 | #if TARGET_FIXED != 0 254 | uint8_t len; 255 | 256 | strcpy(str, TargetBoardName); 257 | len = (uint8_t)(strlen(TargetBoardName) + 1U); 258 | return (len); 259 | #else 260 | (void)str; 261 | return (0U); 262 | #endif 263 | } 264 | 265 | /** Get Product Firmware Version string. 266 | \param str Pointer to buffer to store the string (max 60 characters). 267 | \return String length (including terminating NULL character) or 0 (no string). 268 | */ 269 | __STATIC_INLINE uint8_t DAP_GetProductFirmwareVersionString (char *str) { 270 | (void)str; 271 | return (0U); 272 | } 273 | 274 | ///@} 275 | 276 | 277 | //************************************************************************************************** 278 | /** 279 | \defgroup DAP_Config_PortIO_gr CMSIS-DAP Hardware I/O Pin Access 280 | \ingroup DAP_ConfigIO_gr 281 | @{ 282 | 283 | Standard I/O Pins of the CMSIS-DAP Hardware Debug Port support standard JTAG mode 284 | and Serial Wire Debug (SWD) mode. In SWD mode only 2 pins are required to implement the debug 285 | interface of a device. The following I/O Pins are provided: 286 | 287 | JTAG I/O Pin | SWD I/O Pin | CMSIS-DAP Hardware pin mode 288 | ---------------------------- | -------------------- | --------------------------------------------- 289 | TCK: Test Clock | SWCLK: Clock | Output Push/Pull 290 | TMS: Test Mode Select | SWDIO: Data I/O | Output Push/Pull; Input (for receiving data) 291 | TDI: Test Data Input | | Output Push/Pull 292 | TDO: Test Data Output | | Input 293 | nTRST: Test Reset (optional) | | Output Open Drain with pull-up resistor 294 | nRESET: Device Reset | nRESET: Device Reset | Output Open Drain with pull-up resistor 295 | 296 | 297 | DAP Hardware I/O Pin Access Functions 298 | ------------------------------------- 299 | The various I/O Pins are accessed by functions that implement the Read, Write, Set, or Clear to 300 | these I/O Pins. 301 | 302 | For the SWDIO I/O Pin there are additional functions that are called in SWD I/O mode only. 303 | This functions are provided to achieve faster I/O that is possible with some advanced GPIO 304 | peripherals that can independently write/read a single I/O pin without affecting any other pins 305 | of the same I/O port. The following SWDIO I/O Pin functions are provided: 306 | - \ref PIN_SWDIO_OUT_ENABLE to enable the output mode from the DAP hardware. 307 | - \ref PIN_SWDIO_OUT_DISABLE to enable the input mode to the DAP hardware. 308 | - \ref PIN_SWDIO_IN to read from the SWDIO I/O pin with utmost possible speed. 309 | - \ref PIN_SWDIO_OUT to write to the SWDIO I/O pin with utmost possible speed. 310 | */ 311 | 312 | 313 | // Configure DAP I/O pins ------------------------------ 314 | 315 | /** Setup JTAG I/O pins: TCK, TMS, TDI, TDO, nTRST, and nRESET. 316 | Configures the DAP Hardware I/O pins for JTAG mode: 317 | - TCK, TMS, TDI, nTRST, nRESET to output mode and set to high level. 318 | - TDO to input mode. 319 | */ 320 | __STATIC_INLINE void PORT_JTAG_SETUP (void) { 321 | esp_init_jtag_pins(); 322 | } 323 | 324 | /** Setup SWD I/O pins: SWCLK, SWDIO, and nRESET. 325 | Configures the DAP Hardware I/O pins for Serial Wire Debug (SWD) mode: 326 | - SWCLK, SWDIO, nRESET to output mode and set to default high level. 327 | - TDI, nTRST to HighZ mode (pins are unused in SWD mode). 328 | */ 329 | __STATIC_INLINE void PORT_SWD_SETUP (void) { 330 | esp_init_swd_pins(); 331 | } 332 | 333 | /** Disable JTAG/SWD I/O Pins. 334 | Disables the DAP Hardware I/O pins which configures: 335 | - TCK/SWCLK, TMS/SWDIO, TDI, TDO, nTRST, nRESET to High-Z mode. 336 | */ 337 | __STATIC_INLINE void PORT_OFF (void) { 338 | esp_reset_dap_pins(); 339 | } 340 | 341 | 342 | // SWCLK/TCK I/O pin ------------------------------------- 343 | 344 | /** SWCLK/TCK I/O pin: Get Input. 345 | \return Current status of the SWCLK/TCK DAP hardware I/O pin. 346 | */ 347 | __STATIC_FORCEINLINE uint32_t PIN_SWCLK_TCK_IN (void) { 348 | return (0U); 349 | } 350 | 351 | /** SWCLK/TCK I/O pin: Set Output to High. 352 | Set the SWCLK/TCK DAP hardware I/O pin to high level. 353 | */ 354 | __STATIC_FORCEINLINE void PIN_SWCLK_TCK_SET (void) { 355 | esp_gpio_swclk_set(); 356 | } 357 | 358 | /** SWCLK/TCK I/O pin: Set Output to Low. 359 | Set the SWCLK/TCK DAP hardware I/O pin to low level. 360 | */ 361 | __STATIC_FORCEINLINE void PIN_SWCLK_TCK_CLR (void) { 362 | esp_gpio_swclk_clr(); 363 | } 364 | 365 | 366 | // SWDIO/TMS Pin I/O -------------------------------------- 367 | 368 | /** SWDIO/TMS I/O pin: Get Input. 369 | \return Current status of the SWDIO/TMS DAP hardware I/O pin. 370 | */ 371 | __STATIC_FORCEINLINE uint32_t PIN_SWDIO_TMS_IN (void) { 372 | return esp_gpio_swdio_read(); 373 | } 374 | 375 | /** SWDIO/TMS I/O pin: Set Output to High. 376 | Set the SWDIO/TMS DAP hardware I/O pin to high level. 377 | */ 378 | __STATIC_FORCEINLINE void PIN_SWDIO_TMS_SET (void) { 379 | esp_gpio_swdio_set(); 380 | } 381 | 382 | /** SWDIO/TMS I/O pin: Set Output to Low. 383 | Set the SWDIO/TMS DAP hardware I/O pin to low level. 384 | */ 385 | __STATIC_FORCEINLINE void PIN_SWDIO_TMS_CLR (void) { 386 | esp_gpio_swdio_clr(); 387 | } 388 | 389 | /** SWDIO I/O pin: Get Input (used in SWD mode only). 390 | \return Current status of the SWDIO DAP hardware I/O pin. 391 | */ 392 | __STATIC_FORCEINLINE uint32_t PIN_SWDIO_IN (void) { 393 | return esp_gpio_swdio_read(); 394 | } 395 | 396 | /** SWDIO I/O pin: Set Output (used in SWD mode only). 397 | \param bit Output value for the SWDIO DAP hardware I/O pin. 398 | */ 399 | __STATIC_FORCEINLINE void PIN_SWDIO_OUT (uint32_t bit) { 400 | esp_gpio_swdio_write(bit); 401 | } 402 | 403 | /** SWDIO I/O pin: Switch to Output mode (used in SWD mode only). 404 | Configure the SWDIO DAP hardware I/O pin to output mode. This function is 405 | called prior \ref PIN_SWDIO_OUT function calls. 406 | */ 407 | __STATIC_FORCEINLINE void PIN_SWDIO_OUT_ENABLE (void) { 408 | esp_gpio_swdio_out_enable(); 409 | } 410 | 411 | /** SWDIO I/O pin: Switch to Input mode (used in SWD mode only). 412 | Configure the SWDIO DAP hardware I/O pin to input mode. This function is 413 | called prior \ref PIN_SWDIO_IN function calls. 414 | */ 415 | __STATIC_FORCEINLINE void PIN_SWDIO_OUT_DISABLE (void) { 416 | esp_gpio_swdio_out_disable(); 417 | } 418 | 419 | 420 | // TDI Pin I/O --------------------------------------------- 421 | 422 | /** TDI I/O pin: Get Input. 423 | \return Current status of the TDI DAP hardware I/O pin. 424 | */ 425 | __STATIC_FORCEINLINE uint32_t PIN_TDI_IN (void) { 426 | return (0U); 427 | } 428 | 429 | /** TDI I/O pin: Set Output. 430 | \param bit Output value for the TDI DAP hardware I/O pin. 431 | */ 432 | __STATIC_FORCEINLINE void PIN_TDI_OUT (uint32_t bit) { 433 | esp_gpio_tdi_write(bit); 434 | } 435 | 436 | 437 | // TDO Pin I/O --------------------------------------------- 438 | 439 | /** TDO I/O pin: Get Input. 440 | \return Current status of the TDO DAP hardware I/O pin. 441 | */ 442 | __STATIC_FORCEINLINE uint32_t PIN_TDO_IN (void) { 443 | return esp_gpio_tdo_read(); 444 | } 445 | 446 | 447 | // nTRST Pin I/O ------------------------------------------- 448 | 449 | /** nTRST I/O pin: Get Input. 450 | \return Current status of the nTRST DAP hardware I/O pin. 451 | */ 452 | __STATIC_FORCEINLINE uint32_t PIN_nTRST_IN (void) { 453 | return (0U); 454 | } 455 | 456 | /** nTRST I/O pin: Set Output. 457 | \param bit JTAG TRST Test Reset pin status: 458 | - 0: issue a JTAG TRST Test Reset. 459 | - 1: release JTAG TRST Test Reset. 460 | */ 461 | __STATIC_FORCEINLINE void PIN_nTRST_OUT (uint32_t bit) { 462 | ; 463 | } 464 | 465 | // nRESET Pin I/O------------------------------------------ 466 | 467 | /** nRESET I/O pin: Get Input. 468 | \return Current status of the nRESET DAP hardware I/O pin. 469 | */ 470 | __STATIC_FORCEINLINE uint32_t PIN_nRESET_IN (void) { 471 | return (0U); 472 | } 473 | 474 | /** nRESET I/O pin: Set Output. 475 | \param bit target device hardware reset pin status: 476 | - 0: issue a device hardware reset. 477 | - 1: release device hardware reset. 478 | */ 479 | __STATIC_FORCEINLINE void PIN_nRESET_OUT (uint32_t bit) { 480 | ; 481 | } 482 | 483 | ///@} 484 | 485 | 486 | //************************************************************************************************** 487 | /** 488 | \defgroup DAP_Config_LEDs_gr CMSIS-DAP Hardware Status LEDs 489 | \ingroup DAP_ConfigIO_gr 490 | @{ 491 | 492 | CMSIS-DAP Hardware may provide LEDs that indicate the status of the CMSIS-DAP Debug Unit. 493 | 494 | It is recommended to provide the following LEDs for status indication: 495 | - Connect LED: is active when the DAP hardware is connected to a debugger. 496 | - Running LED: is active when the debugger has put the target device into running state. 497 | */ 498 | 499 | /** Debug Unit: Set status of Connected LED. 500 | \param bit status of the Connect LED. 501 | - 1: Connect LED ON: debugger is connected to CMSIS-DAP Debug Unit. 502 | - 0: Connect LED OFF: debugger is not connected to CMSIS-DAP Debug Unit. 503 | */ 504 | __STATIC_INLINE void LED_CONNECTED_OUT (uint32_t bit) {} 505 | 506 | /** Debug Unit: Set status Target Running LED. 507 | \param bit status of the Target Running LED. 508 | - 1: Target Running LED ON: program execution in target started. 509 | - 0: Target Running LED OFF: program execution in target stopped. 510 | */ 511 | __STATIC_INLINE void LED_RUNNING_OUT (uint32_t bit) { 512 | esp_gpio_swd_blink(bit); 513 | } 514 | 515 | ///@} 516 | 517 | 518 | //************************************************************************************************** 519 | /** 520 | \defgroup DAP_Config_Timestamp_gr CMSIS-DAP Timestamp 521 | \ingroup DAP_ConfigIO_gr 522 | @{ 523 | Access function for Test Domain Timer. 524 | 525 | The value of the Test Domain Timer in the Debug Unit is returned by the function \ref TIMESTAMP_GET. By 526 | default, the DWT timer is used. The frequency of this timer is configured with \ref TIMESTAMP_CLOCK. 527 | 528 | */ 529 | 530 | /** Get timestamp of Test Domain Timer. 531 | \return Current timestamp value. 532 | */ 533 | __STATIC_INLINE uint32_t TIMESTAMP_GET (void) { 534 | return esp_timer_get_time(); 535 | } 536 | 537 | ///@} 538 | 539 | 540 | //************************************************************************************************** 541 | /** 542 | \defgroup DAP_Config_Initialization_gr CMSIS-DAP Initialization 543 | \ingroup DAP_ConfigIO_gr 544 | @{ 545 | 546 | CMSIS-DAP Hardware I/O and LED Pins are initialized with the function \ref DAP_SETUP. 547 | */ 548 | 549 | /** Setup of the Debug Unit I/O pins and LEDs (called when Debug Unit is initialized). 550 | This function performs the initialization of the CMSIS-DAP Hardware I/O Pins and the 551 | Status LEDs. In detail the operation of Hardware I/O and LED pins are enabled and set: 552 | - I/O clock system enabled. 553 | - all I/O pins: input buffer enabled, output pins are set to HighZ mode. 554 | - for nTRST, nRESET a weak pull-up (if available) is enabled. 555 | - LED output pins are enabled and LEDs are turned off. 556 | */ 557 | __STATIC_INLINE void DAP_SETUP (void) { 558 | ; 559 | } 560 | 561 | /** Reset Target Device with custom specific I/O pin or command sequence. 562 | This function allows the optional implementation of a device specific reset sequence. 563 | It is called when the command \ref DAP_ResetTarget and is for example required 564 | when a device needs a time-critical unlock sequence that enables the debug port. 565 | \return 0 = no device specific reset sequence is implemented.\n 566 | 1 = a device specific reset sequence is implemented. 567 | */ 568 | __STATIC_INLINE uint8_t RESET_TARGET (void) { 569 | return (0U); // change to '1' when a device reset sequence is implemented 570 | } 571 | 572 | ///@} 573 | 574 | 575 | #endif /* __DAP_CONFIG_H__ */ 576 | -------------------------------------------------------------------------------- /components/DAP/Include/DAP.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2022 ARM Limited. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the License); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an AS IS BASIS, WITHOUT 14 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ---------------------------------------------------------------------- 19 | * 20 | * $Date: 5. December 2022 21 | * $Revision: V2.1.2 22 | * 23 | * Project: CMSIS-DAP Include 24 | * Title: DAP.h Definitions 25 | * 26 | *---------------------------------------------------------------------------*/ 27 | 28 | #ifndef __DAP_H__ 29 | #define __DAP_H__ 30 | 31 | 32 | // DAP Firmware Version 33 | #ifdef DAP_FW_V1 34 | #define DAP_FW_VER "1.3.0" 35 | #else 36 | #define DAP_FW_VER "2.1.2" 37 | #endif 38 | 39 | // DAP Command IDs 40 | #define ID_DAP_Info 0x00U 41 | #define ID_DAP_HostStatus 0x01U 42 | #define ID_DAP_Connect 0x02U 43 | #define ID_DAP_Disconnect 0x03U 44 | #define ID_DAP_TransferConfigure 0x04U 45 | #define ID_DAP_Transfer 0x05U 46 | #define ID_DAP_TransferBlock 0x06U 47 | #define ID_DAP_TransferAbort 0x07U 48 | #define ID_DAP_WriteABORT 0x08U 49 | #define ID_DAP_Delay 0x09U 50 | #define ID_DAP_ResetTarget 0x0AU 51 | #define ID_DAP_SWJ_Pins 0x10U 52 | #define ID_DAP_SWJ_Clock 0x11U 53 | #define ID_DAP_SWJ_Sequence 0x12U 54 | #define ID_DAP_SWD_Configure 0x13U 55 | #define ID_DAP_SWD_Sequence 0x1DU 56 | #define ID_DAP_JTAG_Sequence 0x14U 57 | #define ID_DAP_JTAG_Configure 0x15U 58 | #define ID_DAP_JTAG_IDCODE 0x16U 59 | #define ID_DAP_SWO_Transport 0x17U 60 | #define ID_DAP_SWO_Mode 0x18U 61 | #define ID_DAP_SWO_Baudrate 0x19U 62 | #define ID_DAP_SWO_Control 0x1AU 63 | #define ID_DAP_SWO_Status 0x1BU 64 | #define ID_DAP_SWO_ExtendedStatus 0x1EU 65 | #define ID_DAP_SWO_Data 0x1CU 66 | #define ID_DAP_UART_Transport 0x1FU 67 | #define ID_DAP_UART_Configure 0x20U 68 | #define ID_DAP_UART_Control 0x22U 69 | #define ID_DAP_UART_Status 0x23U 70 | #define ID_DAP_UART_Transfer 0x21U 71 | 72 | #define ID_DAP_QueueCommands 0x7EU 73 | #define ID_DAP_ExecuteCommands 0x7FU 74 | 75 | // DAP Vendor Command IDs 76 | #define ID_DAP_Vendor0 0x80U 77 | #define ID_DAP_Vendor1 0x81U 78 | #define ID_DAP_Vendor2 0x82U 79 | #define ID_DAP_Vendor3 0x83U 80 | #define ID_DAP_Vendor4 0x84U 81 | #define ID_DAP_Vendor5 0x85U 82 | #define ID_DAP_Vendor6 0x86U 83 | #define ID_DAP_Vendor7 0x87U 84 | #define ID_DAP_Vendor8 0x88U 85 | #define ID_DAP_Vendor9 0x89U 86 | #define ID_DAP_Vendor10 0x8AU 87 | #define ID_DAP_Vendor11 0x8BU 88 | #define ID_DAP_Vendor12 0x8CU 89 | #define ID_DAP_Vendor13 0x8DU 90 | #define ID_DAP_Vendor14 0x8EU 91 | #define ID_DAP_Vendor15 0x8FU 92 | #define ID_DAP_Vendor16 0x90U 93 | #define ID_DAP_Vendor17 0x91U 94 | #define ID_DAP_Vendor18 0x92U 95 | #define ID_DAP_Vendor19 0x93U 96 | #define ID_DAP_Vendor20 0x94U 97 | #define ID_DAP_Vendor21 0x95U 98 | #define ID_DAP_Vendor22 0x96U 99 | #define ID_DAP_Vendor23 0x97U 100 | #define ID_DAP_Vendor24 0x98U 101 | #define ID_DAP_Vendor25 0x99U 102 | #define ID_DAP_Vendor26 0x9AU 103 | #define ID_DAP_Vendor27 0x9BU 104 | #define ID_DAP_Vendor28 0x9CU 105 | #define ID_DAP_Vendor29 0x9DU 106 | #define ID_DAP_Vendor30 0x9EU 107 | #define ID_DAP_Vendor31 0x9FU 108 | 109 | #define ID_DAP_Invalid 0xFFU 110 | 111 | // DAP Status Code 112 | #define DAP_OK 0U 113 | #define DAP_ERROR 0xFFU 114 | 115 | // DAP ID 116 | #define DAP_ID_VENDOR 1U 117 | #define DAP_ID_PRODUCT 2U 118 | #define DAP_ID_SER_NUM 3U 119 | #define DAP_ID_DAP_FW_VER 4U 120 | #define DAP_ID_DEVICE_VENDOR 5U 121 | #define DAP_ID_DEVICE_NAME 6U 122 | #define DAP_ID_BOARD_VENDOR 7U 123 | #define DAP_ID_BOARD_NAME 8U 124 | #define DAP_ID_PRODUCT_FW_VER 9U 125 | #define DAP_ID_CAPABILITIES 0xF0U 126 | #define DAP_ID_TIMESTAMP_CLOCK 0xF1U 127 | #define DAP_ID_UART_RX_BUFFER_SIZE 0xFBU 128 | #define DAP_ID_UART_TX_BUFFER_SIZE 0xFCU 129 | #define DAP_ID_SWO_BUFFER_SIZE 0xFDU 130 | #define DAP_ID_PACKET_COUNT 0xFEU 131 | #define DAP_ID_PACKET_SIZE 0xFFU 132 | 133 | // DAP Host Status 134 | #define DAP_DEBUGGER_CONNECTED 0U 135 | #define DAP_TARGET_RUNNING 1U 136 | 137 | // DAP Port 138 | #define DAP_PORT_AUTODETECT 0U // Autodetect Port 139 | #define DAP_PORT_DISABLED 0U // Port Disabled (I/O pins in High-Z) 140 | #define DAP_PORT_SWD 1U // SWD Port (SWCLK, SWDIO) + nRESET 141 | #define DAP_PORT_JTAG 2U // JTAG Port (TCK, TMS, TDI, TDO, nTRST) + nRESET 142 | 143 | // DAP SWJ Pins 144 | #define DAP_SWJ_SWCLK_TCK 0 // SWCLK/TCK 145 | #define DAP_SWJ_SWDIO_TMS 1 // SWDIO/TMS 146 | #define DAP_SWJ_TDI 2 // TDI 147 | #define DAP_SWJ_TDO 3 // TDO 148 | #define DAP_SWJ_nTRST 5 // nTRST 149 | #define DAP_SWJ_nRESET 7 // nRESET 150 | 151 | // DAP Transfer Request 152 | #define DAP_TRANSFER_APnDP (1U<<0) 153 | #define DAP_TRANSFER_RnW (1U<<1) 154 | #define DAP_TRANSFER_A2 (1U<<2) 155 | #define DAP_TRANSFER_A3 (1U<<3) 156 | #define DAP_TRANSFER_MATCH_VALUE (1U<<4) 157 | #define DAP_TRANSFER_MATCH_MASK (1U<<5) 158 | #define DAP_TRANSFER_TIMESTAMP (1U<<7) 159 | 160 | // DAP Transfer Response 161 | #define DAP_TRANSFER_OK (1U<<0) 162 | #define DAP_TRANSFER_WAIT (1U<<1) 163 | #define DAP_TRANSFER_FAULT (1U<<2) 164 | #define DAP_TRANSFER_ERROR (1U<<3) 165 | #define DAP_TRANSFER_MISMATCH (1U<<4) 166 | 167 | // DAP SWO Trace Mode 168 | #define DAP_SWO_OFF 0U 169 | #define DAP_SWO_UART 1U 170 | #define DAP_SWO_MANCHESTER 2U 171 | 172 | // DAP SWO Trace Status 173 | #define DAP_SWO_CAPTURE_ACTIVE (1U<<0) 174 | #define DAP_SWO_CAPTURE_PAUSED (1U<<1) 175 | #define DAP_SWO_STREAM_ERROR (1U<<6) 176 | #define DAP_SWO_BUFFER_OVERRUN (1U<<7) 177 | 178 | // DAP UART Transport 179 | #define DAP_UART_TRANSPORT_NONE 0U 180 | #define DAP_UART_TRANSPORT_USB_COM_PORT 1U 181 | #define DAP_UART_TRANSPORT_DAP_COMMAND 2U 182 | 183 | // DAP UART Control 184 | #define DAP_UART_CONTROL_RX_ENABLE (1U<<0) 185 | #define DAP_UART_CONTROL_RX_DISABLE (1U<<1) 186 | #define DAP_UART_CONTROL_RX_BUF_FLUSH (1U<<2) 187 | #define DAP_UART_CONTROL_TX_ENABLE (1U<<4) 188 | #define DAP_UART_CONTROL_TX_DISABLE (1U<<5) 189 | #define DAP_UART_CONTROL_TX_BUF_FLUSH (1U<<6) 190 | 191 | // DAP UART Status 192 | #define DAP_UART_STATUS_RX_ENABLED (1U<<0) 193 | #define DAP_UART_STATUS_RX_DATA_LOST (1U<<1) 194 | #define DAP_UART_STATUS_FRAMING_ERROR (1U<<2) 195 | #define DAP_UART_STATUS_PARITY_ERROR (1U<<3) 196 | #define DAP_UART_STATUS_TX_ENABLED (1U<<4) 197 | 198 | // DAP UART Configure Error 199 | #define DAP_UART_CFG_ERROR_DATA_BITS (1U<<0) 200 | #define DAP_UART_CFG_ERROR_PARITY (1U<<1) 201 | #define DAP_UART_CFG_ERROR_STOP_BITS (1U<<2) 202 | 203 | // Debug Port Register Addresses 204 | #define DP_IDCODE 0x00U // IDCODE Register (SW Read only) 205 | #define DP_ABORT 0x00U // Abort Register (SW Write only) 206 | #define DP_CTRL_STAT 0x04U // Control & Status 207 | #define DP_WCR 0x04U // Wire Control Register (SW Only) 208 | #define DP_SELECT 0x08U // Select Register (JTAG R/W & SW W) 209 | #define DP_RESEND 0x08U // Resend (SW Read Only) 210 | #define DP_RDBUFF 0x0CU // Read Buffer (Read Only) 211 | 212 | // JTAG IR Codes 213 | #define JTAG_ABORT 0x08U 214 | #define JTAG_DPACC 0x0AU 215 | #define JTAG_APACC 0x0BU 216 | #define JTAG_IDCODE 0x0EU 217 | #define JTAG_BYPASS 0x0FU 218 | 219 | // JTAG Sequence Info 220 | #define JTAG_SEQUENCE_TCK 0x3FU // TCK count 221 | #define JTAG_SEQUENCE_TMS 0x40U // TMS value 222 | #define JTAG_SEQUENCE_TDO 0x80U // TDO capture 223 | 224 | // SWD Sequence Info 225 | #define SWD_SEQUENCE_CLK 0x3FU // SWCLK count 226 | #define SWD_SEQUENCE_DIN 0x80U // SWDIO capture 227 | 228 | 229 | #include 230 | #include 231 | #include "compiler.h" 232 | 233 | // DAP Data structure 234 | typedef struct { 235 | uint8_t debug_port; // Debug Port 236 | uint8_t fast_clock; // Fast Clock Flag 237 | uint8_t padding[2]; 238 | uint32_t clock_delay; // Clock Delay 239 | uint32_t timestamp; // Last captured Timestamp 240 | struct { // Transfer Configuration 241 | uint8_t idle_cycles; // Idle cycles after transfer 242 | uint8_t padding[3]; 243 | uint16_t retry_count; // Number of retries after WAIT response 244 | uint16_t match_retry; // Number of retries if read value does not match 245 | uint32_t match_mask; // Match Mask 246 | } transfer; 247 | #if (DAP_SWD != 0) 248 | struct { // SWD Configuration 249 | uint8_t turnaround; // Turnaround period 250 | uint8_t data_phase; // Always generate Data Phase 251 | } swd_conf; 252 | #endif 253 | #if (DAP_JTAG != 0) 254 | struct { // JTAG Device Chain 255 | uint8_t count; // Number of devices 256 | uint8_t index; // Device index (device at TDO has index 0) 257 | #if (DAP_JTAG_DEV_CNT != 0) 258 | uint8_t ir_length[DAP_JTAG_DEV_CNT]; // IR Length in bits 259 | uint16_t ir_before[DAP_JTAG_DEV_CNT]; // Bits before IR 260 | uint16_t ir_after [DAP_JTAG_DEV_CNT]; // Bits after IR 261 | #endif 262 | } jtag_dev; 263 | #endif 264 | } DAP_Data_t; 265 | 266 | extern DAP_Data_t DAP_Data; // DAP Data 267 | extern volatile uint8_t DAP_TransferAbort; // Transfer Abort Flag 268 | 269 | 270 | #ifdef __cplusplus 271 | extern "C" 272 | { 273 | #endif 274 | 275 | // Functions 276 | extern void SWJ_Sequence (uint32_t count, const uint8_t *data); 277 | extern void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi); 278 | extern void JTAG_Sequence (uint32_t info, const uint8_t *tdi, uint8_t *tdo); 279 | extern void JTAG_IR (uint32_t ir); 280 | extern uint32_t JTAG_ReadIDCode (void); 281 | extern void JTAG_WriteAbort (uint32_t data); 282 | extern uint8_t JTAG_Transfer (uint32_t request, uint32_t *data); 283 | extern uint8_t SWD_Transfer (uint32_t request, uint32_t *data); 284 | 285 | extern void Delayms (uint32_t delay); 286 | 287 | extern uint32_t SWO_Transport (const uint8_t *request, uint8_t *response); 288 | extern uint32_t SWO_Mode (const uint8_t *request, uint8_t *response); 289 | extern uint32_t SWO_Baudrate (const uint8_t *request, uint8_t *response); 290 | extern uint32_t SWO_Control (const uint8_t *request, uint8_t *response); 291 | extern uint32_t SWO_Status (uint8_t *response); 292 | extern uint32_t SWO_ExtendedStatus (const uint8_t *request, uint8_t *response); 293 | extern uint32_t SWO_Data (const uint8_t *request, uint8_t *response); 294 | 295 | extern void SWO_QueueTransfer (uint8_t *buf, uint32_t num); 296 | extern void SWO_AbortTransfer (void); 297 | extern void SWO_TransferComplete (void); 298 | 299 | extern uint32_t SWO_Mode_UART (uint32_t enable); 300 | extern uint32_t SWO_Baudrate_UART (uint32_t baudrate); 301 | extern uint32_t SWO_Control_UART (uint32_t active); 302 | extern void SWO_Capture_UART (uint8_t *buf, uint32_t num); 303 | extern uint32_t SWO_GetCount_UART (void); 304 | 305 | extern uint32_t SWO_Mode_Manchester (uint32_t enable); 306 | extern uint32_t SWO_Baudrate_Manchester (uint32_t baudrate); 307 | extern uint32_t SWO_Control_Manchester (uint32_t active); 308 | extern void SWO_Capture_Manchester (uint8_t *buf, uint32_t num); 309 | extern uint32_t SWO_GetCount_Manchester (void); 310 | 311 | extern uint32_t UART_Transport (const uint8_t *request, uint8_t *response); 312 | extern uint32_t UART_Configure (const uint8_t *request, uint8_t *response); 313 | extern uint32_t UART_Control (const uint8_t *request, uint8_t *response); 314 | extern uint32_t UART_Status (uint8_t *response); 315 | extern uint32_t UART_Transfer (const uint8_t *request, uint8_t *response); 316 | 317 | extern uint8_t USB_COM_PORT_Activate (uint32_t cmd); 318 | 319 | extern uint32_t DAP_ProcessVendorCommand (const uint8_t *request, uint8_t *response); 320 | extern uint32_t DAP_ProcessCommand (const uint8_t *request, uint8_t *response); 321 | extern uint32_t DAP_ExecuteCommand (const uint8_t *request, uint8_t *response); 322 | 323 | extern void DAP_Setup (void); 324 | 325 | // Configurable delay for clock generation 326 | #ifndef DELAY_SLOW_CYCLES 327 | #define DELAY_SLOW_CYCLES 3U // Number of cycles for one iteration 328 | #endif 329 | #if defined(true) 330 | __STATIC_FORCEINLINE void PIN_DELAY_SLOW (uint32_t delay) { 331 | uint32_t count = delay; 332 | while (--count); 333 | } 334 | #else 335 | __STATIC_FORCEINLINE void PIN_DELAY_SLOW (uint32_t delay) 336 | { 337 | //TODO: delay function in asm 338 | } 339 | #endif 340 | 341 | // Fixed delay for fast clock generation 342 | #ifndef DELAY_FAST_CYCLES 343 | #define DELAY_FAST_CYCLES 0U // Number of cycles: 0..3 344 | #endif 345 | __STATIC_FORCEINLINE void PIN_DELAY_FAST (void) { 346 | #if (DELAY_FAST_CYCLES >= 1U) 347 | __NOP(); 348 | #endif 349 | #if (DELAY_FAST_CYCLES >= 2U) 350 | __NOP(); 351 | #endif 352 | #if (DELAY_FAST_CYCLES >= 3U) 353 | __NOP(); 354 | #endif 355 | } 356 | 357 | #ifdef __cplusplus 358 | } 359 | #endif 360 | 361 | 362 | #endif /* __DAP_H__ */ 363 | -------------------------------------------------------------------------------- /components/DAP/Source/DAP_vendor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2017 ARM Limited. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the License); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an AS IS BASIS, WITHOUT 14 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ---------------------------------------------------------------------- 19 | * 20 | * $Date: 1. December 2017 21 | * $Revision: V2.0.0 22 | * 23 | * Project: CMSIS-DAP Source 24 | * Title: DAP_vendor.c CMSIS-DAP Vendor Commands 25 | * 26 | *---------------------------------------------------------------------------*/ 27 | 28 | #include "DAP_config.h" 29 | #include "DAP.h" 30 | 31 | //************************************************************************************************** 32 | /** 33 | \defgroup DAP_Vendor_Adapt_gr Adapt Vendor Commands 34 | \ingroup DAP_Vendor_gr 35 | @{ 36 | 37 | The file DAP_vendor.c provides template source code for extension of a Debug Unit with 38 | Vendor Commands. Copy this file to the project folder of the Debug Unit and add the 39 | file to the MDK-ARM project under the file group Configuration. 40 | */ 41 | 42 | /** Process DAP Vendor Command and prepare Response Data 43 | \param request pointer to request data 44 | \param response pointer to response data 45 | \return number of bytes in response (lower 16 bits) 46 | number of bytes in request (upper 16 bits) 47 | */ 48 | uint32_t DAP_ProcessVendorCommand(const uint8_t *request, uint8_t *response) { 49 | uint32_t num = (1U << 16) | 1U; 50 | 51 | *response++ = *request; // copy Command ID 52 | 53 | switch (*request++) { // first byte in request is Command ID 54 | case ID_DAP_Vendor0: 55 | #if 0 // example user command 56 | num += 1U << 16; // increment request count 57 | if (*request == 1U) { // when first command data byte is 1 58 | *response++ = 'X'; // send 'X' as response 59 | num++; // increment response count 60 | } 61 | #endif 62 | break; 63 | 64 | case ID_DAP_Vendor1: break; 65 | case ID_DAP_Vendor2: break; 66 | case ID_DAP_Vendor3: break; 67 | case ID_DAP_Vendor4: break; 68 | case ID_DAP_Vendor5: break; 69 | case ID_DAP_Vendor6: break; 70 | case ID_DAP_Vendor7: break; 71 | case ID_DAP_Vendor8: break; 72 | case ID_DAP_Vendor9: break; 73 | case ID_DAP_Vendor10: break; 74 | case ID_DAP_Vendor11: break; 75 | case ID_DAP_Vendor12: break; 76 | case ID_DAP_Vendor13: break; 77 | case ID_DAP_Vendor14: break; 78 | case ID_DAP_Vendor15: break; 79 | case ID_DAP_Vendor16: break; 80 | case ID_DAP_Vendor17: break; 81 | case ID_DAP_Vendor18: break; 82 | case ID_DAP_Vendor19: break; 83 | case ID_DAP_Vendor20: break; 84 | case ID_DAP_Vendor21: break; 85 | case ID_DAP_Vendor22: break; 86 | case ID_DAP_Vendor23: break; 87 | case ID_DAP_Vendor24: break; 88 | case ID_DAP_Vendor25: break; 89 | case ID_DAP_Vendor26: break; 90 | case ID_DAP_Vendor27: break; 91 | case ID_DAP_Vendor28: break; 92 | case ID_DAP_Vendor29: break; 93 | case ID_DAP_Vendor30: break; 94 | case ID_DAP_Vendor31: break; 95 | } 96 | 97 | return (num); 98 | } 99 | 100 | ///@} 101 | -------------------------------------------------------------------------------- /components/DAP/Source/JTAG_DP.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2017 ARM Limited. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the License); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an AS IS BASIS, WITHOUT 14 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ---------------------------------------------------------------------- 19 | * 20 | * $Date: 1. December 2017 21 | * $Revision: V2.0.0 22 | * 23 | * Project: CMSIS-DAP Source 24 | * Title: JTAG_DP.c CMSIS-DAP JTAG DP I/O 25 | * 26 | *---------------------------------------------------------------------------*/ 27 | 28 | #include "DAP_config.h" 29 | #include "DAP.h" 30 | 31 | 32 | // JTAG Macros 33 | 34 | #define PIN_TCK_SET PIN_SWCLK_TCK_SET 35 | #define PIN_TCK_CLR PIN_SWCLK_TCK_CLR 36 | #define PIN_TMS_SET PIN_SWDIO_TMS_SET 37 | #define PIN_TMS_CLR PIN_SWDIO_TMS_CLR 38 | 39 | #define JTAG_CYCLE_TCK() \ 40 | PIN_TCK_CLR(); \ 41 | PIN_DELAY(); \ 42 | PIN_TCK_SET(); \ 43 | PIN_DELAY() 44 | 45 | #define JTAG_CYCLE_TDI(tdi) \ 46 | PIN_TDI_OUT(tdi); \ 47 | PIN_TCK_CLR(); \ 48 | PIN_DELAY(); \ 49 | PIN_TCK_SET(); \ 50 | PIN_DELAY() 51 | 52 | #define JTAG_CYCLE_TDO(tdo) \ 53 | PIN_TCK_CLR(); \ 54 | PIN_DELAY(); \ 55 | tdo = PIN_TDO_IN(); \ 56 | PIN_TCK_SET(); \ 57 | PIN_DELAY() 58 | 59 | #define JTAG_CYCLE_TDIO(tdi,tdo) \ 60 | PIN_TDI_OUT(tdi); \ 61 | PIN_TCK_CLR(); \ 62 | PIN_DELAY(); \ 63 | tdo = PIN_TDO_IN(); \ 64 | PIN_TCK_SET(); \ 65 | PIN_DELAY() 66 | 67 | #define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay) 68 | 69 | 70 | #if (DAP_JTAG != 0) 71 | 72 | 73 | // Generate JTAG Sequence 74 | // info: sequence information 75 | // tdi: pointer to TDI generated data 76 | // tdo: pointer to TDO captured data 77 | // return: none 78 | void JTAG_Sequence (uint32_t info, const uint8_t *tdi, uint8_t *tdo) { 79 | uint32_t i_val; 80 | uint32_t o_val; 81 | uint32_t bit; 82 | uint32_t n, k; 83 | 84 | n = info & JTAG_SEQUENCE_TCK; 85 | if (n == 0U) { 86 | n = 64U; 87 | } 88 | 89 | if (info & JTAG_SEQUENCE_TMS) { 90 | PIN_TMS_SET(); 91 | } else { 92 | PIN_TMS_CLR(); 93 | } 94 | 95 | while (n) { 96 | i_val = *tdi++; 97 | o_val = 0U; 98 | for (k = 8U; k && n; k--, n--) { 99 | JTAG_CYCLE_TDIO(i_val, bit); 100 | i_val >>= 1; 101 | o_val >>= 1; 102 | o_val |= bit << 7; 103 | } 104 | o_val >>= k; 105 | if (info & JTAG_SEQUENCE_TDO) { 106 | *tdo++ = (uint8_t)o_val; 107 | } 108 | } 109 | } 110 | 111 | 112 | // JTAG Set IR 113 | // ir: IR value 114 | // return: none 115 | #define JTAG_IR_Function(speed) /**/ \ 116 | static void JTAG_IR_##speed (uint32_t ir) { \ 117 | uint32_t n; \ 118 | \ 119 | PIN_TMS_SET(); \ 120 | JTAG_CYCLE_TCK(); /* Select-DR-Scan */ \ 121 | JTAG_CYCLE_TCK(); /* Select-IR-Scan */ \ 122 | PIN_TMS_CLR(); \ 123 | JTAG_CYCLE_TCK(); /* Capture-IR */ \ 124 | JTAG_CYCLE_TCK(); /* Shift-IR */ \ 125 | \ 126 | PIN_TDI_OUT(1U); \ 127 | for (n = DAP_Data.jtag_dev.ir_before[DAP_Data.jtag_dev.index]; n; n--) { \ 128 | JTAG_CYCLE_TCK(); /* Bypass before data */ \ 129 | } \ 130 | for (n = DAP_Data.jtag_dev.ir_length[DAP_Data.jtag_dev.index] - 1U; n; n--) { \ 131 | JTAG_CYCLE_TDI(ir); /* Set IR bits (except last) */ \ 132 | ir >>= 1; \ 133 | } \ 134 | n = DAP_Data.jtag_dev.ir_after[DAP_Data.jtag_dev.index]; \ 135 | if (n) { \ 136 | JTAG_CYCLE_TDI(ir); /* Set last IR bit */ \ 137 | PIN_TDI_OUT(1U); \ 138 | for (--n; n; n--) { \ 139 | JTAG_CYCLE_TCK(); /* Bypass after data */ \ 140 | } \ 141 | PIN_TMS_SET(); \ 142 | JTAG_CYCLE_TCK(); /* Bypass & Exit1-IR */ \ 143 | } else { \ 144 | PIN_TMS_SET(); \ 145 | JTAG_CYCLE_TDI(ir); /* Set last IR bit & Exit1-IR */ \ 146 | } \ 147 | \ 148 | JTAG_CYCLE_TCK(); /* Update-IR */ \ 149 | PIN_TMS_CLR(); \ 150 | JTAG_CYCLE_TCK(); /* Idle */ \ 151 | PIN_TDI_OUT(1U); \ 152 | } 153 | 154 | 155 | // JTAG Transfer I/O 156 | // request: A[3:2] RnW APnDP 157 | // data: DATA[31:0] 158 | // return: ACK[2:0] 159 | #define JTAG_TransferFunction(speed) /**/ \ 160 | static uint8_t JTAG_Transfer##speed (uint32_t request, uint32_t *data) { \ 161 | uint32_t ack; \ 162 | uint32_t bit; \ 163 | uint32_t val; \ 164 | uint32_t n; \ 165 | \ 166 | PIN_TMS_SET(); \ 167 | JTAG_CYCLE_TCK(); /* Select-DR-Scan */ \ 168 | PIN_TMS_CLR(); \ 169 | JTAG_CYCLE_TCK(); /* Capture-DR */ \ 170 | JTAG_CYCLE_TCK(); /* Shift-DR */ \ 171 | \ 172 | for (n = DAP_Data.jtag_dev.index; n; n--) { \ 173 | JTAG_CYCLE_TCK(); /* Bypass before data */ \ 174 | } \ 175 | \ 176 | JTAG_CYCLE_TDIO(request >> 1, bit); /* Set RnW, Get ACK.0 */ \ 177 | ack = bit << 1; \ 178 | JTAG_CYCLE_TDIO(request >> 2, bit); /* Set A2, Get ACK.1 */ \ 179 | ack |= bit << 0; \ 180 | JTAG_CYCLE_TDIO(request >> 3, bit); /* Set A3, Get ACK.2 */ \ 181 | ack |= bit << 2; \ 182 | \ 183 | if (ack != DAP_TRANSFER_OK) { \ 184 | /* Exit on error */ \ 185 | PIN_TMS_SET(); \ 186 | JTAG_CYCLE_TCK(); /* Exit1-DR */ \ 187 | goto exit; \ 188 | } \ 189 | \ 190 | if (request & DAP_TRANSFER_RnW) { \ 191 | /* Read Transfer */ \ 192 | val = 0U; \ 193 | for (n = 31U; n; n--) { \ 194 | JTAG_CYCLE_TDO(bit); /* Get D0..D30 */ \ 195 | val |= bit << 31; \ 196 | val >>= 1; \ 197 | } \ 198 | n = DAP_Data.jtag_dev.count - DAP_Data.jtag_dev.index - 1U; \ 199 | if (n) { \ 200 | JTAG_CYCLE_TDO(bit); /* Get D31 */ \ 201 | for (--n; n; n--) { \ 202 | JTAG_CYCLE_TCK(); /* Bypass after data */ \ 203 | } \ 204 | PIN_TMS_SET(); \ 205 | JTAG_CYCLE_TCK(); /* Bypass & Exit1-DR */ \ 206 | } else { \ 207 | PIN_TMS_SET(); \ 208 | JTAG_CYCLE_TDO(bit); /* Get D31 & Exit1-DR */ \ 209 | } \ 210 | val |= bit << 31; \ 211 | if (data) { *data = val; } \ 212 | } else { \ 213 | /* Write Transfer */ \ 214 | val = *data; \ 215 | for (n = 31U; n; n--) { \ 216 | JTAG_CYCLE_TDI(val); /* Set D0..D30 */ \ 217 | val >>= 1; \ 218 | } \ 219 | n = DAP_Data.jtag_dev.count - DAP_Data.jtag_dev.index - 1U; \ 220 | if (n) { \ 221 | JTAG_CYCLE_TDI(val); /* Set D31 */ \ 222 | for (--n; n; n--) { \ 223 | JTAG_CYCLE_TCK(); /* Bypass after data */ \ 224 | } \ 225 | PIN_TMS_SET(); \ 226 | JTAG_CYCLE_TCK(); /* Bypass & Exit1-DR */ \ 227 | } else { \ 228 | PIN_TMS_SET(); \ 229 | JTAG_CYCLE_TDI(val); /* Set D31 & Exit1-DR */ \ 230 | } \ 231 | } \ 232 | \ 233 | exit: \ 234 | JTAG_CYCLE_TCK(); /* Update-DR */ \ 235 | PIN_TMS_CLR(); \ 236 | JTAG_CYCLE_TCK(); /* Idle */ \ 237 | PIN_TDI_OUT(1U); \ 238 | \ 239 | /* Capture Timestamp */ \ 240 | if (request & DAP_TRANSFER_TIMESTAMP) { \ 241 | DAP_Data.timestamp = TIMESTAMP_GET(); \ 242 | } \ 243 | \ 244 | /* Idle cycles */ \ 245 | n = DAP_Data.transfer.idle_cycles; \ 246 | while (n--) { \ 247 | JTAG_CYCLE_TCK(); /* Idle */ \ 248 | } \ 249 | \ 250 | return ((uint8_t)ack); \ 251 | } 252 | 253 | 254 | #undef PIN_DELAY 255 | #define PIN_DELAY() PIN_DELAY_FAST() 256 | JTAG_IR_Function(Fast) 257 | JTAG_TransferFunction(Fast) 258 | 259 | #undef PIN_DELAY 260 | #define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay) 261 | JTAG_IR_Function(Slow) 262 | JTAG_TransferFunction(Slow) 263 | 264 | 265 | // JTAG Read IDCODE register 266 | // return: value read 267 | uint32_t JTAG_ReadIDCode (void) { 268 | uint32_t bit; 269 | uint32_t val; 270 | uint32_t n; 271 | 272 | PIN_TMS_SET(); 273 | JTAG_CYCLE_TCK(); /* Select-DR-Scan */ 274 | PIN_TMS_CLR(); 275 | JTAG_CYCLE_TCK(); /* Capture-DR */ 276 | JTAG_CYCLE_TCK(); /* Shift-DR */ 277 | 278 | for (n = DAP_Data.jtag_dev.index; n; n--) { 279 | JTAG_CYCLE_TCK(); /* Bypass before data */ 280 | } 281 | 282 | val = 0U; 283 | for (n = 31U; n; n--) { 284 | JTAG_CYCLE_TDO(bit); /* Get D0..D30 */ 285 | val |= bit << 31; 286 | val >>= 1; 287 | } 288 | PIN_TMS_SET(); 289 | JTAG_CYCLE_TDO(bit); /* Get D31 & Exit1-DR */ 290 | val |= bit << 31; 291 | 292 | JTAG_CYCLE_TCK(); /* Update-DR */ 293 | PIN_TMS_CLR(); 294 | JTAG_CYCLE_TCK(); /* Idle */ 295 | 296 | return (val); 297 | } 298 | 299 | 300 | // JTAG Write ABORT register 301 | // data: value to write 302 | // return: none 303 | void JTAG_WriteAbort (uint32_t data) { 304 | uint32_t n; 305 | 306 | PIN_TMS_SET(); 307 | JTAG_CYCLE_TCK(); /* Select-DR-Scan */ 308 | PIN_TMS_CLR(); 309 | JTAG_CYCLE_TCK(); /* Capture-DR */ 310 | JTAG_CYCLE_TCK(); /* Shift-DR */ 311 | 312 | for (n = DAP_Data.jtag_dev.index; n; n--) { 313 | JTAG_CYCLE_TCK(); /* Bypass before data */ 314 | } 315 | 316 | PIN_TDI_OUT(0U); 317 | JTAG_CYCLE_TCK(); /* Set RnW=0 (Write) */ 318 | JTAG_CYCLE_TCK(); /* Set A2=0 */ 319 | JTAG_CYCLE_TCK(); /* Set A3=0 */ 320 | 321 | for (n = 31U; n; n--) { 322 | JTAG_CYCLE_TDI(data); /* Set D0..D30 */ 323 | data >>= 1; 324 | } 325 | n = DAP_Data.jtag_dev.count - DAP_Data.jtag_dev.index - 1U; 326 | if (n) { 327 | JTAG_CYCLE_TDI(data); /* Set D31 */ 328 | for (--n; n; n--) { 329 | JTAG_CYCLE_TCK(); /* Bypass after data */ 330 | } 331 | PIN_TMS_SET(); 332 | JTAG_CYCLE_TCK(); /* Bypass & Exit1-DR */ 333 | } else { 334 | PIN_TMS_SET(); 335 | JTAG_CYCLE_TDI(data); /* Set D31 & Exit1-DR */ 336 | } 337 | 338 | JTAG_CYCLE_TCK(); /* Update-DR */ 339 | PIN_TMS_CLR(); 340 | JTAG_CYCLE_TCK(); /* Idle */ 341 | PIN_TDI_OUT(1U); 342 | } 343 | 344 | 345 | // JTAG Set IR 346 | // ir: IR value 347 | // return: none 348 | void JTAG_IR (uint32_t ir) { 349 | if (DAP_Data.fast_clock) { 350 | JTAG_IR_Fast(ir); 351 | } else { 352 | JTAG_IR_Slow(ir); 353 | } 354 | } 355 | 356 | 357 | // JTAG Transfer I/O 358 | // request: A[3:2] RnW APnDP 359 | // data: DATA[31:0] 360 | // return: ACK[2:0] 361 | uint8_t JTAG_Transfer(uint32_t request, uint32_t *data) { 362 | if (DAP_Data.fast_clock) { 363 | return JTAG_TransferFast(request, data); 364 | } else { 365 | return JTAG_TransferSlow(request, data); 366 | } 367 | } 368 | 369 | 370 | #endif /* (DAP_JTAG != 0) */ 371 | -------------------------------------------------------------------------------- /components/DAP/Source/SW_DP.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2017 ARM Limited. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the License); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an AS IS BASIS, WITHOUT 14 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ---------------------------------------------------------------------- 19 | * 20 | * $Date: 1. December 2017 21 | * $Revision: V2.0.0 22 | * 23 | * Project: CMSIS-DAP Source 24 | * Title: SW_DP.c CMSIS-DAP SW DP I/O 25 | * 26 | *---------------------------------------------------------------------------*/ 27 | 28 | #include "DAP_config.h" 29 | #include "DAP.h" 30 | 31 | 32 | // SW Macros 33 | 34 | #define PIN_SWCLK_SET PIN_SWCLK_TCK_SET 35 | #define PIN_SWCLK_CLR PIN_SWCLK_TCK_CLR 36 | 37 | #define SW_CLOCK_CYCLE() \ 38 | PIN_SWCLK_CLR(); \ 39 | PIN_DELAY(); \ 40 | PIN_SWCLK_SET(); \ 41 | PIN_DELAY() 42 | 43 | #define SW_WRITE_BIT(bit) \ 44 | PIN_SWDIO_OUT(bit); \ 45 | PIN_SWCLK_CLR(); \ 46 | PIN_DELAY(); \ 47 | PIN_SWCLK_SET(); \ 48 | PIN_DELAY() 49 | 50 | #define SW_READ_BIT(bit) \ 51 | PIN_SWCLK_CLR(); \ 52 | PIN_DELAY(); \ 53 | bit = PIN_SWDIO_IN(); \ 54 | PIN_SWCLK_SET(); \ 55 | PIN_DELAY() 56 | 57 | #define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay) 58 | 59 | 60 | // Generate SWJ Sequence 61 | // count: sequence bit count 62 | // data: pointer to sequence bit data 63 | // return: none 64 | #if ((DAP_SWD != 0) || (DAP_JTAG != 0)) 65 | void SWJ_Sequence (uint32_t count, const uint8_t *data) { 66 | uint32_t val; 67 | uint32_t n; 68 | 69 | val = 0U; 70 | n = 0U; 71 | while (count--) { 72 | if (n == 0U) { 73 | val = *data++; 74 | n = 8U; 75 | } 76 | if (val & 1U) { 77 | PIN_SWDIO_TMS_SET(); 78 | } else { 79 | PIN_SWDIO_TMS_CLR(); 80 | } 81 | SW_CLOCK_CYCLE(); 82 | val >>= 1; 83 | n--; 84 | } 85 | } 86 | #endif 87 | 88 | 89 | // Generate SWD Sequence 90 | // info: sequence information 91 | // swdo: pointer to SWDIO generated data 92 | // swdi: pointer to SWDIO captured data 93 | // return: none 94 | #if (DAP_SWD != 0) 95 | void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi) { 96 | uint32_t val; 97 | uint32_t bit; 98 | uint32_t n, k; 99 | 100 | n = info & SWD_SEQUENCE_CLK; 101 | if (n == 0U) { 102 | n = 64U; 103 | } 104 | 105 | if (info & SWD_SEQUENCE_DIN) { 106 | while (n) { 107 | val = 0U; 108 | for (k = 8U; k && n; k--, n--) { 109 | SW_READ_BIT(bit); 110 | val >>= 1; 111 | val |= bit << 7; 112 | } 113 | val >>= k; 114 | *swdi++ = (uint8_t)val; 115 | } 116 | } else { 117 | while (n) { 118 | val = *swdo++; 119 | for (k = 8U; k && n; k--, n--) { 120 | SW_WRITE_BIT(val); 121 | val >>= 1; 122 | } 123 | } 124 | } 125 | } 126 | #endif 127 | 128 | 129 | #if (DAP_SWD != 0) 130 | 131 | 132 | // SWD Transfer I/O 133 | // request: A[3:2] RnW APnDP 134 | // data: DATA[31:0] 135 | // return: ACK[2:0] 136 | #define SWD_TransferFunction(speed) /**/ \ 137 | static uint8_t SWD_Transfer##speed (uint32_t request, uint32_t *data) { \ 138 | uint32_t ack; \ 139 | uint32_t bit; \ 140 | uint32_t val; \ 141 | uint32_t parity; \ 142 | \ 143 | uint32_t n; \ 144 | \ 145 | /* Packet Request */ \ 146 | parity = 0U; \ 147 | SW_WRITE_BIT(1U); /* Start Bit */ \ 148 | bit = request >> 0; \ 149 | SW_WRITE_BIT(bit); /* APnDP Bit */ \ 150 | parity += bit; \ 151 | bit = request >> 1; \ 152 | SW_WRITE_BIT(bit); /* RnW Bit */ \ 153 | parity += bit; \ 154 | bit = request >> 2; \ 155 | SW_WRITE_BIT(bit); /* A2 Bit */ \ 156 | parity += bit; \ 157 | bit = request >> 3; \ 158 | SW_WRITE_BIT(bit); /* A3 Bit */ \ 159 | parity += bit; \ 160 | SW_WRITE_BIT(parity); /* Parity Bit */ \ 161 | SW_WRITE_BIT(0U); /* Stop Bit */ \ 162 | SW_WRITE_BIT(1U); /* Park Bit */ \ 163 | \ 164 | /* Turnaround */ \ 165 | PIN_SWDIO_OUT_DISABLE(); \ 166 | for (n = DAP_Data.swd_conf.turnaround; n; n--) { \ 167 | SW_CLOCK_CYCLE(); \ 168 | } \ 169 | \ 170 | /* Acknowledge response */ \ 171 | SW_READ_BIT(bit); \ 172 | ack = bit << 0; \ 173 | SW_READ_BIT(bit); \ 174 | ack |= bit << 1; \ 175 | SW_READ_BIT(bit); \ 176 | ack |= bit << 2; \ 177 | \ 178 | if (ack == DAP_TRANSFER_OK) { /* OK response */ \ 179 | /* Data transfer */ \ 180 | if (request & DAP_TRANSFER_RnW) { \ 181 | /* Read data */ \ 182 | val = 0U; \ 183 | parity = 0U; \ 184 | for (n = 32U; n; n--) { \ 185 | SW_READ_BIT(bit); /* Read RDATA[0:31] */ \ 186 | parity += bit; \ 187 | val >>= 1; \ 188 | val |= bit << 31; \ 189 | } \ 190 | SW_READ_BIT(bit); /* Read Parity */ \ 191 | if ((parity ^ bit) & 1U) { \ 192 | ack = DAP_TRANSFER_ERROR; \ 193 | } \ 194 | if (data) { *data = val; } \ 195 | /* Turnaround */ \ 196 | for (n = DAP_Data.swd_conf.turnaround; n; n--) { \ 197 | SW_CLOCK_CYCLE(); \ 198 | } \ 199 | PIN_SWDIO_OUT_ENABLE(); \ 200 | } else { \ 201 | /* Turnaround */ \ 202 | for (n = DAP_Data.swd_conf.turnaround; n; n--) { \ 203 | SW_CLOCK_CYCLE(); \ 204 | } \ 205 | PIN_SWDIO_OUT_ENABLE(); \ 206 | /* Write data */ \ 207 | val = *data; \ 208 | parity = 0U; \ 209 | for (n = 32U; n; n--) { \ 210 | SW_WRITE_BIT(val); /* Write WDATA[0:31] */ \ 211 | parity += val; \ 212 | val >>= 1; \ 213 | } \ 214 | SW_WRITE_BIT(parity); /* Write Parity Bit */ \ 215 | } \ 216 | /* Capture Timestamp */ \ 217 | if (request & DAP_TRANSFER_TIMESTAMP) { \ 218 | DAP_Data.timestamp = TIMESTAMP_GET(); \ 219 | } \ 220 | /* Idle cycles */ \ 221 | n = DAP_Data.transfer.idle_cycles; \ 222 | if (n) { \ 223 | PIN_SWDIO_OUT(0U); \ 224 | for (; n; n--) { \ 225 | SW_CLOCK_CYCLE(); \ 226 | } \ 227 | } \ 228 | PIN_SWDIO_OUT(1U); \ 229 | return ((uint8_t)ack); \ 230 | } \ 231 | \ 232 | if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) { \ 233 | /* WAIT or FAULT response */ \ 234 | if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0U)) { \ 235 | for (n = 32U+1U; n; n--) { \ 236 | SW_CLOCK_CYCLE(); /* Dummy Read RDATA[0:31] + Parity */ \ 237 | } \ 238 | } \ 239 | /* Turnaround */ \ 240 | for (n = DAP_Data.swd_conf.turnaround; n; n--) { \ 241 | SW_CLOCK_CYCLE(); \ 242 | } \ 243 | PIN_SWDIO_OUT_ENABLE(); \ 244 | if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) { \ 245 | PIN_SWDIO_OUT(0U); \ 246 | for (n = 32U+1U; n; n--) { \ 247 | SW_CLOCK_CYCLE(); /* Dummy Write WDATA[0:31] + Parity */ \ 248 | } \ 249 | } \ 250 | PIN_SWDIO_OUT(1U); \ 251 | return ((uint8_t)ack); \ 252 | } \ 253 | \ 254 | /* Protocol error */ \ 255 | for (n = DAP_Data.swd_conf.turnaround + 32U + 1U; n; n--) { \ 256 | SW_CLOCK_CYCLE(); /* Back off data phase */ \ 257 | } \ 258 | PIN_SWDIO_OUT_ENABLE(); \ 259 | PIN_SWDIO_OUT(1U); \ 260 | return ((uint8_t)ack); \ 261 | } 262 | 263 | 264 | #undef PIN_DELAY 265 | #define PIN_DELAY() PIN_DELAY_FAST() 266 | SWD_TransferFunction(Fast) 267 | 268 | #undef PIN_DELAY 269 | #define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay) 270 | SWD_TransferFunction(Slow) 271 | 272 | 273 | // SWD Transfer I/O 274 | // request: A[3:2] RnW APnDP 275 | // data: DATA[31:0] 276 | // return: ACK[2:0] 277 | uint8_t SWD_Transfer(uint32_t request, uint32_t *data) { 278 | if (DAP_Data.fast_clock) { 279 | return SWD_TransferFast(request, data); 280 | } else { 281 | return SWD_TransferSlow(request, data); 282 | } 283 | } 284 | 285 | 286 | #endif /* (DAP_SWD != 0) */ 287 | -------------------------------------------------------------------------------- /components/platform/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(sources "esp_io.c") 2 | 3 | set(include_dirs ".") 4 | 5 | set(dependencies "driver") 6 | 7 | idf_component_register( 8 | SRCS 9 | ${sources} 10 | INCLUDE_DIRS 11 | ${include_dirs} 12 | REQUIRES 13 | ${dependencies} 14 | ) 15 | -------------------------------------------------------------------------------- /components/platform/compiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | 9 | #if defined ( __GNUC__ ) 10 | #include "xtensa_gcc.h" 11 | #else 12 | #error Unknown compiler. 13 | #endif 14 | -------------------------------------------------------------------------------- /components/platform/esp_io.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include "sdkconfig.h" 8 | #include "esp_log.h" 9 | #include "esp_io.h" 10 | 11 | #define GET_IDX(mask) (__builtin_ctz(mask)) 12 | 13 | #ifndef ARRAY_SIZE 14 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) 15 | #endif 16 | 17 | dedic_gpio_bundle_handle_t s_gpio_out_bundle; 18 | dedic_gpio_bundle_handle_t s_gpio_io_bundle; 19 | static int s_init_gpio = 0; 20 | gpio_dev_t *const s_gpio_dev = GPIO_LL_GET_HW(GPIO_PORT_0); 21 | uint32_t s_gpio_conf; 22 | 23 | static const char *TAG = "esp_io"; 24 | 25 | #if CONFIG_BRIDGE_DEBUG_IFACE_JTAG 26 | 27 | void esp_init_jtag_pins(void) 28 | { 29 | if (!s_init_gpio) { 30 | gpio_config_t io_conf = { 31 | .mode = GPIO_MODE_OUTPUT, 32 | .pin_bit_mask = BIT64(GPIO_TDI) | BIT64(GPIO_TCK) | BIT64(GPIO_TMS), 33 | .pull_down_en = GPIO_PULLDOWN_DISABLE, 34 | .pull_up_en = GPIO_PULLUP_DISABLE, 35 | .intr_type = GPIO_INTR_DISABLE, 36 | }; 37 | ESP_ERROR_CHECK(gpio_config(&io_conf)); 38 | 39 | io_conf.mode = GPIO_MODE_INPUT; 40 | io_conf.pin_bit_mask = BIT64(GPIO_TDO); 41 | ESP_ERROR_CHECK(gpio_config(&io_conf)); 42 | 43 | int bundle_out_gpios[] = { GPIO_TCK, GPIO_TDI, GPIO_TMS }; 44 | int bundle_in_gpios[] = { GPIO_TDO }; 45 | 46 | dedic_gpio_bundle_config_t out_bundle_config = { 47 | .gpio_array = bundle_out_gpios, 48 | .array_size = ARRAY_SIZE(bundle_out_gpios), 49 | .flags = { 50 | .out_en = 1, 51 | }, 52 | }; 53 | 54 | dedic_gpio_bundle_config_t in_bundle_config = { 55 | .gpio_array = bundle_in_gpios, 56 | .array_size = ARRAY_SIZE(bundle_in_gpios), 57 | .flags = { 58 | .in_en = 1, 59 | }, 60 | }; 61 | 62 | dedic_gpio_new_bundle(&out_bundle_config, &s_gpio_out_bundle); 63 | dedic_gpio_new_bundle(&in_bundle_config, &s_gpio_io_bundle); 64 | 65 | dedic_gpio_cpu_ll_write_mask(GPIO_TMS_MASK, GPIO_TMS_MASK); 66 | dedic_gpio_cpu_ll_write_mask(GPIO_TCK_MASK, 0); 67 | 68 | s_init_gpio = 1; 69 | 70 | ESP_LOGI(TAG, "JTAG GPIO init done"); 71 | } 72 | } 73 | 74 | #else 75 | 76 | void esp_init_swd_pins(void) 77 | { 78 | if (!s_init_gpio) { 79 | gpio_reset_pin(GPIO_SWDIO); 80 | gpio_reset_pin(GPIO_SWCLK); 81 | esp_gpio_mode_in_out_enable(GPIO_SWDIO); 82 | gpio_set_pull_mode(GPIO_SWDIO, GPIO_PULLUP_ONLY); 83 | esp_gpio_mode_out_enable(GPIO_SWCLK); 84 | 85 | int bundle_out_gpios[GET_IDX(GPIO_SWD_OUT_MAX_MASK)] = { 0 }; 86 | int bundle_io_gpios[GET_IDX(GPIO_SWDIO_MAX_MASK)] = { 0 }; 87 | 88 | esp_gpio_clear(LED_JTAG_ON); 89 | esp_gpio_mode_out_enable(LED_JTAG_ON); 90 | bundle_out_gpios[GET_IDX(GPIO_SWD_BLINK_MASK)] = LED_JTAG_ON; 91 | 92 | bundle_io_gpios[GET_IDX(GPIO_SWDIO_MASK)] = GPIO_SWDIO; 93 | dedic_gpio_bundle_config_t io_bundle_config = { 94 | .gpio_array = bundle_io_gpios, 95 | .array_size = ARRAY_SIZE(bundle_io_gpios), 96 | .flags = { 97 | .out_en = 1, 98 | .in_en = 1, 99 | }, 100 | }; 101 | 102 | bundle_out_gpios[GET_IDX(GPIO_SWCLK_MASK)] = GPIO_SWCLK; 103 | dedic_gpio_bundle_config_t out_bundle_config = { 104 | .gpio_array = bundle_out_gpios, 105 | .array_size = ARRAY_SIZE(bundle_out_gpios), 106 | .flags = { 107 | .out_en = 1, 108 | }, 109 | }; 110 | 111 | dedic_gpio_new_bundle(&out_bundle_config, &s_gpio_out_bundle); 112 | dedic_gpio_new_bundle(&io_bundle_config, &s_gpio_io_bundle); 113 | s_gpio_conf = REG_READ(GPIO_FUNC0_OUT_SEL_CFG_REG + (GPIO_SWDIO * 4)); 114 | 115 | s_init_gpio = 1; 116 | 117 | ESP_LOGI(TAG, "SWD GPIO init done"); 118 | } 119 | } 120 | 121 | void esp_reset_dap_pins(void) 122 | { 123 | gpio_reset_pin(GPIO_SWDIO); // GPIO_TMS 124 | gpio_reset_pin(GPIO_SWCLK); // GPIO_TCK 125 | gpio_reset_pin(GPIO_TDI); 126 | gpio_reset_pin(GPIO_TDO); 127 | 128 | dedic_gpio_del_bundle(s_gpio_io_bundle); 129 | dedic_gpio_del_bundle(s_gpio_out_bundle); 130 | 131 | s_init_gpio = 0; 132 | } 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /components/platform/esp_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include "hal/gpio_ll.h" 13 | 14 | #include "compiler.h" 15 | 16 | /* jtag */ 17 | #define GPIO_TDI CONFIG_BRIDGE_GPIO_TDI 18 | #define GPIO_TDO CONFIG_BRIDGE_GPIO_TDO 19 | #define GPIO_TCK CONFIG_BRIDGE_GPIO_TCK 20 | #define GPIO_TMS CONFIG_BRIDGE_GPIO_TMS 21 | 22 | /* swd */ 23 | #define GPIO_SWCLK CONFIG_BRIDGE_GPIO_TCK 24 | #define GPIO_SWDIO CONFIG_BRIDGE_GPIO_TMS 25 | 26 | /* serial */ 27 | #define GPIO_BOOT CONFIG_BRIDGE_GPIO_BOOT 28 | #define GPIO_RST CONFIG_BRIDGE_GPIO_RST 29 | #define GPIO_RXD CONFIG_BRIDGE_GPIO_RXD 30 | #define GPIO_TXD CONFIG_BRIDGE_GPIO_TXD 31 | 32 | /* leds */ 33 | #define LED_TX CONFIG_BRIDGE_GPIO_LED1 34 | #define LED_RX CONFIG_BRIDGE_GPIO_LED2 35 | #define LED_JTAG CONFIG_BRIDGE_GPIO_LED3 36 | 37 | #define LED_TX_ON CONFIG_BRIDGE_GPIO_LED1_ACTIVE 38 | #define LED_TX_OFF (!CONFIG_BRIDGE_GPIO_LED1_ACTIVE) 39 | 40 | #define LED_RX_ON CONFIG_BRIDGE_GPIO_LED2_ACTIVE 41 | #define LED_RX_OFF (!CONFIG_BRIDGE_GPIO_LED2_ACTIVE) 42 | 43 | #define LED_JTAG_ON CONFIG_BRIDGE_GPIO_LED3_ACTIVE 44 | #define LED_JTAG_OFF (!CONFIG_BRIDGE_GPIO_LED3_ACTIVE) 45 | 46 | /* mask values depends on the location in the gpio bundle array */ 47 | /* SWD io pin mask values */ 48 | #define GPIO_SWDIO_MASK 0x01 /* bundle_io_gpios[0] */ /* input/output */ 49 | #define GPIO_SWDIO_MAX_MASK 0x02 /* will be used as io array size */ 50 | 51 | /* SWD out pin mask values */ 52 | #define GPIO_SWCLK_MASK 0x01 /* bundle_out_gpios[0] */ 53 | #define GPIO_SWD_BLINK_MASK 0x02 /* bundle_out_gpios[1] */ 54 | #define GPIO_SWD_OUT_MAX_MASK 0x04 /* will be used as out array size */ 55 | #define GPIO_SWDIO_OUT_MASK 0x04 /* will not be in the out array, but it should follow the previous pin mask */ 56 | 57 | /* JTAG out pin mask values */ 58 | #define GPIO_TCK_MASK 0x01 59 | #define GPIO_TDI_MASK 0x02 60 | #define GPIO_TMS_MASK 0x04 61 | #define GPIO_TMS_TDI_MASK 0x06 62 | 63 | /* JTAG input pin mask values */ 64 | #define GPIO_TDO_MASK 0x01 65 | 66 | extern dedic_gpio_bundle_handle_t s_gpio_out_bundle; 67 | extern dedic_gpio_bundle_handle_t s_gpio_io_bundle; 68 | extern gpio_dev_t *const s_gpio_dev; 69 | extern uint32_t s_gpio_conf; 70 | 71 | __STATIC_FORCEINLINE void esp_gpio_mode_input_enable(int gpio_num) 72 | { 73 | gpio_ll_output_disable(s_gpio_dev, gpio_num); 74 | gpio_ll_input_enable(s_gpio_dev, gpio_num); 75 | } 76 | 77 | __STATIC_FORCEINLINE void esp_gpio_mode_out_enable(int gpio_num) 78 | { 79 | gpio_ll_input_disable(s_gpio_dev, gpio_num); 80 | gpio_ll_output_enable(s_gpio_dev, gpio_num); 81 | } 82 | 83 | __STATIC_FORCEINLINE void esp_gpio_mode_in_out_enable(int gpio_num) 84 | { 85 | gpio_ll_input_enable(s_gpio_dev, gpio_num); 86 | gpio_ll_output_enable(s_gpio_dev, gpio_num); 87 | } 88 | 89 | __STATIC_FORCEINLINE void esp_gpio_set(int gpio_num) 90 | { 91 | gpio_ll_set_level(s_gpio_dev, gpio_num, 1); 92 | } 93 | 94 | __STATIC_FORCEINLINE void esp_gpio_clear(int gpio_num) 95 | { 96 | gpio_ll_set_level(s_gpio_dev, gpio_num, 0); 97 | } 98 | 99 | __STATIC_FORCEINLINE void esp_gpio_swdio_out_enable(void) 100 | { 101 | REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (GPIO_SWDIO * 4), s_gpio_conf); 102 | gpio_ll_output_enable(s_gpio_dev, GPIO_SWDIO); 103 | } 104 | 105 | __STATIC_FORCEINLINE void esp_gpio_swdio_out_disable(void) 106 | { 107 | gpio_ll_output_disable(s_gpio_dev, GPIO_SWDIO); 108 | } 109 | 110 | __STATIC_FORCEINLINE int esp_gpio_swdio_read(void) 111 | { 112 | return dedic_gpio_cpu_ll_read_in(); 113 | } 114 | 115 | __STATIC_FORCEINLINE void esp_gpio_swclk_set(void) 116 | { 117 | dedic_gpio_cpu_ll_write_mask(GPIO_SWCLK_MASK, GPIO_SWCLK_MASK); 118 | } 119 | 120 | __STATIC_FORCEINLINE void esp_gpio_swclk_clr(void) 121 | { 122 | dedic_gpio_cpu_ll_write_mask(GPIO_SWCLK_MASK, 0); 123 | } 124 | 125 | __STATIC_FORCEINLINE void esp_gpio_swdio_set(void) 126 | { 127 | dedic_gpio_cpu_ll_write_mask(GPIO_SWDIO_OUT_MASK, GPIO_SWDIO_OUT_MASK); 128 | } 129 | 130 | __STATIC_FORCEINLINE void esp_gpio_swdio_clr(void) 131 | { 132 | dedic_gpio_cpu_ll_write_mask(GPIO_SWDIO_OUT_MASK, 0); 133 | } 134 | 135 | __STATIC_FORCEINLINE void esp_gpio_swdio_write(int val) 136 | { 137 | dedic_gpio_cpu_ll_write_mask(GPIO_SWDIO_OUT_MASK, (val & 0x01) ? GPIO_SWDIO_OUT_MASK : 0); 138 | } 139 | 140 | __STATIC_FORCEINLINE void esp_gpio_swd_blink(int on) 141 | { 142 | gpio_ll_set_level(s_gpio_dev, LED_JTAG, (on & 0x01) ? LED_JTAG_ON : LED_JTAG_OFF); 143 | } 144 | 145 | __STATIC_FORCEINLINE void esp_gpio_jtag_led_off(void) 146 | { 147 | gpio_ll_set_level(s_gpio_dev, LED_JTAG, LED_JTAG_OFF); 148 | } 149 | 150 | __STATIC_FORCEINLINE void esp_gpio_jtag_led_on(void) 151 | { 152 | gpio_ll_set_level(s_gpio_dev, LED_JTAG, LED_JTAG_ON); 153 | } 154 | 155 | __STATIC_FORCEINLINE int esp_gpio_tdo_read(void) 156 | { 157 | return dedic_gpio_cpu_ll_read_in(); 158 | } 159 | 160 | __STATIC_FORCEINLINE void esp_gpio_tdi_write(int val) 161 | { 162 | dedic_gpio_cpu_ll_write_mask(GPIO_TDI_MASK, (val & 0x01) ? GPIO_TDI_MASK : 0); 163 | } 164 | 165 | __STATIC_FORCEINLINE void esp_gpio_tck_set(void) 166 | { 167 | dedic_gpio_cpu_ll_write_mask(GPIO_TCK_MASK, GPIO_TCK_MASK); 168 | } 169 | 170 | __STATIC_FORCEINLINE void esp_gpio_tck_clr(void) 171 | { 172 | dedic_gpio_cpu_ll_write_mask(GPIO_TCK_MASK, 0); 173 | } 174 | 175 | __STATIC_FORCEINLINE void esp_gpio_write_tmstck(uint8_t tms_tdi_mask) 176 | { 177 | dedic_gpio_cpu_ll_write_mask(GPIO_TMS_TDI_MASK, tms_tdi_mask); 178 | } 179 | 180 | void esp_init_jtag_pins(void); 181 | void esp_init_swd_pins(void); 182 | void esp_reset_dap_pins(void); 183 | -------------------------------------------------------------------------------- /components/platform/xtensa_gcc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | #pragma once 7 | 8 | #ifndef __STATIC_FORCEINLINE 9 | #define __STATIC_FORCEINLINE static inline __attribute__((always_inline)) 10 | #endif 11 | 12 | #ifndef __STATIC_INLINE 13 | #define __STATIC_INLINE static inline __attribute__((always_inline)) 14 | #endif 15 | 16 | #ifndef __WEAK 17 | #define __WEAK __attribute__((weak)) 18 | #endif 19 | 20 | #define __NOP() __asm volatile ("nop") 21 | -------------------------------------------------------------------------------- /images/concept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp-usb-bridge/ad33b115b829bd65e20d8d247b4d2d184c40e800/images/concept.png -------------------------------------------------------------------------------- /images/schematics.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esp-usb-bridge/ad33b115b829bd65e20d8d247b4d2d184c40e800/images/schematics.pdf -------------------------------------------------------------------------------- /launchpad.toml: -------------------------------------------------------------------------------- 1 | esp_toml_version = 1.0 2 | 3 | firmware_images_url = "https://espressif.github.io/esp-usb-bridge/" 4 | supported_apps = ["esp-prog2"] 5 | 6 | [esp-prog2] 7 | chipsets = ["ESP32-S3"] 8 | image.esp32-s3 = "esp-prog2.bin" 9 | description = "Firmware for ESP-Prog2 board" 10 | -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" 2 | "esp_usb_jtag.c" 3 | "serial.c" 4 | "msc.c" 5 | "util.c" 6 | "eub_vendord.c" 7 | "eub_swd.c" 8 | REQUIRES "tinyusb" "driver" "esp_timer" "usb" "platform" "DAP" 9 | LDFRAGMENTS "noflash.lf" 10 | INCLUDE_DIRS "." "public_include") 11 | 12 | idf_component_get_property(tusb_lib espressif__tinyusb COMPONENT_LIB) 13 | target_include_directories(${tusb_lib} PRIVATE public_include) 14 | 15 | idf_build_get_property(project_ver PROJECT_VER) 16 | if(project_ver) 17 | message(STATUS "Project version: '${project_ver}'") 18 | # Once a git tag is created, PROJECT_VER will look like v1.2-4-g567890a, assuming 19 | # that we use tags in `vX.Y` format. Extract X and Y from the tag: 20 | string(REGEX MATCH "^v([0-9]+)\\.([0-9]+)[^.]*$" match ${project_ver}) 21 | if(match) 22 | set(project_ver_major ${CMAKE_MATCH_1}) 23 | set(project_ver_minor ${CMAKE_MATCH_2}) 24 | # extend project_ver_minor to two BCD digits 25 | string(LENGTH ${project_ver_minor} len) 26 | if(len EQUAL 1) 27 | set(project_ver_minor "0${project_ver_minor}") 28 | endif() 29 | set(bcd_device "0x${project_ver_major}${project_ver_minor}") 30 | else() 31 | message(STATUS "Failed to determine major and minor version from '${project_ver}'") 32 | endif() 33 | endif() 34 | 35 | # Otherwise, set the version to 0.01 36 | if(NOT bcd_device) 37 | set(bcd_device "0x0001") 38 | endif() 39 | message(STATUS "bcdDevice value: ${bcd_device}") 40 | 41 | target_compile_definitions(${COMPONENT_LIB} PRIVATE BCDDEVICE=${bcd_device}) 42 | -------------------------------------------------------------------------------- /main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Bridge Configuration" 2 | 3 | choice BRIDGE_DEBUG_INTERFACE 4 | prompt "Debug interface" 5 | default BRIDGE_DEBUG_IFACE_JTAG 6 | help 7 | Select which debug interface to use 8 | 9 | config BRIDGE_DEBUG_IFACE_JTAG 10 | bool "ESP-USB JTAG" 11 | config BRIDGE_DEBUG_IFACE_SWD 12 | bool "CMSIS-DAP SWD" 13 | endchoice 14 | 15 | config BRIDGE_USB_VID 16 | hex "USB vendor ID" 17 | default 0x303A 18 | 19 | config BRIDGE_USB_PID 20 | hex "USB product ID" 21 | default 0x1002 22 | 23 | config BRIDGE_MANUFACTURER 24 | string "USB bridge manufacturer" 25 | default "Espressif Systems Co. Ltd." 26 | 27 | config BRIDGE_PRODUCT_NAME 28 | string "USB product name" 29 | default "ESP USB Bridge" 30 | 31 | config BRIDGE_DEBUG_IFACE_NAME 32 | string 33 | default "JTAG" if BRIDGE_DEBUG_IFACE_JTAG 34 | default "CMSIS-DAP" 35 | 36 | config BRIDGE_GPIO_BOOT 37 | int "GPIO pin for the BOOT signal" 38 | default 4 39 | 40 | config BRIDGE_GPIO_RST 41 | int "GPIO pin for the RESET signal" 42 | default 7 43 | 44 | config BRIDGE_GPIO_RXD 45 | int "GPIO pin for the RxD signal" 46 | default 6 47 | 48 | config BRIDGE_GPIO_TXD 49 | int "GPIO pin for the TxD signal" 50 | default 5 51 | 52 | config BRIDGE_GPIO_TDI 53 | int "GPIO pin for the target TDI signal" 54 | default 3 55 | 56 | config BRIDGE_GPIO_TDO 57 | int "GPIO pin for the target TDO signal" 58 | default 9 59 | 60 | config BRIDGE_GPIO_TCK 61 | int "GPIO pin for the target TCK signal" 62 | default 10 63 | 64 | config BRIDGE_GPIO_TMS 65 | int "GPIO pin for the target TMS signal" 66 | default 8 67 | 68 | config BRIDGE_GPIO_LED1 69 | int "GPIO pin LED1" 70 | default 12 71 | 72 | config BRIDGE_GPIO_LED2 73 | int "GPIO pin LED2" 74 | default 13 75 | 76 | config BRIDGE_GPIO_LED3 77 | int "GPIO pin LED3" 78 | default 14 79 | 80 | config BRIDGE_GPIO_LED1_ACTIVE 81 | int "GPIO pin LED1 active value" 82 | range 0 1 83 | default 0 84 | 85 | config BRIDGE_GPIO_LED2_ACTIVE 86 | int "GPIO pin LED2 active value" 87 | range 0 1 88 | default 0 89 | 90 | config BRIDGE_GPIO_LED3_ACTIVE 91 | int "GPIO pin LED3 active value" 92 | range 0 1 93 | default 0 94 | 95 | config BRIDGE_MSC_VOLUME_LABEL 96 | string "Volume label shown in the MSC disc" 97 | default "ESPPROG_MSC" 98 | help 99 | Volume label shown in the MSC disc. Max length is 11 ASCII characters. 100 | 101 | endmenu 102 | -------------------------------------------------------------------------------- /main/esp_usb_jtag.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | #include "sdkconfig.h" 7 | 8 | #if CONFIG_BRIDGE_DEBUG_IFACE_JTAG 9 | 10 | #include "esp_log.h" 11 | #include "freertos/FreeRTOS.h" 12 | #include "freertos/task.h" 13 | #include "esp_chip_info.h" 14 | #include "tusb.h" 15 | 16 | #include "eub_vendord.h" 17 | #include "util.h" 18 | #include "esp_io.h" 19 | 20 | static const char *TAG = "esp_usb_jtag"; 21 | 22 | static esp_chip_model_t s_target_model; 23 | static TaskHandle_t s_jtag_task_handle = NULL; 24 | static uint8_t s_tdo_bytes[1024]; 25 | static uint16_t s_total_tdo_bits = 0; 26 | static uint16_t s_usb_sent_bits = 0; 27 | 28 | #define ROUND_UP_BITS(x) ((x + 7) & (~7)) 29 | 30 | /* esp usb serial protocol specific definitions */ 31 | #define JTAG_PROTO_MAX_BITS (EUB_VENDORD_EPSIZE * 8) 32 | #define JTAG_PROTO_CAPS_VER 1 /*Version field. */ 33 | typedef struct __attribute__((packed)) 34 | { 35 | uint8_t proto_ver; /*Protocol version. Expects JTAG_PROTO_CAPS_VER for now. */ 36 | uint8_t length; /*of this plus any following descriptors */ 37 | } jtag_proto_caps_hdr_t; 38 | 39 | #define JTAG_PROTO_CAPS_SPEED_APB_TYPE 1 40 | typedef struct __attribute__((packed)) 41 | { 42 | uint8_t type; 43 | uint8_t length; 44 | } jtag_gen_hdr_t; 45 | 46 | typedef struct __attribute__((packed)) 47 | { 48 | uint8_t type; /*Type, always JTAG_PROTO_CAPS_SPEED_APB_TYPE */ 49 | uint8_t length; /*Length of this */ 50 | uint16_t apb_speed_10khz; /*ABP bus speed, in 10KHz increments. Base speed is half 51 | * this. */ 52 | uint16_t div_min; /*minimum divisor (to base speed), inclusive */ 53 | uint16_t div_max; /*maximum divisor (to base speed), inclusive */ 54 | } jtag_proto_caps_speed_apb_t; 55 | 56 | typedef struct { 57 | jtag_proto_caps_hdr_t proto_hdr; 58 | jtag_proto_caps_speed_apb_t caps_apb; 59 | } jtag_proto_caps_t; 60 | 61 | #define VEND_JTAG_SETDIV 0 62 | #define VEND_JTAG_SETIO 1 63 | #define VEND_JTAG_GETTDO 2 64 | #define VEND_JTAG_SET_CHIPID 3 65 | 66 | // TCK frequency is around 4800KHZ and we do not support selective clock for now. 67 | #define TCK_FREQ(khz) ((khz * 2) / 10) 68 | static const jtag_proto_caps_t jtag_proto_caps = { 69 | {.proto_ver = JTAG_PROTO_CAPS_VER, .length = sizeof(jtag_proto_caps_hdr_t) + sizeof(jtag_proto_caps_speed_apb_t)}, 70 | {.type = JTAG_PROTO_CAPS_SPEED_APB_TYPE, .length = sizeof(jtag_proto_caps_speed_apb_t), .apb_speed_10khz = TCK_FREQ(4800), .div_min = 1, .div_max = 1} 71 | }; 72 | 73 | void eub_debug_probe_init(void) __attribute__((alias("esp_usb_jtag_init"))); 74 | int eub_debug_probe_get_proto_caps(void *dest) __attribute__((alias("esp_usb_jtag_get_proto_caps"))); 75 | void eub_debug_probe_task_suspend(void) __attribute__((alias("esp_usb_jtag_task_suspend"))); 76 | void eub_debug_probe_task_resume(void) __attribute__((alias("esp_usb_jtag_task_resume"))); 77 | bool eub_debug_probe_target_is_esp32(void) __attribute__((alias("esp_usb_jtag_target_is_esp32"))); 78 | 79 | static int esp_usb_jtag_get_proto_caps(void *dest) 80 | { 81 | memcpy(dest, (uint16_t *)&jtag_proto_caps, sizeof(jtag_proto_caps)); 82 | return sizeof(jtag_proto_caps); 83 | } 84 | 85 | static bool esp_usb_jtag_target_is_esp32(void) 86 | { 87 | return s_target_model == CHIP_ESP32; 88 | } 89 | 90 | static void esp_usb_jtag_task_suspend(void) 91 | { 92 | if (s_jtag_task_handle) { 93 | vTaskSuspend(s_jtag_task_handle); 94 | } 95 | } 96 | 97 | static void esp_usb_jtag_task_resume(void) 98 | { 99 | if (s_jtag_task_handle) { 100 | vTaskResume(s_jtag_task_handle); 101 | } 102 | } 103 | 104 | bool eub_debug_probe_control_handler(const uint8_t rhport, const uint8_t stage, tusb_control_request_t const *request) 105 | { 106 | // Function to handle the TUSB_REQ_TYPE_VENDOR control requests from the host, 107 | // called by the tusb_control_xfer_cb function in eub_vendord.c. 108 | switch (request->bRequest) { 109 | case VEND_JTAG_SETDIV: 110 | case VEND_JTAG_SETIO: 111 | // TODO: process the commands 112 | return tud_control_status(rhport, request); 113 | 114 | case VEND_JTAG_GETTDO: { 115 | uint8_t buf = gpio_get_level(GPIO_TDI); 116 | return tud_control_xfer(rhport, request, (void *)&buf, 1); 117 | } 118 | 119 | case VEND_JTAG_SET_CHIPID: 120 | s_target_model = request->wValue; 121 | return tud_control_status(rhport, request); 122 | 123 | default: 124 | return false; 125 | } 126 | } 127 | 128 | inline static void do_jtag_one(const uint8_t tdo_req, const uint8_t tms_tdi_mask) 129 | { 130 | esp_gpio_write_tmstck(tms_tdi_mask); 131 | esp_gpio_tck_set(); 132 | 133 | if (tdo_req) { 134 | s_tdo_bytes[s_total_tdo_bits / 8] |= (esp_gpio_tdo_read() << (s_total_tdo_bits % 8)); 135 | s_total_tdo_bits++; 136 | } 137 | 138 | esp_gpio_tck_clr(); 139 | } 140 | 141 | static void esp_usb_jtag_task(void *pvParameters) 142 | { 143 | enum e_cmds { 144 | CMD_CLK_0 = 0, CMD_CLK_1, CMD_CLK_2, CMD_CLK_3, 145 | CMD_CLK_4, CMD_CLK_5, CMD_CLK_6, CMD_CLK_7, 146 | CMD_SRST0, CMD_SRST1, CMD_FLUSH, CMD_RSV, 147 | CMD_REP0, CMD_REP1, CMD_REP2, CMD_REP3 148 | }; 149 | 150 | const struct { 151 | uint8_t tdo_req; 152 | uint8_t tms_tdi_mask; 153 | } pin_levels[] = { // { tdo_req, tms, tdi } 154 | { 0, 0 }, // { 0, 0, 0 }, CMD_CLK_0 155 | { 0, GPIO_TDI_MASK }, // { 0, 0, 1 }, CMD_CLK_1 156 | { 0, GPIO_TMS_MASK }, // { 0, 1, 0 }, CMD_CLK_2 157 | { 0, GPIO_TMS_TDI_MASK }, // { 0, 1, 1 }, CMD_CLK_3 158 | { 1, 0 }, // { 1, 0, 0 }, CMD_CLK_4 159 | { 1, GPIO_TDI_MASK }, // { 1, 0, 1 }, CMD_CLK_5 160 | { 1, GPIO_TMS_MASK }, // { 1, 1, 0 }, CMD_CLK_6 161 | { 1, GPIO_TMS_TDI_MASK }, // { 1, 1, 1 }, CMD_CLK_7 162 | { 0, GPIO_TMS_MASK }, // { 0, 1, 0 }, CMD_SRST0 163 | { 0, GPIO_TMS_MASK }, // { 0, 1, 0 }, CMD_SRST1 164 | }; 165 | 166 | s_jtag_task_handle = xTaskGetCurrentTaskHandle(); 167 | 168 | size_t cnt = 0; 169 | int prev_cmd = CMD_SRST0, rep_cnt = 0; 170 | 171 | while (1) { 172 | esp_gpio_jtag_led_off(); 173 | char *nibbles = eub_vendord_recv_acquire_item(&cnt); 174 | esp_gpio_jtag_led_on(); 175 | 176 | ESP_LOG_BUFFER_HEXDUMP(TAG, nibbles, cnt, ESP_LOG_DEBUG); 177 | 178 | for (size_t n = 0; n < cnt * 2 ; n++) { 179 | const int cmd = (n & 1) ? (nibbles[n / 2] & 0x0F) : (nibbles[n / 2] >> 4); 180 | int cmd_exec = cmd, cmd_rpt_cnt = 1; 181 | 182 | switch (cmd) { 183 | case CMD_REP0: 184 | case CMD_REP1: 185 | case CMD_REP2: 186 | case CMD_REP3: 187 | //(r1*2+r0)<<(2*n) 188 | cmd_rpt_cnt = (cmd - CMD_REP0) << (2 * rep_cnt++); 189 | cmd_exec = prev_cmd; 190 | break; 191 | case CMD_SRST0: // JTAG Tap reset command is not expected from host but still we are ready 192 | cmd_rpt_cnt = 8; // 8 TMS=1 is more than enough to return the TAP state to RESET 193 | break; 194 | case CMD_SRST1: // FIXME: system reset may cause an issue during openocd examination 195 | cmd_rpt_cnt = 8; // for now this is also used for the tap reset 196 | // gpio_set_level(GPIO_RST, 0); 197 | // ets_delay_us(100); 198 | // gpio_set_level(GPIO_RST, 1); 199 | break; 200 | default: 201 | rep_cnt = 0; 202 | break; 203 | } 204 | 205 | if (cmd_exec < CMD_FLUSH) { 206 | for (int i = 0; i < cmd_rpt_cnt; i++) { 207 | do_jtag_one(pin_levels[cmd_exec].tdo_req, pin_levels[cmd_exec].tms_tdi_mask); 208 | } 209 | } else if (cmd_exec == CMD_FLUSH) { 210 | s_total_tdo_bits = ROUND_UP_BITS(s_total_tdo_bits); 211 | if (s_usb_sent_bits < s_total_tdo_bits) { 212 | int waiting_to_send_bits = s_total_tdo_bits - s_usb_sent_bits; 213 | while (waiting_to_send_bits > 0) { 214 | int send_bits = waiting_to_send_bits > JTAG_PROTO_MAX_BITS ? JTAG_PROTO_MAX_BITS : waiting_to_send_bits; 215 | eub_vendord_send_item(s_tdo_bytes + (s_usb_sent_bits / 8), send_bits / 8); 216 | s_usb_sent_bits += send_bits; 217 | waiting_to_send_bits -= send_bits; 218 | } 219 | memset(s_tdo_bytes, 0x00, sizeof(s_tdo_bytes)); 220 | s_total_tdo_bits = s_usb_sent_bits = 0; 221 | } 222 | } 223 | 224 | /* As soon as either 64 bytes (512 bits) have been collected or a CMD_FLUSH command is executed, 225 | make the usb buffer available for the host to receive. 226 | */ 227 | int waiting_to_send_bits = s_total_tdo_bits - s_usb_sent_bits; 228 | if (waiting_to_send_bits >= JTAG_PROTO_MAX_BITS) { 229 | int send_bits = ROUND_UP_BITS(waiting_to_send_bits > JTAG_PROTO_MAX_BITS ? JTAG_PROTO_MAX_BITS : waiting_to_send_bits); 230 | int n_byte = send_bits / 8; 231 | eub_vendord_send_item(s_tdo_bytes + (s_usb_sent_bits / 8), n_byte); 232 | memset(s_tdo_bytes + (s_usb_sent_bits / 8), 0x00, n_byte); 233 | s_usb_sent_bits += send_bits; 234 | waiting_to_send_bits -= send_bits; 235 | if (waiting_to_send_bits <= 0) { 236 | s_total_tdo_bits = s_usb_sent_bits = 0; 237 | } 238 | } 239 | 240 | if (cmd < CMD_REP0 && cmd != CMD_FLUSH) { 241 | prev_cmd = cmd; 242 | } 243 | } 244 | 245 | eub_vendord_free_item(nibbles); 246 | } 247 | 248 | vTaskDelete(NULL); 249 | } 250 | 251 | static void esp_usb_jtag_init(void) 252 | { 253 | /* dedicated GPIO will be binded to the CPU who invokes this API */ 254 | /* we will create a jtag task pinned to this core */ 255 | esp_init_jtag_pins(); 256 | 257 | BaseType_t res = xTaskCreatePinnedToCore(esp_usb_jtag_task, 258 | "jtag_task", 259 | 4 * 1024, 260 | NULL, 261 | EUB_VENDORD_USB_TASK_PRI + 1, 262 | NULL, 263 | esp_cpu_get_core_id()); 264 | if (res != pdPASS) { 265 | ESP_LOGE(TAG, "Cannot create JTAG task!"); 266 | eub_abort(); 267 | } 268 | } 269 | 270 | #endif /* CONFIG_BRIDGE_DEBUG_IFACE_JTAG */ 271 | -------------------------------------------------------------------------------- /main/eub_debug_probe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "tusb.h" 10 | 11 | /* Be careful if you want to change the index. It must be bigger then the size of string_desc_arr 12 | For now 7 would be also ok. But lets reserve some fields for the future additions 13 | Also it must be match with the value in the openocd/esp_usb_bridge.cfg file 14 | Currently it is defined as < esp_usb_jtag_caps_descriptor 0x030A > 15 | */ 16 | #define EUB_DEBUG_PROBE_STR_DESC_INX 0x0A 17 | 18 | void eub_debug_probe_init(void); 19 | int eub_debug_probe_get_proto_caps(void *dest); 20 | void eub_debug_probe_task_suspend(void); 21 | void eub_debug_probe_task_resume(void); 22 | bool eub_debug_probe_target_is_esp32(void); 23 | bool eub_debug_probe_control_handler(const uint8_t rhport, const uint8_t stage, tusb_control_request_t const *request); 24 | -------------------------------------------------------------------------------- /main/eub_swd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include "sdkconfig.h" 8 | 9 | #if CONFIG_BRIDGE_DEBUG_IFACE_SWD 10 | 11 | #include "freertos/FreeRTOS.h" 12 | #include "freertos/task.h" 13 | #include "esp_log.h" 14 | 15 | #include "tusb.h" 16 | #include "eub_vendord.h" 17 | #include "util.h" 18 | #include "esp_io.h" 19 | #include "DAP_config.h" 20 | #include "DAP.h" 21 | 22 | static const char *TAG = "eub_swd"; 23 | 24 | static TaskHandle_t s_swd_task_handle = NULL; 25 | 26 | void eub_debug_probe_init(void) __attribute__((alias("eub_swd_init"))); 27 | int eub_debug_probe_get_proto_caps(void *dest) __attribute__((alias("eub_swd_get_proto_caps"))); 28 | void eub_debug_probe_task_suspend(void) __attribute__((alias("eub_swd_task_suspend"))); 29 | void eub_debug_probe_task_resume(void) __attribute__((alias("eub_swd_task_resume"))); 30 | bool eub_debug_probe_target_is_esp32(void) __attribute__((alias("eub_swd_target_is_esp32"))); 31 | 32 | static int eub_swd_get_proto_caps(void *dest) 33 | { 34 | return 0; 35 | } 36 | 37 | static bool eub_swd_target_is_esp32(void) 38 | { 39 | return false; 40 | } 41 | 42 | static void eub_swd_task_suspend(void) 43 | { 44 | if (s_swd_task_handle) { 45 | vTaskSuspend(s_swd_task_handle); 46 | } 47 | } 48 | 49 | static void eub_swd_task_resume(void) 50 | { 51 | if (s_swd_task_handle) { 52 | vTaskResume(s_swd_task_handle); 53 | } 54 | } 55 | 56 | bool eub_debug_probe_control_handler(const uint8_t rhport, const uint8_t stage, tusb_control_request_t const *request) 57 | { 58 | // Function to handle the TUSB_REQ_TYPE_VENDOR control requests from the host, 59 | // called by the tusb_control_xfer_cb function in eub_vendord.c. 60 | return false; 61 | } 62 | 63 | static void swd_task(void *pvParameters) 64 | { 65 | size_t total_bytes = 0; 66 | uint8_t response[DAP_PACKET_SIZE]; 67 | 68 | while (1) { 69 | uint8_t *request = eub_vendord_recv_acquire_item(&total_bytes); 70 | 71 | ESP_LOG_BUFFER_HEXDUMP("req", request, total_bytes, ESP_LOG_DEBUG); 72 | 73 | uint32_t resp_len = DAP_ProcessCommand(request, response) & 0xFFFF; //lower 16 bits are response len 74 | 75 | ESP_LOG_BUFFER_HEXDUMP("res", response, resp_len, ESP_LOG_DEBUG); 76 | 77 | eub_vendord_send_item(response, resp_len); 78 | 79 | eub_vendord_free_item(request); 80 | } 81 | 82 | vTaskDelete(NULL); 83 | } 84 | 85 | static void eub_swd_init(void) 86 | { 87 | DAP_Setup(); 88 | 89 | BaseType_t res = xTaskCreatePinnedToCore(swd_task, 90 | "swd_task", 91 | 4 * 1024, 92 | NULL, 93 | EUB_VENDORD_USB_TASK_PRI + 1, 94 | &s_swd_task_handle, 95 | esp_cpu_get_core_id()); 96 | if (res != pdPASS) { 97 | ESP_LOGE(TAG, "Cannot create SWD task!"); 98 | eub_abort(); 99 | } 100 | } 101 | 102 | #endif /* CONFIG_BRIDGE_DEBUG_IFACE_SWD */ 103 | -------------------------------------------------------------------------------- /main/eub_vendord.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | #include "sdkconfig.h" 7 | #include "esp_log.h" 8 | #include "freertos/FreeRTOS.h" 9 | #include "freertos/task.h" 10 | #include "freertos/ringbuf.h" 11 | #include "tusb.h" 12 | #include "device/usbd_pvt.h" 13 | #include "tusb_config.h" 14 | #include "esp_timer.h" 15 | 16 | #include "eub_vendord.h" 17 | #include "eub_debug_probe.h" 18 | #include "usb_defs.h" 19 | #include "util.h" 20 | 21 | static const char *TAG = "eub_vendor"; 22 | 23 | /* 4K ringbuffer size is more than enough */ 24 | #define USB_RCVBUF_SIZE 4096 25 | #define USB_SNDBUF_SIZE 4096 26 | #define USB_BUSY_TOUT_US 100000 /* 100ms */ 27 | 28 | typedef struct { 29 | uint8_t ep_in; 30 | uint8_t ep_out; 31 | RingbufHandle_t usb_rcvbuf; 32 | RingbufHandle_t usb_sndbuf; 33 | TaskHandle_t usb_tx_task_handle; 34 | uint8_t epout_buf[EUB_VENDORD_EPSIZE]; /* Endpoint Transfer buffer */ 35 | } eub_vendord_interface_t; 36 | 37 | static eub_vendord_interface_t s_eub_vendord_itf; 38 | static const uint8_t s_rhport = 0; 39 | 40 | static void eub_vendord_init(void) 41 | { 42 | ESP_LOGD(TAG, "%s", __func__); 43 | 44 | memset(&s_eub_vendord_itf, 0x00, sizeof(s_eub_vendord_itf)); 45 | } 46 | 47 | static void eub_vendord_reset(uint8_t rhport) 48 | { 49 | ESP_LOGD(TAG, "%s", __func__); 50 | 51 | s_eub_vendord_itf.ep_in = 0; 52 | s_eub_vendord_itf.ep_out = 0; 53 | /* do not reset the FreeRTOS handlers */ 54 | } 55 | 56 | static uint16_t eub_vendord_open(uint8_t rhport, tusb_desc_interface_t const *desc_intf, uint16_t max_len) 57 | { 58 | ESP_LOGD(TAG, "%s", __func__); 59 | 60 | TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_intf->bInterfaceClass && 61 | EUB_VENDORD_IFACE_SUBCLASS == desc_intf->bInterfaceSubClass && 62 | EUB_VENDORD_IFACE_PROTOCOL == desc_intf->bInterfaceProtocol && 63 | EUB_VENDORD_IFACE_STR_IDX == desc_intf->iInterface, 0); 64 | 65 | uint8_t const *p_desc = tu_desc_next(desc_intf); 66 | eub_vendord_interface_t *p_vendor = &s_eub_vendord_itf; 67 | 68 | // Open endpoint pair with usbd helper 69 | usbd_open_edpt_pair(rhport, p_desc, desc_intf->bNumEndpoints, TUSB_XFER_BULK, &p_vendor->ep_out, &p_vendor->ep_in); 70 | 71 | ESP_LOGI(TAG, "EP_OUT:0x%x EP_IN:0x%x", p_vendor->ep_out, p_vendor->ep_in); 72 | 73 | p_desc += desc_intf->bNumEndpoints * sizeof(tusb_desc_endpoint_t); 74 | 75 | // Prepare for incoming data 76 | if (p_vendor->ep_out) { 77 | usbd_edpt_xfer(rhport, p_vendor->ep_out, p_vendor->epout_buf, sizeof(p_vendor->epout_buf)); 78 | } 79 | 80 | return (uint16_t)((uintptr_t)p_desc - (uintptr_t)desc_intf); 81 | } 82 | 83 | static const uint8_t desc_ms_os_20[] = { 84 | // Microsoft OS 2.0 Descriptor 85 | // Values are taken from https://github.com/hathach/tinyusb/tree/master/examples/device/webusb_serial 86 | // 87 | // Set header: length, type, windows version, total length 88 | // 0x000A = size of the header 89 | // MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00 (descriptor type) 90 | // 0x06030000 = Windows version 6.3 (Windows 8.1 and later) 91 | // MS_OS_20_DESC_LEN = total length of all descriptors 92 | U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN), 93 | 94 | // Configuration subset header 95 | // 0x0008 = size of the header 96 | // MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01 (descriptor type) 97 | // 0 = configuration index (first configuration) 98 | // 0 = reserved 99 | // MS_OS_20_DESC_LEN - 0x0A = remaining length (total - header length) 100 | U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A), 101 | 102 | // Function Subset header 103 | // 0x0008 = size of the header 104 | // MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02 (descriptor type) 105 | // ITF_NUM_VENDOR = interface number for vendor class 106 | // 0 = reserved 107 | // MS_OS_20_DESC_LEN - 0x0A - 0x08 = remaining length (total - header - config subset) 108 | U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), ITF_NUM_VENDOR, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08), 109 | 110 | // MS OS 2.0 Compatible ID descriptor 111 | // 0x0014 = size of the descriptor 112 | // MS_OS_20_FEATURE_COMPATBLE_ID = 0x03 (descriptor type) 113 | // 'WINUSB' = compatible ID string 114 | // 8 bytes of zeros = sub-compatible ID (not used) 115 | U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, 116 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible 117 | 118 | // MS OS 2.0 Registry property descriptor 119 | // length = remaining bytes (total - all previous sections) 120 | // MS_OS_20_FEATURE_REG_PROPERTY = 0x04 (descriptor type) 121 | // 0x0007 = REG_MULTI_SZ (property data type) 122 | // 0x002A = 42 bytes for property name length ("DeviceInterfaceGUIDs" in UTF-16) 123 | U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08 - 0x08 - 0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), 124 | U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16 125 | 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, 126 | 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00, 127 | // 0x0050 = 80 bytes for property data length (GUID string in UTF-16) 128 | U16_TO_U8S_LE(0x0050), // wPropertyDataLength 129 | // Property data: GUID "{80869ad8-c37f-476a-a6b4-ae241c30a473}" in UTF-16 format 130 | // Each character is followed by 0x00 for UTF-16 encoding 131 | '{', 0x00, '8', 0x00, '0', 0x00, '8', 0x00, '6', 0x00, '9', 0x00, 'a', 0x00, 'd', 0x00, '8', 0x00, '-', 0x00, 132 | 'c', 0x00, '3', 0x00, '7', 0x00, 'f', 0x00, '-', 0x00, '4', 0x00, '7', 0x00, '6', 0x00, 'a', 0x00, '-', 0x00, 133 | 'a', 0x00, '6', 0x00, 'b', 0x00, '4', 0x00, '-', 0x00, 'a', 0x00, 'e', 0x00, '2', 0x00, '4', 0x00, '1', 0x00, 134 | 'c', 0x00, '3', 0x00, '0', 0x00, 'a', 0x00, '4', 0x00, '7', 0x00, '3', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 135 | }; 136 | 137 | TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size"); 138 | 139 | bool tud_vendor_control_xfer_cb(const uint8_t rhport, const uint8_t stage, tusb_control_request_t const *request) 140 | { 141 | // nothing to with DATA & ACK stage 142 | if (stage != CONTROL_STAGE_SETUP) { 143 | return true; 144 | } 145 | 146 | switch (request->bmRequestType_bit.type) { 147 | case TUSB_REQ_TYPE_VENDOR: 148 | ESP_LOGI(TAG, "bRequest: (%d) wValue: (%d) wIndex: (%d)", 149 | request->bRequest, request->wValue, request->wIndex); 150 | 151 | switch (request->bRequest) { 152 | 153 | case VENDOR_REQUEST_MICROSOFT: 154 | return tud_control_xfer(rhport, request, (void *)(uintptr_t)desc_ms_os_20, MS_OS_20_DESC_LEN); 155 | default: 156 | return eub_debug_probe_control_handler(rhport, stage, request); 157 | } 158 | 159 | default: 160 | return false; 161 | } 162 | } 163 | 164 | static bool eub_endpt_transfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buf, uint16_t total_bytes) 165 | { 166 | uint64_t end = esp_timer_get_time() + USB_BUSY_TOUT_US; 167 | 168 | while (usbd_edpt_busy(rhport, ep_addr)) { 169 | if (esp_timer_get_time() > end) { 170 | ESP_LOGE(TAG, "usbd_edpt_busy timeout!"); 171 | return false; 172 | } 173 | } 174 | 175 | return usbd_edpt_xfer(s_rhport, ep_addr, buf, total_bytes); 176 | } 177 | 178 | static bool eub_vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) 179 | { 180 | TU_VERIFY(xferred_bytes > 0 && xferred_bytes <= 64, false); 181 | TU_VERIFY(ep_addr == s_eub_vendord_itf.ep_out || ep_addr == s_eub_vendord_itf.ep_in, false); 182 | 183 | const uint8_t ep_dir = tu_edpt_dir(ep_addr); 184 | 185 | ESP_LOGD(TAG, "%s xfer from ep:%x recvd:%" PRId32 " bytes", __func__, ep_addr, xferred_bytes); 186 | 187 | if (ep_dir == TUSB_DIR_IN) { 188 | /* nothing to do for now */ 189 | return true; 190 | } else if (ep_dir == TUSB_DIR_OUT) { 191 | BaseType_t res = xRingbufferSend(s_eub_vendord_itf.usb_rcvbuf, 192 | s_eub_vendord_itf.epout_buf, xferred_bytes, pdMS_TO_TICKS(1000)); 193 | if (res != pdTRUE) { 194 | ESP_LOGE(TAG, "Cannot write to usb_rcvbuf ringbuffer (free %d of %d)!", 195 | xRingbufferGetCurFreeSize(s_eub_vendord_itf.usb_rcvbuf), USB_RCVBUF_SIZE); 196 | eub_abort(); 197 | } 198 | 199 | // Prepare for next incoming data 200 | if (!eub_endpt_transfer(rhport, ep_addr, s_eub_vendord_itf.epout_buf, EUB_VENDORD_EPSIZE)) { 201 | ESP_LOGE(TAG, "USB transfer error on EP:%x", ep_addr); 202 | eub_abort(); 203 | } 204 | 205 | return true; 206 | } 207 | 208 | return false; 209 | } 210 | 211 | static void usb_send_task(void *pvParameters) 212 | { 213 | size_t total_bytes; 214 | 215 | ESP_LOGI(TAG, "usb_send_task is ready!"); 216 | 217 | for (;;) { 218 | uint8_t *buf = xRingbufferReceive(s_eub_vendord_itf.usb_sndbuf, &total_bytes, portMAX_DELAY); 219 | 220 | ESP_LOG_BUFFER_HEXDUMP("res", buf, total_bytes, ESP_LOG_DEBUG); 221 | 222 | if (!eub_endpt_transfer(s_rhport, s_eub_vendord_itf.ep_in, buf, total_bytes)) { 223 | ESP_LOGE(TAG, "USB transfer error on EP:%x", s_eub_vendord_itf.ep_in); 224 | eub_abort(); 225 | } 226 | 227 | vRingbufferReturnItem(s_eub_vendord_itf.usb_sndbuf, buf); 228 | } 229 | 230 | vTaskDelete(NULL); 231 | } 232 | 233 | int eub_vendord_send_item(const void *buf, const size_t size) 234 | { 235 | if (xRingbufferSend(s_eub_vendord_itf.usb_sndbuf, buf, size, pdMS_TO_TICKS(1000)) != pdTRUE) { 236 | ESP_LOGE(TAG, "Cannot write to usb_sndbuf ringbuffer (free %d of %d)!", 237 | xRingbufferGetCurFreeSize(s_eub_vendord_itf.usb_sndbuf), USB_SNDBUF_SIZE); 238 | return 0; 239 | } 240 | return size; 241 | } 242 | 243 | void *eub_vendord_recv_acquire_item(size_t *item_size) 244 | { 245 | return xRingbufferReceive(s_eub_vendord_itf.usb_rcvbuf, item_size, portMAX_DELAY); 246 | } 247 | 248 | void eub_vendord_free_item(void *item) 249 | { 250 | vRingbufferReturnItem(s_eub_vendord_itf.usb_rcvbuf, item); 251 | } 252 | 253 | void eub_vendord_start(void) 254 | { 255 | ESP_LOGD(TAG, "%s", __func__); 256 | 257 | static bool init = false; 258 | 259 | if (!init) { 260 | 261 | /* We would like to process OpenOCD packages one by one. RINGBUF_TYPE_NOSPLIT allows us to do it. */ 262 | s_eub_vendord_itf.usb_rcvbuf = xRingbufferCreate(USB_RCVBUF_SIZE, RINGBUF_TYPE_NOSPLIT); 263 | if (!s_eub_vendord_itf.usb_rcvbuf) { 264 | ESP_LOGE(TAG, "Cannot allocate USB receive buffer!"); 265 | eub_abort(); 266 | } 267 | 268 | s_eub_vendord_itf.usb_sndbuf = xRingbufferCreate(USB_SNDBUF_SIZE, RINGBUF_TYPE_NOSPLIT); 269 | if (!s_eub_vendord_itf.usb_sndbuf) { 270 | ESP_LOGE(TAG, "Cannot allocate USB send buffer!"); 271 | eub_abort(); 272 | } 273 | 274 | if (xTaskCreate(usb_send_task, 275 | "usb_send_task", 276 | 4 * 1024, 277 | NULL, 278 | EUB_VENDORD_USB_TASK_PRI, 279 | &s_eub_vendord_itf.usb_tx_task_handle) != pdPASS) { 280 | ESP_LOGE(TAG, "Cannot create USB send task!"); 281 | eub_abort(); 282 | } 283 | 284 | init = true; 285 | } 286 | } 287 | 288 | const usbd_class_driver_t s_eub_vendord_driver = { 289 | #if CFG_TUSB_DEBUG >= 2 290 | .name = "EUB-VENDOR", 291 | #endif 292 | .init = eub_vendord_init, 293 | .reset = eub_vendord_reset, 294 | .open = eub_vendord_open, 295 | .control_xfer_cb = tud_vendor_control_xfer_cb, 296 | .xfer_cb = eub_vendord_xfer_cb, 297 | .sof = NULL, 298 | }; 299 | 300 | const usbd_class_driver_t *usbd_app_driver_get_cb(uint8_t *driver_count) 301 | { 302 | ESP_LOGD(TAG, "%s", __func__); 303 | 304 | *driver_count = 1; 305 | return &s_eub_vendord_driver; 306 | } 307 | -------------------------------------------------------------------------------- /main/eub_vendord.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | 9 | #define EUB_VENDORD_EPSIZE 64 /* full speed */ 10 | #define EUB_VENDORD_IFACE_SUBCLASS 0xFF 11 | #define EUB_VENDORD_IFACE_PROTOCOL 0x01 12 | #define EUB_VENDORD_IFACE_STR_IDX 5 13 | #define EUB_VENDORD_USB_TASK_PRI 4 14 | 15 | #define VENDOR_REQUEST_MICROSOFT 0x20 // Can be any value between 0x20 and 0xFF 16 | #define MS_OS_20_DESC_LEN 0xB2 17 | 18 | void eub_vendord_start(void); 19 | int eub_vendord_send_item(const void *buf, const size_t size); 20 | void *eub_vendord_recv_acquire_item(size_t *item_size); 21 | void eub_vendord_free_item(void *item); 22 | -------------------------------------------------------------------------------- /main/idf_component.yml: -------------------------------------------------------------------------------- 1 | ## IDF Component Manager Manifest File 2 | dependencies: 3 | idf: ">=5.0" 4 | espressif/esp-serial-flasher: "^1.8" 5 | espressif/tinyusb: "^0.18.0~2" 6 | -------------------------------------------------------------------------------- /main/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include "util.h" 9 | #include "esp_log.h" 10 | #include "freertos/FreeRTOS.h" 11 | #include "freertos/task.h" 12 | #include "serial.h" 13 | #include "tusb.h" 14 | #include "msc.h" 15 | #include "hal/usb_phy_types.h" 16 | #include "soc/usb_periph.h" 17 | #include "rom/gpio.h" 18 | #include "driver/gpio.h" 19 | #include "sdkconfig.h" 20 | #include "esp_idf_version.h" 21 | #include "esp_system.h" 22 | #include "esp_mac.h" 23 | #include "esp_private/periph_ctrl.h" 24 | #include "esp_private/usb_phy.h" 25 | #include "eub_vendord.h" 26 | #include "eub_debug_probe.h" 27 | #include "usb_defs.h" 28 | 29 | static const char *TAG = "bridge_main"; 30 | 31 | #define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_VENDOR_DESC_LEN + TUD_MSC_DESC_LEN) 32 | 33 | static const tusb_desc_device_t descriptor_config = { 34 | .bLength = sizeof(descriptor_config), 35 | .bDescriptorType = TUSB_DESC_DEVICE, 36 | .bcdUSB = 0x0210, // at least 2.1 or 3.x for BOS 37 | .bDeviceClass = TUSB_CLASS_MISC, 38 | .bDeviceSubClass = MISC_SUBCLASS_COMMON, 39 | .bDeviceProtocol = MISC_PROTOCOL_IAD, 40 | #ifdef CFG_TUD_ENDPOINT0_SIZE 41 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 42 | #else // earlier versions have a typo in the name 43 | .bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE, 44 | #endif 45 | .idVendor = CONFIG_BRIDGE_USB_VID, 46 | .idProduct = CONFIG_BRIDGE_USB_PID, 47 | .bcdDevice = BCDDEVICE, // defined in CMakeLists.txt 48 | .iManufacturer = 0x01, 49 | .iProduct = 0x02, 50 | .iSerialNumber = 0x03, 51 | .bNumConfigurations = 0x01 52 | }; 53 | 54 | /* 55 | ESP usb builtin jtag subclass and protocol is 0xFF and 0x01 respectively. 56 | However, Tinyusb default values are 0x00. 57 | In order to use same protocol without tinyusb customization we are re-defining 58 | vendor descriptor here. 59 | */ 60 | // Interface number, string index, EP Out & IN address, EP size 61 | #define TUD_VENDOR_EUB_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ 62 | /* Interface */\ 63 | 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_VENDOR_SPECIFIC, EUB_VENDORD_IFACE_SUBCLASS, EUB_VENDORD_IFACE_PROTOCOL, _stridx,\ 64 | /* Endpoint Out */\ 65 | 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ 66 | /* Endpoint In */\ 67 | 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 68 | 69 | static uint8_t const desc_configuration[] = { 70 | // config number, interface count, string index, total length, attribute, power in mA 71 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, 0, 100), 72 | 73 | // Interface number, string index, EP notification address and size, EP data address (out, in) and size. 74 | TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, 0x81, 8, EPNUM_CDC, 0x80 | EPNUM_CDC, CFG_TUD_CDC_EP_BUFSIZE), 75 | 76 | // Interface number, string index, EP Out & IN address, EP size 77 | TUD_VENDOR_EUB_DESCRIPTOR(ITF_NUM_VENDOR, EUB_VENDORD_IFACE_STR_IDX, EPNUM_VENDOR, 0x80 | EPNUM_VENDOR, 64), 78 | 79 | // Interface number, string index, EP Out & EP In address, EP size 80 | TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 6, EPNUM_MSC, 0x80 | EPNUM_MSC, 64), 81 | }; 82 | 83 | #define MAC_BYTES 6 84 | 85 | static char serial_descriptor[MAC_BYTES * 2 + 1] = {'\0'}; // 2 chars per hexnumber + '\0' 86 | 87 | static char const *string_desc_arr[] = { 88 | (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) 89 | CONFIG_BRIDGE_MANUFACTURER, // 1: Manufacturer 90 | #if CONFIG_BRIDGE_DEBUG_IFACE_JTAG 91 | CONFIG_BRIDGE_PRODUCT_NAME, // 2: Product 92 | #else 93 | "CMSIS-DAP", // OpenOCD expects "CMSIS-DAP" as a product name 94 | #endif 95 | serial_descriptor, // 3: Serials 96 | "CDC", 97 | CONFIG_BRIDGE_DEBUG_IFACE_NAME, // JTAG or CMSIS-DAP 98 | "MSC", 99 | 100 | /* JTAG_STR_DESC_INX 0x0A */ 101 | }; 102 | 103 | #define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN) 104 | 105 | // BOS Descriptor with Microsoft OS 2.0 support 106 | static uint8_t const desc_bos[] = { 107 | // total length, number of device caps 108 | TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1), 109 | 110 | // Microsoft OS 2.0 descriptor 111 | TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT) 112 | }; 113 | 114 | uint8_t const *tud_descriptor_bos_cb(void) 115 | { 116 | return desc_bos; 117 | } 118 | 119 | uint8_t const *tud_descriptor_configuration_cb(uint8_t index) 120 | { 121 | return desc_configuration; 122 | } 123 | 124 | uint8_t const *tud_descriptor_device_cb(void) 125 | { 126 | return (uint8_t const *) &descriptor_config; 127 | } 128 | 129 | void tud_mount_cb(void) 130 | { 131 | ESP_LOGI(TAG, "Mounted"); 132 | 133 | eub_vendord_start(); 134 | eub_debug_probe_init(); 135 | } 136 | 137 | static void init_serial_no(void) 138 | { 139 | uint8_t m[MAC_BYTES] = {0}; 140 | esp_err_t ret = esp_efuse_mac_get_default(m); 141 | 142 | if (ret != ESP_OK) { 143 | ESP_LOGD(TAG, "Cannot read MAC address and set the device serial number"); 144 | eub_abort(); 145 | } 146 | 147 | snprintf(serial_descriptor, sizeof(serial_descriptor), 148 | "%02X%02X%02X%02X%02X%02X", m[0], m[1], m[2], m[3], m[4], m[5]); 149 | } 150 | 151 | uint16_t const *tud_descriptor_string_cb(const uint8_t index, const uint16_t langid) 152 | { 153 | static uint16_t _desc_str[32]; // Static, because it must exist long enough for transfer to complete 154 | uint8_t chr_count; 155 | 156 | if (index == 0) { 157 | memcpy(&_desc_str[1], string_desc_arr[0], 2); 158 | chr_count = 1; 159 | } else if (index == EUB_DEBUG_PROBE_STR_DESC_INX) { 160 | chr_count = eub_debug_probe_get_proto_caps(&_desc_str[1]) / 2; 161 | } else { 162 | // Convert ASCII string into UTF-16 163 | 164 | if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) { 165 | return NULL; 166 | } 167 | 168 | const char *str = string_desc_arr[index]; 169 | 170 | // Cap at max char 171 | chr_count = strlen(str); 172 | if (chr_count > 31) { 173 | chr_count = 31; 174 | } 175 | 176 | for (uint8_t i = 0; i < chr_count; i++) { 177 | _desc_str[1 + i] = str[i]; 178 | } 179 | } 180 | 181 | // first byte is length (including header), second byte is string type 182 | _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); 183 | 184 | return _desc_str; 185 | } 186 | 187 | static void tusb_device_task(void *pvParameters) 188 | { 189 | while (1) { 190 | tud_task(); 191 | } 192 | vTaskDelete(NULL); 193 | } 194 | 195 | static void init_led_gpios(void) 196 | { 197 | gpio_config_t io_conf = {}; 198 | io_conf.intr_type = GPIO_PIN_INTR_DISABLE; 199 | io_conf.mode = GPIO_MODE_OUTPUT; 200 | io_conf.pin_bit_mask = (1ULL << CONFIG_BRIDGE_GPIO_LED1) | (1ULL << CONFIG_BRIDGE_GPIO_LED2) | 201 | (1ULL << CONFIG_BRIDGE_GPIO_LED3); 202 | io_conf.pull_down_en = 0; 203 | io_conf.pull_up_en = 0; 204 | ESP_ERROR_CHECK(gpio_config(&io_conf)); 205 | 206 | gpio_set_level(CONFIG_BRIDGE_GPIO_LED1, !CONFIG_BRIDGE_GPIO_LED1_ACTIVE); 207 | gpio_set_level(CONFIG_BRIDGE_GPIO_LED2, !CONFIG_BRIDGE_GPIO_LED2_ACTIVE); 208 | gpio_set_level(CONFIG_BRIDGE_GPIO_LED3, !CONFIG_BRIDGE_GPIO_LED3_ACTIVE); 209 | 210 | ESP_LOGI(TAG, "LED GPIO init done"); 211 | } 212 | 213 | static void int_usb_phy(void) 214 | { 215 | usb_phy_config_t phy_config = { 216 | .controller = USB_PHY_CTRL_OTG, 217 | .target = USB_PHY_TARGET_INT, 218 | .otg_mode = USB_OTG_MODE_DEVICE, 219 | .otg_speed = USB_PHY_SPEED_FULL, 220 | .ext_io_conf = NULL, 221 | .otg_io_conf = NULL, 222 | }; 223 | usb_phy_handle_t phy_handle; 224 | usb_new_phy(&phy_config, &phy_handle); 225 | } 226 | 227 | void app_main(void) 228 | { 229 | init_led_gpios(); // Keep this at the beginning. LEDs are used for error reporting. 230 | 231 | init_serial_no(); 232 | 233 | int_usb_phy(); 234 | 235 | tusb_init(); 236 | msc_init(); 237 | serial_init(); 238 | 239 | xTaskCreate(tusb_device_task, "tusb_device_task", 4 * 1024, NULL, 5, NULL); 240 | } 241 | -------------------------------------------------------------------------------- /main/msc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | // The mass storage class creates a mountable USB device into which UF2 formatted files can be dropped to flash the 8 | // target ESP device. Some necessary initialization is done by running the msc_task(). The task is deleted after the 9 | // initialization. The module contains the following callbacks from the tinyusb USB stack. 10 | // - tud_msc_inquiry_cb - returns string identifiers about the device. 11 | // - tud_msc_test_unit_ready_cb - return the availability of the device. It is available in the beginning and while it 12 | // is mounted. It becomes unavailable after ejecting the device. 13 | // - tud_msc_capacity_cb - returns the device capacity. 14 | // - tud_msc_start_stop_cb - handles disc ejection. 15 | // - tud_msc_scsi_cb - desired actions to SCSI disc commands can be handler there. 16 | // - tud_msc_read10_cb - invoked in order to read from the disc. A skeleton structure of FAT16 file system is 17 | // pre-defined by variables msc_disk_boot_sector, msc_disk_fat_table_sector0, msc_disk_readme_sector0 and 18 | // msc_disk_root_directory_sector0. A disc read outside of these variables returns all zeroes. 19 | // - tud_msc_write10_cb - invoked in order to write the disc. The above mentioned file system structure is not modified. 20 | // Each write is interpreted as a block for flashing. UF2 block format is used where the flashing address is encoded 21 | // among other information. The flashing is done by the esp-serial-flasher IDF component. 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "esp_log.h" 29 | #include "tusb.h" 30 | #include "msc.h" 31 | #include "util.h" 32 | #include "esp_loader.h" 33 | #include "esp_timer.h" 34 | #include "serial.h" 35 | #include "sdkconfig.h" 36 | #include "driver/gpio.h" 37 | #include "esp_io.h" 38 | 39 | #define KB(x) ((x) * 1024) 40 | #define FAT_CLUSTERS KB(6) 41 | #define FAT_SECTORS_PER_CLUSTER 8 42 | #define FAT_SECTORS (FAT_CLUSTERS * FAT_SECTORS_PER_CLUSTER) 43 | #define FAT_SECTOR_SIZE 512 44 | #define FAT_ROOT_SECTORS 2 45 | #define FAT_ROOT_ENTRY_SIZE 32 46 | #define FAT_VOLUME_NAME_SIZE 11 47 | 48 | // Windows will generate a volume information file on the first mount. And it uses long file names, therefore, will 49 | // use three entries per file. So only three new files could be added if using one 512B partition and 16 root 50 | // entries. 51 | 52 | #define FAT_ROOT_ENTRIES (FAT_ROOT_SECTORS * FAT_SECTOR_SIZE / FAT_ROOT_ENTRY_SIZE) 53 | #define FAT16_CLUSTER_BYTES 2 54 | #define FAT_TABLE_SECTORS (FAT_CLUSTERS * FAT16_CLUSTER_BYTES / FAT_SECTOR_SIZE) 55 | #define FAT_BOOT_SECTORS 1 56 | 57 | typedef struct __attribute__((__packed__)) 58 | { 59 | uint8_t jump_instructions[3]; 60 | uint8_t oem_name[8]; 61 | uint16_t bytes_per_sector; 62 | // BIOS parameter block (25 bytes) 63 | uint8_t sectors_per_cluster; 64 | uint16_t reserved_sectors; 65 | uint8_t fat_table_copies; 66 | uint16_t root_entries; 67 | uint16_t no_small_sectors; 68 | uint8_t media_type; 69 | uint16_t fat_table_sectors; 70 | uint16_t sectors_per_track; 71 | uint16_t heads; 72 | uint32_t hidden_sectors; 73 | uint32_t large_sectors; 74 | // Extended BIOS parameter block (26 bytes) 75 | uint8_t physical_disco_no; 76 | uint8_t current_head; 77 | uint8_t signature; 78 | uint32_t serial_no; 79 | uint8_t volume[FAT_VOLUME_NAME_SIZE]; 80 | uint8_t system_id[8]; 81 | 82 | uint8_t bootstrap_code[448]; 83 | 84 | uint16_t end_marker; 85 | 86 | } msc_boot_sector_t; 87 | 88 | static const char *TAG = "bridge_msc"; 89 | static bool ejected = false; 90 | 91 | _Static_assert(FAT_SECTORS == (FAT_SECTORS & 0xFFFF), "Large sectors should be used instead of small ones"); 92 | _Static_assert(FAT_SECTOR_SIZE == (FAT_SECTOR_SIZE & 0xFFFF), "FAT Sector Size must fit into a 16-bit field"); 93 | _Static_assert(FAT_SECTORS_PER_CLUSTER == (FAT_SECTORS_PER_CLUSTER & 0xFF), "FAT Sectors per Cluster must fit into a 8-bit field"); 94 | _Static_assert(FAT_ROOT_ENTRIES == (FAT_ROOT_ENTRIES & 0xFFFF), "FAT ROOT entries must fit into a 16-bit field"); 95 | _Static_assert(FAT_TABLE_SECTORS == (FAT_TABLE_SECTORS & 0xFFFF), "FAT table sectors must fit into a 16-bit field"); 96 | _Static_assert(FAT_BOOT_SECTORS == (FAT_BOOT_SECTORS & 0xFFFF), "FAT boot sectors must fit into a 16-bit field"); 97 | _Static_assert(sizeof(msc_boot_sector_t) == FAT_SECTOR_SIZE, "The boot sector has incorrect size!"); 98 | _Static_assert(strlen(CONFIG_BRIDGE_MSC_VOLUME_LABEL) <= FAT_VOLUME_NAME_SIZE, "BRIDGE_MSC_VOLUME_LABEL is too long"); 99 | 100 | static msc_boot_sector_t msc_disk_boot_sector = { 101 | .jump_instructions = {0xEB, 0x3C, 0x90}, 102 | .oem_name = {'m', 'k', 'f', 's', '.', 'f', 'a', 't'}, 103 | .bytes_per_sector = FAT_SECTOR_SIZE, 104 | .sectors_per_cluster = FAT_SECTORS_PER_CLUSTER, 105 | .reserved_sectors = FAT_BOOT_SECTORS, 106 | .fat_table_copies = 1, 107 | .root_entries = FAT_ROOT_ENTRIES, 108 | .no_small_sectors = FAT_SECTORS, // Small sectors if the number fits here 109 | .media_type = 0xF8, // hard disk 110 | .fat_table_sectors = FAT_TABLE_SECTORS, 111 | .sectors_per_track = 0, 112 | .heads = 0, 113 | .hidden_sectors = 0, 114 | .large_sectors = 0, 115 | .physical_disco_no = 0x80, // Hard drives are numbered from 0x80. 116 | .current_head = 0, // not used by FAT 117 | .signature = 0x29, // Must be 0x28 or 0x29 for Windows. 118 | .serial_no = 0x563d0c93, // Random number created upon formatting. 119 | .volume = {' '}, 120 | .system_id = {'F', 'A', 'T', '1', '6', ' ', ' ', ' '}, 121 | 122 | // The bootstrap code was generated with mkfs.fat and it prints "This is not a bootable disk. Please insert a 123 | // bootable floppy and press any key to try again". 124 | .bootstrap_code = { 125 | 0x0e, 0x1f, 0xbe, 0x5b, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10, 0x5e, 126 | 0xeb, 0xf0, 0x32, 0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 127 | 0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 128 | 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 129 | 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 130 | 0x0a, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74, 131 | 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0, 0, 0, 0, 0, 0, 0, 0, 132 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140 | 0, 0, 0, 0, 0, 0, 0 141 | }, 142 | 143 | .end_marker = 0xAA55, 144 | }; 145 | 146 | static const uint8_t msc_disk_fat_table_sector0[] = { 147 | 0xF8, 0xFF, 148 | 0xFF, 0xFF, 149 | 0xFF, 0xFF, // Cluster no. 2 - Readme file start and end 150 | }; 151 | 152 | static const uint8_t msc_disk_readme_sector0[] = 153 | "Use 'idf.py uf2' to generate an UF2 binary. Drop the generated file into this disk in order to flash the device. \ 154 | \r\n"; 155 | 156 | #define MSC_README_SIZE (sizeof(msc_disk_readme_sector0) - 1) 157 | _Static_assert(MSC_README_SIZE < FAT_SECTOR_SIZE, "Only the first sector of the README is stored in RAM"); 158 | 159 | static uint8_t msc_disk_root_directory_sector0[] = { 160 | ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 161 | 0x08, // attribute byte where volume bit is set 162 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // time and date for creation & modification 163 | 0, 0, // starting cluster in the FAT table 164 | 0, 0, 0, 0, // size 165 | // readme file 166 | 'R', 'E', 'A', 'D', 'M', 'E', ' ', ' ', 'T', 'X', 'T', 167 | 0x01, // attribute byte where read-only bit is set 168 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // time and date for creation & modification 169 | 0x02, 0, // starting cluster in the FAT table 170 | GET_BYTE(MSC_README_SIZE, 0), GET_BYTE(MSC_README_SIZE, 1), GET_BYTE(MSC_README_SIZE, 2), GET_BYTE(MSC_README_SIZE, 3), // size 171 | }; 172 | 173 | void tud_msc_inquiry_cb(const uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) 174 | { 175 | (void) lun; 176 | 177 | const char vid[8] = "ESP"; 178 | const char pid[16] = "Flash Storage"; 179 | const char rev[4] = "0.1"; 180 | 181 | ESP_LOGD(TAG, "tud_msc_inquiry_cb() invoked"); 182 | 183 | memcpy(vendor_id, vid, strlen(vid)); 184 | memcpy(product_id, pid, strlen(pid)); 185 | memcpy(product_rev, rev, strlen(rev)); 186 | } 187 | 188 | bool tud_msc_test_unit_ready_cb(const uint8_t lun) 189 | { 190 | ESP_LOGD(TAG, "tud_msc_test_unit_ready_cb() invoked"); 191 | 192 | if (ejected) { 193 | tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); 194 | return false; 195 | } 196 | 197 | return true; 198 | } 199 | 200 | void tud_msc_capacity_cb(const uint8_t lun, uint32_t *block_count, uint16_t *block_size) 201 | { 202 | (void) lun; 203 | 204 | ESP_LOGD(TAG, "tud_msc_capacity_cb() invoked"); 205 | *block_count = FAT_SECTORS; 206 | *block_size = FAT_SECTOR_SIZE; 207 | } 208 | 209 | bool tud_msc_start_stop_cb(const uint8_t lun, const uint8_t power_condition, const bool start, const bool load_eject) 210 | { 211 | (void) lun; 212 | 213 | ESP_LOGI(TAG, "tud_msc_start_stop_cb() invoked, power_condition=%d, start=%d, load_eject=%d", power_condition, 214 | start, load_eject); 215 | 216 | if (load_eject) { 217 | if (start) { 218 | // load disk storage 219 | } else { 220 | // unload disk storage 221 | ejected = true; 222 | } 223 | } 224 | 225 | return true; 226 | } 227 | 228 | #define FIRST_FAT_SECTOR FAT_BOOT_SECTORS 229 | #define FIRST_ROOT_SECTOR (FIRST_FAT_SECTOR + FAT_TABLE_SECTORS) 230 | #define FIRST_README_SECTOR (FIRST_ROOT_SECTOR + FAT_ROOT_SECTORS) 231 | #define FIRST_ELSE_SECTOR (FIRST_README_SECTOR + FAT_SECTORS_PER_CLUSTER) 232 | #define IS_LBA_BOOT(lba) ((lba) < FIRST_FAT_SECTOR) 233 | #define IS_LBA_FAT(lba) ((lba) >= FIRST_FAT_SECTOR && (lba) < FIRST_ROOT_SECTOR) 234 | #define IS_LBA_ROOT(lba) ((lba) >= FIRST_ROOT_SECTOR && (lba) < FIRST_README_SECTOR) 235 | #define IS_LBA_README(lba) ((lba) >= FIRST_README_SECTOR && (lba) < FIRST_ELSE_SECTOR) 236 | #define IS_LBA_ELSE(lba) ((lba) >= FIRST_ELSE_SECTOR) 237 | 238 | int32_t tud_msc_read10_cb(const uint8_t lun, const uint32_t lba, const uint32_t offset, void *buffer, const uint32_t bufsize) 239 | { 240 | ESP_LOGD(TAG, "tud_msc_read10_cb() invoked, lun=%d, lba=%" PRId32 ", offset=%" PRId32 ", bufsize=%" PRId32, lun, lba, offset, bufsize); 241 | 242 | const uint8_t *addr = NULL; 243 | size_t size = FAT_SECTOR_SIZE; 244 | 245 | if (IS_LBA_BOOT(lba)) { 246 | addr = (const uint8_t *) &msc_disk_boot_sector; 247 | } else if (lba == FIRST_FAT_SECTOR) { 248 | addr = msc_disk_fat_table_sector0; 249 | size = sizeof(msc_disk_fat_table_sector0); 250 | } else if (lba == FIRST_ROOT_SECTOR) { 251 | addr = msc_disk_root_directory_sector0; 252 | size = sizeof(msc_disk_root_directory_sector0); 253 | } else if (lba == FIRST_README_SECTOR) { 254 | addr = msc_disk_readme_sector0; 255 | size = sizeof(msc_disk_readme_sector0); 256 | } // else lba sector is not kept in RAM 257 | 258 | int done = 0; 259 | int left_to_do = bufsize; 260 | 261 | if (addr) { 262 | const int available = size - offset; 263 | if (available > 0) { 264 | const int len = MIN(available, left_to_do); 265 | memcpy(buffer, addr + offset, len); 266 | done = len; 267 | left_to_do -= len; 268 | } 269 | } 270 | 271 | if (left_to_do > 0) { 272 | memset((uint8_t *)buffer + done, 0, left_to_do); 273 | } 274 | 275 | return bufsize; 276 | } 277 | 278 | #define UF2_BLOCK_SIZE 512 279 | #define UF2_DATA_SIZE 476 280 | #define UF2_MD5_SIZE 24 281 | #define UF2_FIRST_MAGIC 0x0A324655 282 | #define UF2_SECOND_MAGIC 0x9E5D5157 283 | #define UF2_FINAL_MAGIC 0x0AB16F30 284 | #define UF2_FLAG_FAMILYID_PRESENT 0x00002000 285 | #define UF2_FLAG_MD5_PRESENT 0x00004000 286 | 287 | typedef struct { 288 | uint32_t magic0; 289 | uint32_t magic1; 290 | uint32_t flags; 291 | uint32_t addr; 292 | uint32_t payload_size; 293 | uint32_t block_no; 294 | uint32_t blocks; 295 | uint32_t chip_id; 296 | uint8_t data[UF2_DATA_SIZE]; 297 | uint32_t magic3; 298 | } uf2_block_t; 299 | 300 | #define UF2_ESP8266_ID 0x7eab61ed 301 | 302 | static const char *chipid_to_name(const uint32_t id) 303 | { 304 | // IDs can be found at https://github.com/Microsoft/uf2 305 | switch (id) { 306 | case UF2_ESP8266_ID: 307 | return "ESP8266"; 308 | case 0x1c5f21b0: 309 | return "ESP32"; 310 | case 0xbfdd4eee: 311 | return "ESP32-S2"; 312 | case 0xd42ba06c: 313 | return "ESP32-C3"; 314 | case 0xc47e5767: 315 | return "ESP32-S3"; 316 | case 0x2b88d29c: 317 | return "ESP32-C2"; 318 | case 0x332726f6: 319 | return "ESP32-H2"; 320 | case 0x540ddf62: 321 | return "ESP32-C6"; 322 | case 0x3d308e94: 323 | return "ESP32-P4"; 324 | case 0xf71c0343: 325 | return "ESP32-C5"; 326 | case 0x77d850c4: 327 | return "ESP32-C61"; 328 | case 0xb6dd00af: 329 | return "ESP32-H21"; 330 | case 0x9e0baa8a: 331 | return "ESP32-H4"; 332 | default: 333 | return "unknown"; 334 | } 335 | } 336 | 337 | #define BUFFER_SIZE KB(16) 338 | #define FLASH_BLOCK_SIZE KB(4) 339 | 340 | #define MSC_FLASH_HIGH_BAUDRATE 230400 341 | #define MSC_FLASH_DEFAULT_BAUDRATE 115200 342 | 343 | static int msc_last_block_written = -1; 344 | static esp_timer_handle_t reset_timer; 345 | 346 | static bool msc_change_baudrate(const uint32_t chip_id, const uint32_t baud) 347 | { 348 | if (chip_id == UF2_ESP8266_ID) { 349 | return true; 350 | } 351 | return (esp_loader_change_baudrate(baud) == ESP_LOADER_SUCCESS) && serial_set_baudrate(baud); 352 | } 353 | 354 | static esp_loader_error_t flash_data(uint32_t addr, uint8_t *data, uint32_t datalen) 355 | { 356 | const uint32_t block_size = KB(4); // Seems like max for ROM flash 357 | if (esp_loader_flash_start(addr, datalen, block_size) != ESP_LOADER_SUCCESS) { 358 | ESP_LOGE(TAG, "Erasing flash failed at addr %" PRId32 " of length %" PRId32, addr, datalen); 359 | return ESP_LOADER_ERROR_FAIL; 360 | } 361 | 362 | while (datalen > 0) { 363 | uint32_t bytes_to_write = MIN(datalen, block_size); 364 | ESP_LOGD(TAG, "Writing flash at addr %" PRId32 " of length %" PRId32, addr, bytes_to_write); 365 | if (esp_loader_flash_write(data, bytes_to_write) != ESP_LOADER_SUCCESS) { 366 | ESP_LOGE(TAG, "Writing flash failed at addr %" PRId32 " of length %" PRId32, addr, datalen); 367 | return ESP_LOADER_ERROR_FAIL; 368 | } 369 | datalen -= bytes_to_write; 370 | data += bytes_to_write; 371 | addr += bytes_to_write; 372 | } 373 | return ESP_LOADER_SUCCESS; 374 | } 375 | 376 | static bool handle_data(uint32_t addr, uint8_t *data, uint32_t datalen, bool flush_buffer) 377 | { 378 | static uint8_t buffer[BUFFER_SIZE]; 379 | static uint32_t last_written_pos = 0; 380 | static uint32_t aligned_addr = 0; 381 | 382 | if (flush_buffer) { 383 | // If we have data in buffer, pad it to block boundary and write it 384 | if (last_written_pos > 0) { 385 | uint32_t end_addr = aligned_addr + last_written_pos; 386 | uint32_t pad_size = (FLASH_BLOCK_SIZE - (end_addr % FLASH_BLOCK_SIZE)) % FLASH_BLOCK_SIZE; 387 | 388 | // Read existing data for padding if needed 389 | if (pad_size > 0) { 390 | if (esp_loader_flash_read(buffer + last_written_pos, end_addr, pad_size) != ESP_LOADER_SUCCESS) { 391 | return false; 392 | } 393 | last_written_pos += pad_size; 394 | } 395 | 396 | // Write the complete block 397 | if (flash_data(aligned_addr, buffer, last_written_pos) != ESP_LOADER_SUCCESS) { 398 | return false; 399 | } 400 | last_written_pos = 0; 401 | } 402 | return true; 403 | } 404 | 405 | // If buffer is empty, check if the address is aligned to the block size, 406 | // if not, pad the buffer with the existing data from the flash. 407 | if (last_written_pos == 0) { 408 | aligned_addr = addr & ~(FLASH_BLOCK_SIZE - 1); 409 | if (addr != aligned_addr) { 410 | uint32_t bytes_to_read = addr - aligned_addr; 411 | if (esp_loader_flash_read(buffer, aligned_addr, bytes_to_read) != ESP_LOADER_SUCCESS) { 412 | return false; 413 | } 414 | last_written_pos = bytes_to_read; 415 | } 416 | } 417 | 418 | // Handle buffer overflow by writing current buffer and continuing with remaining data 419 | uint32_t remaining_space = BUFFER_SIZE - last_written_pos; 420 | if (datalen > remaining_space) { 421 | uint32_t bytes_to_copy = remaining_space; 422 | memcpy(buffer + last_written_pos, data, bytes_to_copy); 423 | 424 | if (flash_data(aligned_addr, buffer, BUFFER_SIZE) != ESP_LOADER_SUCCESS) { 425 | return false; 426 | } 427 | 428 | // Update the remaining data to be written 429 | datalen -= bytes_to_copy; 430 | data += bytes_to_copy; 431 | addr += bytes_to_copy; 432 | last_written_pos = 0; 433 | 434 | // Recursively handle remaining data 435 | return handle_data(addr, data, datalen, false); 436 | } 437 | 438 | // Add new data to buffer 439 | memcpy(buffer + last_written_pos, data, datalen); 440 | last_written_pos += datalen; 441 | 442 | return true; 443 | } 444 | 445 | static bool init_flash(uint32_t chip_id) 446 | { 447 | serial_set(false); 448 | 449 | if (!serial_set_baudrate(MSC_FLASH_DEFAULT_BAUDRATE)) { 450 | ESP_LOGW(TAG, "BRIDGE UART failed to change baudrate to %d", MSC_FLASH_DEFAULT_BAUDRATE); 451 | return false; 452 | } 453 | 454 | esp_loader_connect_args_t connect_config = ESP_LOADER_CONNECT_DEFAULT(); 455 | if (esp_loader_connect(&connect_config) != ESP_LOADER_SUCCESS) { 456 | ESP_LOGE(TAG, "ESP LOADER connection failed!"); 457 | return false; 458 | } 459 | ESP_LOGD(TAG, "ESP LOADER connection success!"); 460 | 461 | if (!msc_change_baudrate(chip_id, MSC_FLASH_HIGH_BAUDRATE)) { 462 | ESP_LOGE(TAG, "Failed to change baudrate to %d", MSC_FLASH_HIGH_BAUDRATE); 463 | return false; 464 | } 465 | ESP_LOGD(TAG, "Baudrate changed to %d", MSC_FLASH_HIGH_BAUDRATE); 466 | 467 | return true; 468 | } 469 | 470 | static bool finish_flash(void) 471 | { 472 | // Flush any remaining data in the buffer 473 | if (!handle_data(0, NULL, 0, true)) { 474 | ESP_LOGE(TAG, "Failed to flush buffer"); 475 | return false; 476 | } 477 | 478 | if (!serial_set_baudrate(MSC_FLASH_DEFAULT_BAUDRATE)) { 479 | ESP_LOGW(TAG, "ESP LOADER cannot change baudrate to %d", MSC_FLASH_DEFAULT_BAUDRATE); 480 | } 481 | 482 | esp_loader_flash_finish(false); 483 | serial_set(true); 484 | gpio_set_level(GPIO_RST, false); 485 | ESP_ERROR_CHECK(esp_timer_start_once(reset_timer, SERIAL_FLASHER_RESET_HOLD_TIME_MS * 1000)); 486 | return true; 487 | } 488 | 489 | int32_t tud_msc_write10_cb(const uint8_t lun, const uint32_t lba, const uint32_t offset, uint8_t *buffer, const uint32_t bufsize) 490 | { 491 | ESP_LOGD(TAG, "tud_msc_write10_cb() invoked, lun=%d, lba=%" PRId32 ", offset=%" PRId32, lun, lba, offset); 492 | ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, bufsize, ESP_LOG_DEBUG); 493 | 494 | assert(bufsize == UF2_BLOCK_SIZE); 495 | 496 | if (esp_timer_is_active(reset_timer)) { 497 | ESP_LOGD(TAG, "Waiting for the target to reset"); 498 | return 0; 499 | } 500 | 501 | // Linux and Windows write files differently. Windows also creates system volume information files on the first 502 | // mount. In an ideal case, FAT and ROOT content would be analyzed and the flash file detected. 503 | // However, the only reliable way to detect files for flashing is look at the content. 504 | if (IS_LBA_ELSE(lba)) { 505 | uf2_block_t *p = (uf2_block_t *) buffer; 506 | 507 | if (p->magic0 == UF2_FIRST_MAGIC && p->magic1 == UF2_SECOND_MAGIC && p->magic3 == UF2_FINAL_MAGIC) { 508 | // UF2 block detected 509 | const char *chip_name = (p->flags & UF2_FLAG_FAMILYID_PRESENT) ? chipid_to_name(p->chip_id) : "???"; 510 | 511 | ESP_LOGD(TAG, "LBA %" PRId32 ": UF2 block %" PRId32 " of %" PRId32 " for chip %s at %#08" PRIx32 " with length %" PRId32, lba, p->block_no, p->blocks, 512 | chip_name, p->addr, p->payload_size); 513 | 514 | // Not connected to chip, initialize 515 | if (msc_last_block_written == -1) { 516 | if (!init_flash(p->chip_id)) { 517 | ESP_LOGE(TAG, "Failed to initialize flash"); 518 | eub_abort(); 519 | } 520 | } 521 | // The block number is not sequential, flush the buffer 522 | else if (p->block_no - 1 != msc_last_block_written) { 523 | if (!handle_data(0, NULL, 0, true)) { 524 | ESP_LOGE(TAG, "Failed to flush buffer"); 525 | eub_abort(); 526 | } 527 | } 528 | 529 | const uint32_t real_payload_size = (p->flags & UF2_FLAG_MD5_PRESENT) ? (UF2_DATA_SIZE - UF2_MD5_SIZE) : UF2_DATA_SIZE; 530 | if (!handle_data(p->addr, p->data, real_payload_size, false)) { 531 | ESP_LOGE(TAG, "Failed to handle data"); 532 | eub_abort(); 533 | } 534 | msc_last_block_written = p->block_no; 535 | } 536 | } 537 | return bufsize; 538 | } 539 | 540 | // Callback invoked when WRITE10 command is completed (status received and accepted by host). 541 | void tud_msc_write10_complete_cb(uint8_t lun) 542 | { 543 | if (msc_last_block_written != -1) { 544 | if (!finish_flash()) { 545 | ESP_LOGE(TAG, "Failed to finish flash"); 546 | eub_abort(); 547 | } 548 | msc_last_block_written = -1; 549 | } 550 | ESP_LOGD(TAG, "tud_msc_write10_complete_cb() invoked, lun=%" PRIu8, lun); 551 | } 552 | 553 | int32_t tud_msc_scsi_cb(const uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, const uint16_t bufsize) 554 | { 555 | (void) buffer; 556 | int32_t ret; 557 | 558 | ESP_LOGD(TAG, "tud_msc_scsi_cb() invoked. bufsize=%d", bufsize); 559 | ESP_LOG_BUFFER_HEXDUMP("scsi_cmd", scsi_cmd, 16, ESP_LOG_DEBUG); 560 | 561 | switch (scsi_cmd[0]) { 562 | case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: 563 | ESP_LOGD(TAG, "tud_msc_scsi_cb() invoked: SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL"); 564 | ret = 0; 565 | break; 566 | 567 | default: 568 | ESP_LOGW(TAG, "tud_msc_scsi_cb() invoked: %d", scsi_cmd[0]); 569 | 570 | tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); 571 | 572 | ret = -1; 573 | break; 574 | } 575 | 576 | return ret; 577 | } 578 | 579 | static void reset_timer_cb(void *arg) 580 | { 581 | (void) arg; 582 | gpio_set_level(GPIO_RST, true); 583 | serial_set(true); 584 | } 585 | 586 | static void init_reset_timer(void) 587 | { 588 | const esp_timer_create_args_t timer_args = { 589 | .callback = reset_timer_cb, 590 | .arg = NULL, 591 | .dispatch_method = ESP_TIMER_TASK, 592 | .name = "msc_reset", 593 | .skip_unhandled_events = false, 594 | }; 595 | ESP_ERROR_CHECK(esp_timer_create(&timer_args, &reset_timer)); 596 | } 597 | 598 | void msc_init(void) 599 | { 600 | char volume_label[FAT_VOLUME_NAME_SIZE + 1] = CONFIG_BRIDGE_MSC_VOLUME_LABEL; // +1 because the config value is 0-terminated 601 | // fill the volume_label with spaces up to length FAT_VOLUME_NAME_SIZE 602 | memset(volume_label + strlen(CONFIG_BRIDGE_MSC_VOLUME_LABEL), ' ', 603 | FAT_VOLUME_NAME_SIZE - strlen(CONFIG_BRIDGE_MSC_VOLUME_LABEL)); 604 | memcpy(msc_disk_boot_sector.volume, volume_label, FAT_VOLUME_NAME_SIZE); 605 | memcpy(msc_disk_root_directory_sector0, volume_label, FAT_VOLUME_NAME_SIZE); 606 | 607 | ESP_LOG_BUFFER_HEXDUMP("boot", &msc_disk_boot_sector, sizeof(msc_boot_sector_t), ESP_LOG_DEBUG); 608 | ESP_LOG_BUFFER_HEXDUMP("fat", msc_disk_fat_table_sector0, sizeof(msc_disk_fat_table_sector0), ESP_LOG_DEBUG); 609 | ESP_LOG_BUFFER_HEXDUMP("root", msc_disk_root_directory_sector0, sizeof(msc_disk_root_directory_sector0), ESP_LOG_DEBUG); 610 | ESP_LOGI(TAG, "MSC disk RAM usage: %d bytes", sizeof(msc_boot_sector_t) + sizeof(msc_disk_fat_table_sector0) + 611 | sizeof(msc_disk_root_directory_sector0) + sizeof(msc_disk_readme_sector0)); 612 | 613 | init_reset_timer(); 614 | } 615 | -------------------------------------------------------------------------------- /main/msc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | # pragma once 8 | 9 | #include "freertos/FreeRTOS.h" 10 | #include "freertos/task.h" 11 | 12 | void msc_init(void); 13 | -------------------------------------------------------------------------------- /main/noflash.lf: -------------------------------------------------------------------------------- 1 | [mapping:main] 2 | archive: libmain.a 3 | entries: 4 | esp_usb_jtag (noflash) 5 | eub_vendord (noflash) 6 | eub_swd (noflash) 7 | -------------------------------------------------------------------------------- /main/public_include/tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | # pragma once 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #define CFG_TUSB_DEBUG 0 14 | 15 | #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED) 16 | #define CFG_TUSB_OS OPT_OS_FREERTOS 17 | 18 | #define CFG_TUD_ENDPOINT0_SIZE 64 19 | 20 | // DMA Mode has a priority over Slave/IRQ mode and will be used if hardware supports it 21 | // Slave/IRQ mode has issue with handling zero length packets 22 | #define CFG_TUD_DWC2_DMA_ENABLE 1 // Enable DMA 23 | 24 | #define CFG_TUD_CDC 1 25 | #define CFG_TUD_CDC_RX_BUFSIZE 64 26 | #define CFG_TUD_CDC_TX_BUFSIZE 64 27 | 28 | #define CFG_TUD_MSC 1 29 | #define CFG_TUD_MSC_BUFSIZE 512 30 | 31 | #define CFG_TUD_VENDOR 0 32 | #define CFG_TUD_HID 0 33 | #define CFG_TUD_MIDI 0 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /main/serial.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "tusb_config.h" 13 | #include "tusb.h" 14 | #include "esp_log.h" 15 | #include "esp_idf_version.h" 16 | #include "freertos/FreeRTOS.h" 17 | #include "freertos/task.h" 18 | #include "freertos/ringbuf.h" 19 | #include "driver/gpio.h" 20 | #include "driver/uart.h" 21 | #include "sdkconfig.h" 22 | #include "esp_loader.h" 23 | #include "esp32_port.h" 24 | #include "esp_timer.h" 25 | #include "esp_rom_sys.h" 26 | #include "util.h" 27 | #include "serial.h" 28 | #include "esp_io.h" 29 | #include "eub_debug_probe.h" 30 | 31 | #define SLAVE_UART_NUM UART_NUM_1 32 | #define SLAVE_UART_BUF_SIZE (2 * 1024) 33 | #define SLAVE_UART_DEFAULT_BAUD 115200 34 | 35 | #define USB_SEND_RINGBUFFER_SIZE SLAVE_UART_BUF_SIZE 36 | 37 | static const char *TAG = "bridge_serial"; 38 | 39 | static QueueHandle_t uart_queue; 40 | static RingbufHandle_t usb_sendbuf; 41 | static SemaphoreHandle_t usb_tx_requested = NULL; 42 | static SemaphoreHandle_t usb_tx_done = NULL; 43 | 44 | static esp_timer_handle_t state_change_timer; 45 | 46 | static atomic_bool serial_read_enabled = false; 47 | 48 | static void uart_event_task(void *pvParameters) 49 | { 50 | uart_event_t event; 51 | uint8_t dtmp[SLAVE_UART_BUF_SIZE]; 52 | 53 | while (1) { 54 | gpio_set_level(LED_TX, LED_TX_OFF); 55 | if (xQueueReceive(uart_queue, (void *) &event, portMAX_DELAY)) { 56 | gpio_set_level(LED_TX, LED_TX_ON); 57 | switch (event.type) { 58 | case UART_DATA: 59 | if (serial_read_enabled) { 60 | size_t buffered_len; 61 | uart_get_buffered_data_len(SLAVE_UART_NUM, &buffered_len); 62 | const int read = uart_read_bytes(SLAVE_UART_NUM, dtmp, MIN(buffered_len, SLAVE_UART_BUF_SIZE), portMAX_DELAY); 63 | ESP_LOGD(TAG, "UART -> CDC ringbuffer (%d bytes)", read); 64 | ESP_LOG_BUFFER_HEXDUMP("UART -> CDC", dtmp, read, ESP_LOG_DEBUG); 65 | 66 | // We cannot wait it here because UART events would overflow and have to copy the data into 67 | // another buffer and wait until it can be sent. 68 | if (xRingbufferSend(usb_sendbuf, dtmp, read, pdMS_TO_TICKS(10)) != pdTRUE) { 69 | ESP_LOGV(TAG, "Cannot write to ringbuffer (free %d of %d)!", 70 | xRingbufferGetCurFreeSize(usb_sendbuf), 71 | USB_SEND_RINGBUFFER_SIZE); 72 | vTaskDelay(pdMS_TO_TICKS(10)); 73 | } 74 | } 75 | break; 76 | case UART_FIFO_OVF: 77 | ESP_LOGW(TAG, "UART FIFO overflow"); 78 | uart_flush_input(SLAVE_UART_NUM); 79 | xQueueReset(uart_queue); 80 | break; 81 | case UART_BUFFER_FULL: 82 | ESP_LOGW(TAG, "UART ring buffer full"); 83 | uart_flush_input(SLAVE_UART_NUM); 84 | xQueueReset(uart_queue); 85 | break; 86 | case UART_BREAK: 87 | ESP_LOGW(TAG, "UART RX break"); 88 | break; 89 | case UART_PARITY_ERR: 90 | ESP_LOGW(TAG, "UART parity error"); 91 | break; 92 | case UART_FRAME_ERR: 93 | ESP_LOGW(TAG, "UART frame error"); 94 | break; 95 | default: 96 | ESP_LOGW(TAG, "UART event type: %d", event.type); 97 | break; 98 | } 99 | taskYIELD(); 100 | } 101 | vTaskDelay(pdMS_TO_TICKS(10)); 102 | } 103 | vTaskDelete(NULL); 104 | } 105 | 106 | static esp_err_t usb_wait_for_tx(const uint32_t block_time_ms) 107 | { 108 | if (xSemaphoreTake(usb_tx_done, pdMS_TO_TICKS(block_time_ms)) != pdTRUE) { 109 | return ESP_ERR_TIMEOUT; 110 | } 111 | return ESP_OK; 112 | } 113 | 114 | static void usb_sender_task(void *pvParameters) 115 | { 116 | while (1) { 117 | size_t ringbuf_received; 118 | uint8_t *buf = (uint8_t *) xRingbufferReceiveUpTo(usb_sendbuf, &ringbuf_received, pdMS_TO_TICKS(100), 119 | CFG_TUD_CDC_TX_BUFSIZE); 120 | 121 | if (buf) { 122 | uint8_t int_buf[CFG_TUD_CDC_TX_BUFSIZE]; 123 | memcpy(int_buf, buf, ringbuf_received); 124 | vRingbufferReturnItem(usb_sendbuf, (void *) buf); 125 | 126 | for (int transferred = 0, to_send = ringbuf_received; transferred < ringbuf_received;) { 127 | xSemaphoreGive(usb_tx_requested); 128 | const int wr_len = tud_cdc_write(int_buf + transferred, to_send); 129 | /* tinyusb might have been flushed the data. In case not flushed, we are flushing here. 130 | 2nd attempt might return zero, meaning there is no data to transfer. So it is safe to call it again. 131 | */ 132 | tud_cdc_write_flush(); 133 | if (usb_wait_for_tx(50) != ESP_OK) { 134 | xSemaphoreTake(usb_tx_requested, 0); 135 | tud_cdc_write_clear(); /* host might be disconnected. drop the buffer */ 136 | ESP_LOGV(TAG, "usb tx timeout"); 137 | break; 138 | } 139 | ESP_LOGD(TAG, "CDC ringbuffer -> CDC (%d bytes)", wr_len); 140 | transferred += wr_len; 141 | to_send -= wr_len; 142 | } 143 | } else { 144 | ESP_LOGD(TAG, "usb_sender_task: nothing to send"); 145 | vTaskDelay(pdMS_TO_TICKS(100)); 146 | continue; 147 | } 148 | } 149 | vTaskDelete(NULL); 150 | } 151 | 152 | void tud_cdc_tx_complete_cb(const uint8_t itf) 153 | { 154 | if (xSemaphoreTake(usb_tx_requested, 0) != pdTRUE) { 155 | /* Semaphore should have been given before write attempt. 156 | Sometimes tinyusb can send one more cb even xfer_complete len is zero 157 | */ 158 | return; 159 | } 160 | 161 | xSemaphoreGive(usb_tx_done); 162 | } 163 | 164 | void tud_cdc_rx_cb(const uint8_t itf) 165 | { 166 | uint8_t buf[CFG_TUD_CDC_RX_BUFSIZE]; 167 | 168 | const uint32_t rx_size = tud_cdc_n_read(itf, buf, CFG_TUD_CDC_RX_BUFSIZE); 169 | if (rx_size > 0) { 170 | gpio_set_level(LED_RX, LED_RX_ON); 171 | ESP_LOGD(TAG, "CDC -> UART (%" PRId32 " bytes)", rx_size); 172 | ESP_LOG_BUFFER_HEXDUMP("CDC -> UART", buf, rx_size, ESP_LOG_DEBUG); 173 | 174 | const int transferred = uart_write_bytes(SLAVE_UART_NUM, buf, rx_size); 175 | if (transferred != rx_size) { 176 | ESP_LOGW(TAG, "uart_write_bytes transferred %d bytes only!", transferred); 177 | } 178 | } else { 179 | ESP_LOGW(TAG, "tud_cdc_rx_cb receive error"); 180 | } 181 | gpio_set_level(LED_RX, LED_RX_OFF); 182 | } 183 | 184 | void tud_cdc_line_coding_cb(const uint8_t itf, cdc_line_coding_t const *p_line_coding) 185 | { 186 | if (serial_set_baudrate(p_line_coding->bit_rate)) { 187 | ESP_LOGI(TAG, "Baudrate set to %" PRId32 "", p_line_coding->bit_rate); 188 | } else { 189 | ESP_LOGE(TAG, "Could not set the baudrate to %" PRId32 "", p_line_coding->bit_rate); 190 | eub_abort(); 191 | } 192 | } 193 | 194 | void tud_cdc_line_state_cb(const uint8_t itf, const bool dtr, const bool rts) 195 | { 196 | // The following transformation of DTR & RTS signals to BOOT & RST is done based on auto reset circutry shown in 197 | // schematics of ESP boards. 198 | 199 | // defaults for ((dtr && rts) || (!dtr && !rts)) 200 | bool rst = true; 201 | bool boot = true; 202 | 203 | if (!dtr && rts) { 204 | rst = false; 205 | boot = true; 206 | } else if (dtr && !rts) { 207 | rst = true; 208 | boot = false; 209 | } 210 | 211 | esp_timer_stop(state_change_timer); // maybe it is not started so not check the exit value 212 | 213 | if (dtr & rts) { 214 | // The assignment of BOOT=1 and RST=1 is postponed and it is done only if no other state change occurs in time 215 | // period set by the timer. 216 | // This is a patch for Esptool. Esptool generates DTR=0 & RTS=1 followed by DTR=1 & RTS=0. However, a callback 217 | // with DTR = 1 & RTS = 1 is received between. This would prevent to put the target chip into download mode. 218 | ESP_ERROR_CHECK(esp_timer_start_once(state_change_timer, 10 * 1000 /*us*/)); 219 | 220 | // Enable serial read on the first connection. It can be kept enabled after disconnection because that is not 221 | // causing crash of the USB subsystem. 222 | serial_read_enabled = true; 223 | } else { 224 | ESP_LOGI(TAG, "DTR = %d, RTS = %d -> BOOT = %d, RST = %d", dtr, rts, boot, rst); 225 | 226 | gpio_set_level(GPIO_BOOT, boot); 227 | gpio_set_level(GPIO_RST, rst); 228 | 229 | if (!rst) { 230 | if (serial_set_baudrate(SLAVE_UART_DEFAULT_BAUD)) { 231 | ESP_LOGI(TAG, "Baudrate set to %d", SLAVE_UART_DEFAULT_BAUD); 232 | } else { 233 | ESP_LOGE(TAG, "Could not set the baudrate to %d", SLAVE_UART_DEFAULT_BAUD); 234 | eub_abort(); 235 | } 236 | } 237 | 238 | // On ESP32, TDI jtag signal is on GPIO12, which is also a strapping pin that determines flash voltage. 239 | // If TDI is high when ESP32 is released from external reset, the flash voltage is set to 1.8V, and the chip will fail to boot. 240 | // As a solution, MTDI signal forced to be low when RST is about to go high. 241 | static bool tdi_bootstrapping = false; 242 | if (eub_debug_probe_target_is_esp32() && !tdi_bootstrapping && boot && !rst) { 243 | eub_debug_probe_task_suspend(); 244 | tdi_bootstrapping = true; 245 | gpio_set_level(CONFIG_BRIDGE_GPIO_TDO, 0); 246 | ESP_LOGW(TAG, "jtag task suspended"); 247 | } 248 | if (tdi_bootstrapping && boot && rst) { 249 | esp_rom_delay_us(1000); /* wait for reset */ 250 | eub_debug_probe_task_resume(); 251 | tdi_bootstrapping = false; 252 | ESP_LOGW(TAG, "jtag task resumed"); 253 | } 254 | } 255 | } 256 | 257 | static void state_change_timer_cb(void *arg) 258 | { 259 | ESP_LOGI(TAG, "BOOT = 1, RST = 1"); 260 | gpio_set_level(GPIO_BOOT, true); 261 | gpio_set_level(GPIO_RST, true); 262 | } 263 | 264 | static void init_state_change_timer(void) 265 | { 266 | const esp_timer_create_args_t timer_args = { 267 | .callback = state_change_timer_cb, 268 | .name = "serial_state_change" 269 | }; 270 | ESP_ERROR_CHECK(esp_timer_create(&timer_args, &state_change_timer)); 271 | } 272 | 273 | void serial_init(void) 274 | { 275 | const loader_esp32_config_t serial_conf = { 276 | .baud_rate = SLAVE_UART_DEFAULT_BAUD, 277 | .uart_port = SLAVE_UART_NUM, 278 | .uart_rx_pin = GPIO_RXD, 279 | .uart_tx_pin = GPIO_TXD, 280 | .rx_buffer_size = SLAVE_UART_BUF_SIZE * 2, 281 | .tx_buffer_size = 0, 282 | .uart_queue = &uart_queue, 283 | .queue_size = 20, 284 | .reset_trigger_pin = GPIO_RST, 285 | .gpio0_trigger_pin = GPIO_BOOT, 286 | }; 287 | 288 | if (loader_port_esp32_init(&serial_conf) == ESP_LOADER_SUCCESS) { 289 | ESP_LOGI(TAG, "UART & GPIO have been initialized"); 290 | 291 | gpio_set_level(GPIO_RST, 1); 292 | gpio_set_level(GPIO_BOOT, 1); 293 | 294 | init_state_change_timer(); 295 | 296 | usb_sendbuf = xRingbufferCreate(USB_SEND_RINGBUFFER_SIZE, RINGBUF_TYPE_BYTEBUF); 297 | 298 | if (usb_sendbuf) { 299 | usb_tx_done = xSemaphoreCreateBinary(); 300 | usb_tx_requested = xSemaphoreCreateBinary(); 301 | xTaskCreate(usb_sender_task, "usb_sender_task", 4 * 1024, NULL, 5, NULL); 302 | xTaskCreate(uart_event_task, "uart_event_task", 8 * 1024, NULL, 5, NULL); 303 | } else { 304 | ESP_LOGE(TAG, "Cannot create ringbuffer for USB sender"); 305 | eub_abort(); 306 | } 307 | 308 | // Serial read is enabled only with the first connection. This is done in order to 309 | // avoid crashing the USB subsystem. 310 | serial_read_enabled = false; 311 | } else { 312 | ESP_LOGE(TAG, "loader_port_serial_init failed"); 313 | eub_abort(); 314 | } 315 | } 316 | 317 | void serial_set(const bool enable) 318 | { 319 | serial_read_enabled = enable; 320 | } 321 | 322 | bool serial_set_baudrate(const uint32_t baud) 323 | { 324 | static uint32_t current_baudrate = SLAVE_UART_DEFAULT_BAUD; 325 | 326 | if (current_baudrate == baud) { 327 | return true; 328 | } 329 | 330 | esp_err_t result = uart_set_baudrate(SLAVE_UART_NUM, baud); 331 | 332 | if (result == ESP_OK) { 333 | current_baudrate = baud; 334 | } 335 | 336 | return result == ESP_OK; 337 | } 338 | -------------------------------------------------------------------------------- /main/serial.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | 9 | void serial_init(void); 10 | void serial_set(const bool enable); 11 | bool serial_set_baudrate(const uint32_t baud); 12 | -------------------------------------------------------------------------------- /main/usb_defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | 9 | // USB Interface Numbers 10 | enum { 11 | ITF_NUM_CDC = 0, 12 | ITF_NUM_CDC_DATA, 13 | ITF_NUM_VENDOR, 14 | ITF_NUM_MSC, 15 | ITF_NUM_TOTAL 16 | }; 17 | 18 | // USB Endpoint Numbers 19 | #define EPNUM_CDC 2 20 | #define EPNUM_VENDOR 3 21 | #define EPNUM_MSC 4 22 | -------------------------------------------------------------------------------- /main/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include "freertos/FreeRTOS.h" 9 | #include "freertos/task.h" 10 | #include "driver/gpio.h" 11 | #include "util.h" 12 | #include "sdkconfig.h" 13 | #include "esp_io.h" 14 | 15 | void __attribute__((noreturn)) eub_abort(void) 16 | { 17 | const int led_patterns[][3] = { 18 | {LED_TX_ON, LED_RX_ON, LED_JTAG_ON}, 19 | {LED_TX_ON, LED_RX_OFF, LED_JTAG_ON}, 20 | {LED_TX_OFF, LED_RX_ON, LED_JTAG_OFF}, 21 | {LED_TX_ON, LED_RX_OFF, LED_JTAG_ON}, 22 | {LED_TX_OFF, LED_RX_ON, LED_JTAG_OFF}, 23 | {LED_TX_ON, LED_RX_OFF, LED_JTAG_ON}, 24 | {LED_TX_ON, LED_RX_ON, LED_JTAG_ON}, 25 | }; 26 | 27 | for (int i = 0; i < sizeof(led_patterns) / sizeof(led_patterns[0]); ++i) { 28 | gpio_set_level(LED_TX, led_patterns[i][0]); 29 | gpio_set_level(LED_RX, led_patterns[i][1]); 30 | gpio_set_level(LED_JTAG, led_patterns[i][2]); 31 | vTaskDelay(pdMS_TO_TICKS(500)); 32 | } 33 | 34 | abort(); 35 | } 36 | -------------------------------------------------------------------------------- /main/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | # pragma once 8 | 9 | #include 10 | 11 | #ifndef ARRAY_SIZE 12 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) 13 | #endif 14 | 15 | #define GET_BYTE(n, b) (((n) >> ((b) * 8)) & 0xFF) 16 | 17 | #define EUB_ASSERT(condition) do { \ 18 | if (!(condition)) { \ 19 | eub_abort(); \ 20 | } \ 21 | } while(0) 22 | 23 | void __attribute__((noreturn)) eub_abort(void); 24 | -------------------------------------------------------------------------------- /sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=n 2 | CONFIG_SERIAL_FLASHER_MD5_ENABLED=y 3 | -------------------------------------------------------------------------------- /sdkconfig.defaults.esp32s2: -------------------------------------------------------------------------------- 1 | CONFIG_IDF_TARGET="esp32s2" 2 | CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y 3 | CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240 4 | CONFIG_ESP32S2_INSTRUCTION_CACHE_16KB=y 5 | CONFIG_ESP32S2_DATA_CACHE_16KB=y 6 | -------------------------------------------------------------------------------- /sdkconfig.defaults.esp32s3: -------------------------------------------------------------------------------- 1 | CONFIG_IDF_TARGET="esp32s3" 2 | CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y 3 | CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240 4 | CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y 5 | CONFIG_ESP32S3_DATA_CACHE_64KB=y 6 | -------------------------------------------------------------------------------- /sdkconfig.defaults.esp_prog2: -------------------------------------------------------------------------------- 1 | # This file is used to pass ESP-Prog-2 specific options when building firmware for the probe 2 | # To pass it to the build system, set the SDKCONFIG_DEFAULTS environment environment variable like so: 3 | # export SDKCONFIG_DEFAULTS="sdkconfig.defaults.esp_prog2;sdkconfig.defaults.esp32s3" 4 | 5 | CONFIG_BRIDGE_PRODUCT_NAME="ESP-Prog-2" 6 | CONFIG_BRIDGE_GPIO_BOOT=33 7 | CONFIG_BRIDGE_GPIO_RST=21 8 | CONFIG_BRIDGE_GPIO_RXD=18 9 | CONFIG_BRIDGE_GPIO_TXD=17 10 | CONFIG_BRIDGE_GPIO_TDI=7 11 | CONFIG_BRIDGE_GPIO_TDO=6 12 | CONFIG_BRIDGE_GPIO_TCK=5 13 | CONFIG_BRIDGE_GPIO_TMS=4 14 | CONFIG_BRIDGE_GPIO_LED1=48 15 | CONFIG_BRIDGE_GPIO_LED2=34 16 | CONFIG_BRIDGE_GPIO_LED3=34 17 | CONFIG_BRIDGE_GPIO_LED1_ACTIVE=0 18 | CONFIG_BRIDGE_GPIO_LED2_ACTIVE=0 19 | CONFIG_BRIDGE_GPIO_LED3_ACTIVE=0 20 | -------------------------------------------------------------------------------- /sdkconfig.jtag.defaults: -------------------------------------------------------------------------------- 1 | CONFIG_BRIDGE_DEBUG_IFACE_JTAG=y 2 | CONFIG_BRIDGE_DEBUG_IFACE_NAME="JTAG" 3 | -------------------------------------------------------------------------------- /sdkconfig.swd.defaults: -------------------------------------------------------------------------------- 1 | CONFIG_BRIDGE_DEBUG_IFACE_SWD=y 2 | CONFIG_BRIDGE_DEBUG_IFACE_NAME="CMSIS-DAP" 3 | --------------------------------------------------------------------------------