├── .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 | 
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 |
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 |
--------------------------------------------------------------------------------